linux/drivers/iio/adc/npcm_adc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2019 Nuvoton Technology corporation.
   3
   4#include <linux/clk.h>
   5#include <linux/device.h>
   6#include <linux/mfd/syscon.h>
   7#include <linux/io.h>
   8#include <linux/iio/iio.h>
   9#include <linux/interrupt.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/platform_device.h>
  13#include <linux/regmap.h>
  14#include <linux/regulator/consumer.h>
  15#include <linux/spinlock.h>
  16#include <linux/uaccess.h>
  17#include <linux/reset.h>
  18
  19struct npcm_adc {
  20        bool int_status;
  21        u32 adc_sample_hz;
  22        struct device *dev;
  23        void __iomem *regs;
  24        struct clk *adc_clk;
  25        wait_queue_head_t wq;
  26        struct regulator *vref;
  27        struct reset_control *reset;
  28        /*
  29         * Lock to protect the device state during a potential concurrent
  30         * read access from userspace. Reading a raw value requires a sequence
  31         * of register writes, then a wait for a event and finally a register
  32         * read, during which userspace could issue another read request.
  33         * This lock protects a read access from ocurring before another one
  34         * has finished.
  35         */
  36        struct mutex lock;
  37};
  38
  39/* ADC registers */
  40#define NPCM_ADCCON      0x00
  41#define NPCM_ADCDATA     0x04
  42
  43/* ADCCON Register Bits */
  44#define NPCM_ADCCON_ADC_INT_EN          BIT(21)
  45#define NPCM_ADCCON_REFSEL              BIT(19)
  46#define NPCM_ADCCON_ADC_INT_ST          BIT(18)
  47#define NPCM_ADCCON_ADC_EN              BIT(17)
  48#define NPCM_ADCCON_ADC_RST             BIT(16)
  49#define NPCM_ADCCON_ADC_CONV            BIT(13)
  50
  51#define NPCM_ADCCON_CH_MASK             GENMASK(27, 24)
  52#define NPCM_ADCCON_CH(x)               ((x) << 24)
  53#define NPCM_ADCCON_DIV_SHIFT           1
  54#define NPCM_ADCCON_DIV_MASK            GENMASK(8, 1)
  55#define NPCM_ADC_DATA_MASK(x)           ((x) & GENMASK(9, 0))
  56
  57#define NPCM_ADC_ENABLE         (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
  58
  59/* ADC General Definition */
  60#define NPCM_RESOLUTION_BITS            10
  61#define NPCM_INT_VREF_MV                2000
  62
  63#define NPCM_ADC_CHAN(ch) {                                     \
  64        .type = IIO_VOLTAGE,                                    \
  65        .indexed = 1,                                           \
  66        .channel = ch,                                          \
  67        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
  68        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  \
  69                                BIT(IIO_CHAN_INFO_SAMP_FREQ),   \
  70}
  71
  72static const struct iio_chan_spec npcm_adc_iio_channels[] = {
  73        NPCM_ADC_CHAN(0),
  74        NPCM_ADC_CHAN(1),
  75        NPCM_ADC_CHAN(2),
  76        NPCM_ADC_CHAN(3),
  77        NPCM_ADC_CHAN(4),
  78        NPCM_ADC_CHAN(5),
  79        NPCM_ADC_CHAN(6),
  80        NPCM_ADC_CHAN(7),
  81};
  82
  83static irqreturn_t npcm_adc_isr(int irq, void *data)
  84{
  85        u32 regtemp;
  86        struct iio_dev *indio_dev = data;
  87        struct npcm_adc *info = iio_priv(indio_dev);
  88
  89        regtemp = ioread32(info->regs + NPCM_ADCCON);
  90        if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
  91                iowrite32(regtemp, info->regs + NPCM_ADCCON);
  92                wake_up_interruptible(&info->wq);
  93                info->int_status = true;
  94        }
  95
  96        return IRQ_HANDLED;
  97}
  98
  99static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
 100{
 101        int ret;
 102        u32 regtemp;
 103
 104        /* Select ADC channel */
 105        regtemp = ioread32(info->regs + NPCM_ADCCON);
 106        regtemp &= ~NPCM_ADCCON_CH_MASK;
 107        info->int_status = false;
 108        iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
 109                  NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
 110
 111        ret = wait_event_interruptible_timeout(info->wq, info->int_status,
 112                                               msecs_to_jiffies(10));
 113        if (ret == 0) {
 114                regtemp = ioread32(info->regs + NPCM_ADCCON);
 115                if (regtemp & NPCM_ADCCON_ADC_CONV) {
 116                        /* if conversion failed - reset ADC module */
 117                        reset_control_assert(info->reset);
 118                        msleep(100);
 119                        reset_control_deassert(info->reset);
 120                        msleep(100);
 121
 122                        /* Enable ADC and start conversion module */
 123                        iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
 124                                  info->regs + NPCM_ADCCON);
 125                        dev_err(info->dev, "RESET ADC Complete\n");
 126                }
 127                return -ETIMEDOUT;
 128        }
 129        if (ret < 0)
 130                return ret;
 131
 132        *val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA));
 133
 134        return 0;
 135}
 136
 137static int npcm_adc_read_raw(struct iio_dev *indio_dev,
 138                             struct iio_chan_spec const *chan, int *val,
 139                             int *val2, long mask)
 140{
 141        int ret;
 142        int vref_uv;
 143        struct npcm_adc *info = iio_priv(indio_dev);
 144
 145        switch (mask) {
 146        case IIO_CHAN_INFO_RAW:
 147                mutex_lock(&info->lock);
 148                ret = npcm_adc_read(info, val, chan->channel);
 149                mutex_unlock(&info->lock);
 150                if (ret) {
 151                        dev_err(info->dev, "NPCM ADC read failed\n");
 152                        return ret;
 153                }
 154                return IIO_VAL_INT;
 155        case IIO_CHAN_INFO_SCALE:
 156                if (!IS_ERR(info->vref)) {
 157                        vref_uv = regulator_get_voltage(info->vref);
 158                        *val = vref_uv / 1000;
 159                } else {
 160                        *val = NPCM_INT_VREF_MV;
 161                }
 162                *val2 = NPCM_RESOLUTION_BITS;
 163                return IIO_VAL_FRACTIONAL_LOG2;
 164        case IIO_CHAN_INFO_SAMP_FREQ:
 165                *val = info->adc_sample_hz;
 166                return IIO_VAL_INT;
 167        default:
 168                return -EINVAL;
 169        }
 170
 171        return 0;
 172}
 173
 174static const struct iio_info npcm_adc_iio_info = {
 175        .read_raw = &npcm_adc_read_raw,
 176};
 177
 178static const struct of_device_id npcm_adc_match[] = {
 179        { .compatible = "nuvoton,npcm750-adc", },
 180        { /* sentinel */ }
 181};
 182MODULE_DEVICE_TABLE(of, npcm_adc_match);
 183
 184static int npcm_adc_probe(struct platform_device *pdev)
 185{
 186        int ret;
 187        int irq;
 188        u32 div;
 189        u32 reg_con;
 190        struct npcm_adc *info;
 191        struct iio_dev *indio_dev;
 192        struct device *dev = &pdev->dev;
 193
 194        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
 195        if (!indio_dev)
 196                return -ENOMEM;
 197        info = iio_priv(indio_dev);
 198
 199        mutex_init(&info->lock);
 200
 201        info->dev = &pdev->dev;
 202
 203        info->regs = devm_platform_ioremap_resource(pdev, 0);
 204        if (IS_ERR(info->regs))
 205                return PTR_ERR(info->regs);
 206
 207        info->reset = devm_reset_control_get(&pdev->dev, NULL);
 208        if (IS_ERR(info->reset))
 209                return PTR_ERR(info->reset);
 210
 211        info->adc_clk = devm_clk_get(&pdev->dev, NULL);
 212        if (IS_ERR(info->adc_clk)) {
 213                dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
 214                return PTR_ERR(info->adc_clk);
 215        }
 216
 217        /* calculate ADC clock sample rate */
 218        reg_con = ioread32(info->regs + NPCM_ADCCON);
 219        div = reg_con & NPCM_ADCCON_DIV_MASK;
 220        div = div >> NPCM_ADCCON_DIV_SHIFT;
 221        info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
 222
 223        irq = platform_get_irq(pdev, 0);
 224        if (irq <= 0) {
 225                ret = -EINVAL;
 226                goto err_disable_clk;
 227        }
 228
 229        ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
 230                               "NPCM_ADC", indio_dev);
 231        if (ret < 0) {
 232                dev_err(dev, "failed requesting interrupt\n");
 233                goto err_disable_clk;
 234        }
 235
 236        reg_con = ioread32(info->regs + NPCM_ADCCON);
 237        info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
 238        if (!IS_ERR(info->vref)) {
 239                ret = regulator_enable(info->vref);
 240                if (ret) {
 241                        dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
 242                        goto err_disable_clk;
 243                }
 244
 245                iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
 246                          info->regs + NPCM_ADCCON);
 247        } else {
 248                /*
 249                 * Any error which is not ENODEV indicates the regulator
 250                 * has been specified and so is a failure case.
 251                 */
 252                if (PTR_ERR(info->vref) != -ENODEV) {
 253                        ret = PTR_ERR(info->vref);
 254                        goto err_disable_clk;
 255                }
 256
 257                /* Use internal reference */
 258                iowrite32(reg_con | NPCM_ADCCON_REFSEL,
 259                          info->regs + NPCM_ADCCON);
 260        }
 261
 262        init_waitqueue_head(&info->wq);
 263
 264        reg_con = ioread32(info->regs + NPCM_ADCCON);
 265        reg_con |= NPCM_ADC_ENABLE;
 266
 267        /* Enable the ADC Module */
 268        iowrite32(reg_con, info->regs + NPCM_ADCCON);
 269
 270        /* Start ADC conversion */
 271        iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
 272
 273        platform_set_drvdata(pdev, indio_dev);
 274        indio_dev->name = dev_name(&pdev->dev);
 275        indio_dev->info = &npcm_adc_iio_info;
 276        indio_dev->modes = INDIO_DIRECT_MODE;
 277        indio_dev->channels = npcm_adc_iio_channels;
 278        indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
 279
 280        ret = iio_device_register(indio_dev);
 281        if (ret) {
 282                dev_err(&pdev->dev, "Couldn't register the device.\n");
 283                goto err_iio_register;
 284        }
 285
 286        pr_info("NPCM ADC driver probed\n");
 287
 288        return 0;
 289
 290err_iio_register:
 291        iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
 292        if (!IS_ERR(info->vref))
 293                regulator_disable(info->vref);
 294err_disable_clk:
 295        clk_disable_unprepare(info->adc_clk);
 296
 297        return ret;
 298}
 299
 300static int npcm_adc_remove(struct platform_device *pdev)
 301{
 302        struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 303        struct npcm_adc *info = iio_priv(indio_dev);
 304        u32 regtemp;
 305
 306        iio_device_unregister(indio_dev);
 307
 308        regtemp = ioread32(info->regs + NPCM_ADCCON);
 309        iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
 310        if (!IS_ERR(info->vref))
 311                regulator_disable(info->vref);
 312        clk_disable_unprepare(info->adc_clk);
 313
 314        return 0;
 315}
 316
 317static struct platform_driver npcm_adc_driver = {
 318        .probe          = npcm_adc_probe,
 319        .remove         = npcm_adc_remove,
 320        .driver         = {
 321                .name   = "npcm_adc",
 322                .of_match_table = npcm_adc_match,
 323        },
 324};
 325
 326module_platform_driver(npcm_adc_driver);
 327
 328MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver");
 329MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
 330MODULE_LICENSE("GPL v2");
 331