linux/arch/arm/plat-samsung/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 <plat/regs-adc.h>
  23#include <plat/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        struct resource *regs;
 337        enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
 338        int ret;
 339        unsigned tmp;
 340
 341        adc = devm_kzalloc(dev, sizeof(*adc), GFP_KERNEL);
 342        if (!adc)
 343                return -ENOMEM;
 344
 345        spin_lock_init(&adc->lock);
 346
 347        adc->pdev = pdev;
 348        adc->prescale = S3C2410_ADCCON_PRSCVL(49);
 349
 350        adc->vdd = devm_regulator_get(dev, "vdd");
 351        if (IS_ERR(adc->vdd)) {
 352                dev_err(dev, "operating without regulator \"vdd\" .\n");
 353                return PTR_ERR(adc->vdd);
 354        }
 355
 356        adc->irq = platform_get_irq(pdev, 1);
 357        if (adc->irq <= 0) {
 358                dev_err(dev, "failed to get adc irq\n");
 359                return -ENOENT;
 360        }
 361
 362        ret = devm_request_irq(dev, adc->irq, s3c_adc_irq, 0, dev_name(dev),
 363                                adc);
 364        if (ret < 0) {
 365                dev_err(dev, "failed to attach adc irq\n");
 366                return ret;
 367        }
 368
 369        adc->clk = devm_clk_get(dev, "adc");
 370        if (IS_ERR(adc->clk)) {
 371                dev_err(dev, "failed to get adc clock\n");
 372                return PTR_ERR(adc->clk);
 373        }
 374
 375        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 376        adc->regs = devm_ioremap_resource(dev, regs);
 377        if (IS_ERR(adc->regs))
 378                return PTR_ERR(adc->regs);
 379
 380        ret = regulator_enable(adc->vdd);
 381        if (ret)
 382                return ret;
 383
 384        clk_prepare_enable(adc->clk);
 385
 386        tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
 387
 388        /* Enable 12-bit ADC resolution */
 389        if (cpu == TYPE_ADCV12)
 390                tmp |= S3C2416_ADCCON_RESSEL;
 391        if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
 392                tmp |= S3C64XX_ADCCON_RESSEL;
 393
 394        writel(tmp, adc->regs + S3C2410_ADCCON);
 395
 396        dev_info(dev, "attached adc driver\n");
 397
 398        platform_set_drvdata(pdev, adc);
 399        adc_dev = adc;
 400
 401        return 0;
 402}
 403
 404static int s3c_adc_remove(struct platform_device *pdev)
 405{
 406        struct adc_device *adc = platform_get_drvdata(pdev);
 407
 408        clk_disable_unprepare(adc->clk);
 409        regulator_disable(adc->vdd);
 410
 411        return 0;
 412}
 413
 414#ifdef CONFIG_PM
 415static int s3c_adc_suspend(struct device *dev)
 416{
 417        struct adc_device *adc = dev_get_drvdata(dev);
 418        unsigned long flags;
 419        u32 con;
 420
 421        spin_lock_irqsave(&adc->lock, flags);
 422
 423        con = readl(adc->regs + S3C2410_ADCCON);
 424        con |= S3C2410_ADCCON_STDBM;
 425        writel(con, adc->regs + S3C2410_ADCCON);
 426
 427        disable_irq(adc->irq);
 428        spin_unlock_irqrestore(&adc->lock, flags);
 429        clk_disable(adc->clk);
 430        regulator_disable(adc->vdd);
 431
 432        return 0;
 433}
 434
 435static int s3c_adc_resume(struct device *dev)
 436{
 437        struct platform_device *pdev = to_platform_device(dev);
 438        struct adc_device *adc = platform_get_drvdata(pdev);
 439        enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;
 440        int ret;
 441        unsigned long tmp;
 442
 443        ret = regulator_enable(adc->vdd);
 444        if (ret)
 445                return ret;
 446        clk_enable(adc->clk);
 447        enable_irq(adc->irq);
 448
 449        tmp = adc->prescale | S3C2410_ADCCON_PRSCEN;
 450
 451        /* Enable 12-bit ADC resolution */
 452        if (cpu == TYPE_ADCV12)
 453                tmp |= S3C2416_ADCCON_RESSEL;
 454        if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)
 455                tmp |= S3C64XX_ADCCON_RESSEL;
 456
 457        writel(tmp, adc->regs + S3C2410_ADCCON);
 458
 459        return 0;
 460}
 461
 462#else
 463#define s3c_adc_suspend NULL
 464#define s3c_adc_resume NULL
 465#endif
 466
 467static const struct platform_device_id s3c_adc_driver_ids[] = {
 468        {
 469                .name           = "s3c24xx-adc",
 470                .driver_data    = TYPE_ADCV1,
 471        }, {
 472                .name           = "s3c2443-adc",
 473                .driver_data    = TYPE_ADCV11,
 474        }, {
 475                .name           = "s3c2416-adc",
 476                .driver_data    = TYPE_ADCV12,
 477        }, {
 478                .name           = "s3c64xx-adc",
 479                .driver_data    = TYPE_ADCV2,
 480        }, {
 481                .name           = "samsung-adc-v3",
 482                .driver_data    = TYPE_ADCV3,
 483        },
 484        { }
 485};
 486MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids);
 487
 488static const struct dev_pm_ops adc_pm_ops = {
 489        .suspend        = s3c_adc_suspend,
 490        .resume         = s3c_adc_resume,
 491};
 492
 493static struct platform_driver s3c_adc_driver = {
 494        .id_table       = s3c_adc_driver_ids,
 495        .driver         = {
 496                .name   = "s3c-adc",
 497                .pm     = &adc_pm_ops,
 498        },
 499        .probe          = s3c_adc_probe,
 500        .remove         = s3c_adc_remove,
 501};
 502
 503static int __init adc_init(void)
 504{
 505        int ret;
 506
 507        ret = platform_driver_register(&s3c_adc_driver);
 508        if (ret)
 509                printk(KERN_ERR "%s: failed to add adc driver\n", __func__);
 510
 511        return ret;
 512}
 513
 514module_init(adc_init);
 515