linux/arch/arm/mach-s3c/adc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-1.0+
   2//
   3// Copyright (c) 2008 Simtec Electronics
   4//      http://armlinux.simtec.co.uk/
   5//      Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
   6//
   7// Samsung ADC device core
   8
   9#include <linux/module.h>
  10#include <linux/kernel.h>
  11#include <linux/mod_devicetable.h>
  12#include <linux/platform_device.h>
  13#include <linux/sched.h>
  14#include <linux/list.h>
  15#include <linux/slab.h>
  16#include <linux/err.h>
  17#include <linux/clk.h>
  18#include <linux/interrupt.h>
  19#include <linux/io.h>
  20#include <linux/regulator/consumer.h>
  21
  22#include "regs-adc.h"
  23#include <linux/soc/samsung/s3c-adc.h>
  24
  25/* This driver is designed to control the usage of the ADC block between
  26 * the touchscreen and any other drivers that may need to use it, such as
  27 * the hwmon driver.
  28 *
  29 * Priority will be given to the touchscreen driver, but as this itself is
  30 * rate limited it should not starve other requests which are processed in
  31 * order that they are received.
  32 *
  33 * Each user registers to get a client block which uniquely identifies it
  34 * and stores information such as the necessary functions to callback when
  35 * action is required.
  36 */
  37
  38enum s3c_cpu_type {
  39        TYPE_ADCV1, /* S3C24XX */
  40        TYPE_ADCV11, /* S3C2443 */
  41        TYPE_ADCV12, /* S3C2416, S3C2450 */
  42        TYPE_ADCV2, /* S3C64XX */
  43        TYPE_ADCV3, /* S5PV210, S5PC110, Exynos4210 */
  44};
  45
  46struct s3c_adc_client {
  47        struct platform_device  *pdev;
  48        struct list_head         pend;
  49        wait_queue_head_t       *wait;
  50
  51        unsigned int             nr_samples;
  52        int                      result;
  53        unsigned char            is_ts;
  54        unsigned char            channel;
  55
  56        void    (*select_cb)(struct s3c_adc_client *c, unsigned selected);
  57        void    (*convert_cb)(struct s3c_adc_client *c,
  58                              unsigned val1, unsigned val2,
  59                              unsigned *samples_left);
  60};
  61
  62struct adc_device {
  63        struct platform_device  *pdev;
  64        struct platform_device  *owner;
  65        struct clk              *clk;
  66        struct s3c_adc_client   *cur;
  67        struct s3c_adc_client   *ts_pend;
  68        void __iomem            *regs;
  69        spinlock_t               lock;
  70
  71        unsigned int             prescale;
  72
  73        int                      irq;
  74        struct regulator        *vdd;
  75};
  76
  77static struct adc_device *adc_dev;
  78
  79static LIST_HEAD(adc_pending);  /* protected by adc_device.lock */
  80
  81#define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg)
  82
  83static inline void s3c_adc_convert(struct adc_device *adc)
  84{
  85        unsigned con = readl(adc->regs + S3C2410_ADCCON);
  86
  87        con |= S3C2410_ADCCON_ENABLE_START;
  88        writel(con, adc->regs + S3C2410_ADCCON);
  89}
  90
  91static inline void s3c_adc_select(struct adc_device *adc,
  92                                  struct s3c_adc_client *client)
  93{
  94        unsigned con = readl(adc->regs + S3C2410_ADCCON);
  95        enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
  96
  97        client->select_cb(client, 1);
  98
  99        if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV2)
 100                con &= ~S3C2410_ADCCON_MUXMASK;
 101        con &= ~S3C2410_ADCCON_STDBM;
 102        con &= ~S3C2410_ADCCON_STARTMASK;
 103
 104        if (!client->is_ts) {
 105                if (cpu == TYPE_ADCV3)
 106                        writel(client->channel & 0xf, adc->regs + S5P_ADCMUX);
 107                else if (cpu == TYPE_ADCV11 || cpu == TYPE_ADCV12)
 108                        writel(client->channel & 0xf,
 109                                                adc->regs + S3C2443_ADCMUX);
 110                else
 111                        con |= S3C2410_ADCCON_SELMUX(client->channel);
 112        }
 113
 114        writel(con, adc->regs + S3C2410_ADCCON);
 115}
 116
 117static void s3c_adc_dbgshow(struct adc_device *adc)
 118{
 119        adc_dbg(adc, "CON=%08x, TSC=%08x, DLY=%08x\n",
 120                readl(adc->regs + S3C2410_ADCCON),
 121                readl(adc->regs + S3C2410_ADCTSC),
 122                readl(adc->regs + S3C2410_ADCDLY));
 123}
 124
 125static void s3c_adc_try(struct adc_device *adc)
 126{
 127        struct s3c_adc_client *next = adc->ts_pend;
 128
 129        if (!next && !list_empty(&adc_pending)) {
 130                next = list_first_entry(&adc_pending,
 131                                        struct s3c_adc_client, pend);
 132                list_del(&next->pend);
 133        } else
 134                adc->ts_pend = NULL;
 135
 136        if (next) {
 137                adc_dbg(adc, "new client is %p\n", next);
 138                adc->cur = next;
 139                s3c_adc_select(adc, next);
 140                s3c_adc_convert(adc);
 141                s3c_adc_dbgshow(adc);
 142        }
 143}
 144
 145int s3c_adc_start(struct s3c_adc_client *client,
 146                  unsigned int channel, unsigned int nr_samples)
 147{
 148        struct adc_device *adc = adc_dev;
 149        unsigned long flags;
 150
 151        if (!adc) {
 152                printk(KERN_ERR "%s: failed to find adc\n", __func__);
 153                return -EINVAL;
 154        }
 155
 156        spin_lock_irqsave(&adc->lock, flags);
 157
 158        if (client->is_ts && adc->ts_pend) {
 159                spin_unlock_irqrestore(&adc->lock, flags);
 160                return -EAGAIN;
 161        }
 162
 163        client->channel = channel;
 164        client->nr_samples = nr_samples;
 165
 166        if (client->is_ts)
 167                adc->ts_pend = client;
 168        else
 169                list_add_tail(&client->pend, &adc_pending);
 170
 171        if (!adc->cur)
 172                s3c_adc_try(adc);
 173
 174        spin_unlock_irqrestore(&adc->lock, flags);
 175
 176        return 0;
 177}
 178EXPORT_SYMBOL_GPL(s3c_adc_start);
 179
 180static void s3c_convert_done(struct s3c_adc_client *client,
 181                             unsigned v, unsigned u, unsigned *left)
 182{
 183        client->result = v;
 184        wake_up(client->wait);
 185}
 186
 187int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch)
 188{
 189        DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
 190        int ret;
 191
 192        client->convert_cb = s3c_convert_done;
 193        client->wait = &wake;
 194        client->result = -1;
 195
 196        ret = s3c_adc_start(client, ch, 1);
 197        if (ret < 0)
 198                goto err;
 199
 200        ret = wait_event_timeout(wake, client->result >= 0, HZ / 2);
 201        if (client->result < 0) {
 202                ret = -ETIMEDOUT;
 203                goto err;
 204        }
 205
 206        client->convert_cb = NULL;
 207        return client->result;
 208
 209err:
 210        return ret;
 211}
 212EXPORT_SYMBOL_GPL(s3c_adc_read);
 213
 214static void s3c_adc_default_select(struct s3c_adc_client *client,
 215                                   unsigned select)
 216{
 217}
 218
 219struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
 220                                        void (*select)(struct s3c_adc_client *client,
 221                                                       unsigned int selected),
 222                                        void (*conv)(struct s3c_adc_client *client,
 223                                                     unsigned d0, unsigned d1,
 224                                                     unsigned *samples_left),
 225                                        unsigned int is_ts)
 226{
 227        struct s3c_adc_client *client;
 228
 229        WARN_ON(!pdev);
 230
 231        if (!select)
 232                select = s3c_adc_default_select;
 233
 234        if (!pdev)
 235                return ERR_PTR(-EINVAL);
 236
 237        client = kzalloc(sizeof(*client), GFP_KERNEL);
 238        if (!client)
 239                return ERR_PTR(-ENOMEM);
 240
 241        client->pdev = pdev;
 242        client->is_ts = is_ts;
 243        client->select_cb = select;
 244        client->convert_cb = conv;
 245
 246        return client;
 247}
 248EXPORT_SYMBOL_GPL(s3c_adc_register);
 249
 250void s3c_adc_release(struct s3c_adc_client *client)
 251{
 252        unsigned long flags;
 253
 254        spin_lock_irqsave(&adc_dev->lock, flags);
 255
 256        /* We should really check that nothing is in progress. */
 257        if (adc_dev->cur == client)
 258                adc_dev->cur = NULL;
 259        if (adc_dev->ts_pend == client)
 260                adc_dev->ts_pend = NULL;
 261        else {
 262                struct list_head *p, *n;
 263                struct s3c_adc_client *tmp;
 264
 265                list_for_each_safe(p, n, &adc_pending) {
 266                        tmp = list_entry(p, struct s3c_adc_client, pend);
 267                        if (tmp == client)
 268                                list_del(&tmp->pend);
 269                }
 270        }
 271
 272        if (adc_dev->cur == NULL)
 273                s3c_adc_try(adc_dev);
 274
 275        spin_unlock_irqrestore(&adc_dev->lock, flags);
 276        kfree(client);
 277}
 278EXPORT_SYMBOL_GPL(s3c_adc_release);
 279
 280static irqreturn_t s3c_adc_irq(int irq, void *pw)
 281{
 282        struct adc_device *adc = pw;
 283        struct s3c_adc_client *client = adc->cur;
 284        enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data;
 285        unsigned data0, data1;
 286
 287        if (!client) {
 288                dev_warn(&adc->pdev->dev, "%s: no adc pending\n", __func__);
 289                goto exit;
 290        }
 291
 292        data0 = readl(adc->regs + S3C2410_ADCDAT0);
 293        data1 = readl(adc->regs + S3C2410_ADCDAT1);
 294        adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);
 295
 296        client->nr_samples--;
 297
 298        if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV11) {
 299                data0 &= 0x3ff;
 300                data1 &= 0x3ff;
 301        } else {
 302                /* S3C2416/S3C64XX/S5P ADC resolution is 12-bit */
 303                data0 &= 0xfff;
 304                data1 &= 0xfff;
 305        }
 306
 307        if (client->convert_cb)
 308                (client->convert_cb)(client, data0, data1, &client->nr_samples);
 309
 310        if (client->nr_samples > 0) {
 311                /* fire another conversion for this */
 312
 313                client->select_cb(client, 1);
 314                s3c_adc_convert(adc);
 315        } else {
 316                spin_lock(&adc->lock);
 317                (client->select_cb)(client, 0);
 318                adc->cur = NULL;
 319
 320                s3c_adc_try(adc);
 321                spin_unlock(&adc->lock);
 322        }
 323
 324exit:
 325        if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3) {
 326                /* Clear ADC interrupt */
 327                writel(0, adc->regs + S3C64XX_ADCCLRINT);
 328        }
 329        return IRQ_HANDLED;
 330}
 331
 332static int s3c_adc_probe(struct platform_device *pdev)
 333{
 334        struct device *dev = &pdev->dev;
 335        struct adc_device *adc;
 336        enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
 337        int ret;
 338        unsigned tmp;
 339
 340        adc = devm_kzalloc(dev, sizeof(*adc), GFP_KERNEL);
 341        if (!adc)
 342                return -ENOMEM;
 343
 344        spin_lock_init(&adc->lock);
 345
 346        adc->pdev = pdev;
 347        adc->prescale = S3C2410_ADCCON_PRSCVL(49);
 348
 349        adc->vdd = devm_regulator_get(dev, "vdd");
 350        if (IS_ERR(adc->vdd)) {
 351                dev_err(dev, "operating without regulator \"vdd\" .\n");
 352                return PTR_ERR(adc->vdd);
 353        }
 354
 355        adc->irq = platform_get_irq(pdev, 1);
 356        if (adc->irq <= 0)
 357                return -ENOENT;
 358
 359        ret = devm_request_irq(dev, adc->irq, s3c_adc_irq, 0, dev_name(dev),
 360                                adc);
 361        if (ret < 0) {
 362                dev_err(dev, "failed to attach adc irq\n");
 363                return ret;
 364        }
 365
 366        adc->clk = devm_clk_get(dev, "adc");
 367        if (IS_ERR(adc->clk)) {
 368                dev_err(dev, "failed to get adc clock\n");
 369                return PTR_ERR(adc->clk);
 370        }
 371
 372        adc->regs = devm_platform_ioremap_resource(pdev, 0);
 373        if (IS_ERR(adc->regs))
 374                return PTR_ERR(adc->regs);
 375
 376        ret = regulator_enable(adc->vdd);
 377        if (ret)
 378                return ret;
 379
 380        clk_prepare_enable(adc->clk);
 381
 382        tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
 383
 384        /* Enable 12-bit ADC resolution */
 385        if (cpu == TYPE_ADCV12)
 386                tmp |= S3C2416_ADCCON_RESSEL;
 387        if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
 388                tmp |= S3C64XX_ADCCON_RESSEL;
 389
 390        writel(tmp, adc->regs + S3C2410_ADCCON);
 391
 392        dev_info(dev, "attached adc driver\n");
 393
 394        platform_set_drvdata(pdev, adc);
 395        adc_dev = adc;
 396
 397        return 0;
 398}
 399
 400static int s3c_adc_remove(struct platform_device *pdev)
 401{
 402        struct adc_device *adc = platform_get_drvdata(pdev);
 403
 404        clk_disable_unprepare(adc->clk);
 405        regulator_disable(adc->vdd);
 406
 407        return 0;
 408}
 409
 410#ifdef CONFIG_PM
 411static int s3c_adc_suspend(struct device *dev)
 412{
 413        struct adc_device *adc = dev_get_drvdata(dev);
 414        unsigned long flags;
 415        u32 con;
 416
 417        spin_lock_irqsave(&adc->lock, flags);
 418
 419        con = readl(adc->regs + S3C2410_ADCCON);
 420        con |= S3C2410_ADCCON_STDBM;
 421        writel(con, adc->regs + S3C2410_ADCCON);
 422
 423        disable_irq(adc->irq);
 424        spin_unlock_irqrestore(&adc->lock, flags);
 425        clk_disable(adc->clk);
 426        regulator_disable(adc->vdd);
 427
 428        return 0;
 429}
 430
 431static int s3c_adc_resume(struct device *dev)
 432{
 433        struct platform_device *pdev = to_platform_device(dev);
 434        struct adc_device *adc = platform_get_drvdata(pdev);
 435        enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
 436        int ret;
 437        unsigned long tmp;
 438
 439        ret = regulator_enable(adc->vdd);
 440        if (ret)
 441                return ret;
 442        clk_enable(adc->clk);
 443        enable_irq(adc->irq);
 444
 445        tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
 446
 447        /* Enable 12-bit ADC resolution */
 448        if (cpu == TYPE_ADCV12)
 449                tmp |= S3C2416_ADCCON_RESSEL;
 450        if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
 451                tmp |= S3C64XX_ADCCON_RESSEL;
 452
 453        writel(tmp, adc->regs + S3C2410_ADCCON);
 454
 455        return 0;
 456}
 457
 458#else
 459#define s3c_adc_suspend NULL
 460#define s3c_adc_resume NULL
 461#endif
 462
 463static const struct platform_device_id s3c_adc_driver_ids[] = {
 464        {
 465                .name           = "s3c24xx-adc",
 466                .driver_data    = TYPE_ADCV1,
 467        }, {
 468                .name           = "s3c2443-adc",
 469                .driver_data    = TYPE_ADCV11,
 470        }, {
 471                .name           = "s3c2416-adc",
 472                .driver_data    = TYPE_ADCV12,
 473        }, {
 474                .name           = "s3c64xx-adc",
 475                .driver_data    = TYPE_ADCV2,
 476        }, {
 477                .name           = "samsung-adc-v3",
 478                .driver_data    = TYPE_ADCV3,
 479        },
 480        { }
 481};
 482MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);
 483
 484static const struct dev_pm_ops adc_pm_ops = {
 485        .suspend        = s3c_adc_suspend,
 486        .resume         = s3c_adc_resume,
 487};
 488
 489static struct platform_driver s3c_adc_driver = {
 490        .id_table       = s3c_adc_driver_ids,
 491        .driver         = {
 492                .name   = "s3c-adc",
 493                .pm     = &adc_pm_ops,
 494        },
 495        .probe          = s3c_adc_probe,
 496        .remove         = s3c_adc_remove,
 497};
 498
 499static int __init adc_init(void)
 500{
 501        int ret;
 502
 503        ret = platform_driver_register(&s3c_adc_driver);
 504        if (ret)
 505                printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
 506
 507        return ret;
 508}
 509
 510module_init(adc_init);
 511