root/drivers/iio/adc/lpc18xx_adc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lpc18xx_adc_read_chan
  2. lpc18xx_adc_read_raw
  3. lpc18xx_adc_probe
  4. lpc18xx_adc_remove

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * IIO ADC driver for NXP LPC18xx ADC
   4  *
   5  * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
   6  *
   7  * UNSUPPORTED hardware features:
   8  *  - Hardware triggers
   9  *  - Burst mode
  10  *  - Interrupts
  11  *  - DMA
  12  */
  13 
  14 #include <linux/clk.h>
  15 #include <linux/err.h>
  16 #include <linux/iio/iio.h>
  17 #include <linux/iio/driver.h>
  18 #include <linux/io.h>
  19 #include <linux/iopoll.h>
  20 #include <linux/module.h>
  21 #include <linux/mutex.h>
  22 #include <linux/of.h>
  23 #include <linux/of_device.h>
  24 #include <linux/platform_device.h>
  25 #include <linux/regulator/consumer.h>
  26 
  27 /* LPC18XX ADC registers and bits */
  28 #define LPC18XX_ADC_CR                  0x000
  29 #define  LPC18XX_ADC_CR_CLKDIV_SHIFT    8
  30 #define  LPC18XX_ADC_CR_PDN             BIT(21)
  31 #define  LPC18XX_ADC_CR_START_NOW       (0x1 << 24)
  32 #define LPC18XX_ADC_GDR                 0x004
  33 
  34 /* Data register bits */
  35 #define LPC18XX_ADC_SAMPLE_SHIFT        6
  36 #define LPC18XX_ADC_SAMPLE_MASK         0x3ff
  37 #define LPC18XX_ADC_CONV_DONE           BIT(31)
  38 
  39 /* Clock should be 4.5 MHz or less */
  40 #define LPC18XX_ADC_CLK_TARGET          4500000
  41 
  42 struct lpc18xx_adc {
  43         struct regulator *vref;
  44         void __iomem *base;
  45         struct device *dev;
  46         struct mutex lock;
  47         struct clk *clk;
  48         u32 cr_reg;
  49 };
  50 
  51 #define LPC18XX_ADC_CHAN(_idx) {                                \
  52         .type = IIO_VOLTAGE,                                    \
  53         .indexed = 1,                                           \
  54         .channel = _idx,                                        \
  55         .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
  56         .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
  57 }
  58 
  59 static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = {
  60         LPC18XX_ADC_CHAN(0),
  61         LPC18XX_ADC_CHAN(1),
  62         LPC18XX_ADC_CHAN(2),
  63         LPC18XX_ADC_CHAN(3),
  64         LPC18XX_ADC_CHAN(4),
  65         LPC18XX_ADC_CHAN(5),
  66         LPC18XX_ADC_CHAN(6),
  67         LPC18XX_ADC_CHAN(7),
  68 };
  69 
  70 static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch)
  71 {
  72         int ret;
  73         u32 reg;
  74 
  75         reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW;
  76         writel(reg, adc->base + LPC18XX_ADC_CR);
  77 
  78         ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg,
  79                                  reg & LPC18XX_ADC_CONV_DONE, 3, 9);
  80         if (ret) {
  81                 dev_warn(adc->dev, "adc read timed out\n");
  82                 return ret;
  83         }
  84 
  85         return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK;
  86 }
  87 
  88 static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev,
  89                                 struct iio_chan_spec const *chan,
  90                                 int *val, int *val2, long mask)
  91 {
  92         struct lpc18xx_adc *adc = iio_priv(indio_dev);
  93 
  94         switch (mask) {
  95         case IIO_CHAN_INFO_RAW:
  96                 mutex_lock(&adc->lock);
  97                 *val = lpc18xx_adc_read_chan(adc, chan->channel);
  98                 mutex_unlock(&adc->lock);
  99                 if (*val < 0)
 100                         return *val;
 101 
 102                 return IIO_VAL_INT;
 103 
 104         case IIO_CHAN_INFO_SCALE:
 105                 *val = regulator_get_voltage(adc->vref) / 1000;
 106                 *val2 = 10;
 107 
 108                 return IIO_VAL_FRACTIONAL_LOG2;
 109         }
 110 
 111         return -EINVAL;
 112 }
 113 
 114 static const struct iio_info lpc18xx_adc_info = {
 115         .read_raw = lpc18xx_adc_read_raw,
 116 };
 117 
 118 static int lpc18xx_adc_probe(struct platform_device *pdev)
 119 {
 120         struct iio_dev *indio_dev;
 121         struct lpc18xx_adc *adc;
 122         struct resource *res;
 123         unsigned int clkdiv;
 124         unsigned long rate;
 125         int ret;
 126 
 127         indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
 128         if (!indio_dev)
 129                 return -ENOMEM;
 130 
 131         platform_set_drvdata(pdev, indio_dev);
 132         adc = iio_priv(indio_dev);
 133         adc->dev = &pdev->dev;
 134         mutex_init(&adc->lock);
 135 
 136         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 137         adc->base = devm_ioremap_resource(&pdev->dev, res);
 138         if (IS_ERR(adc->base))
 139                 return PTR_ERR(adc->base);
 140 
 141         adc->clk = devm_clk_get(&pdev->dev, NULL);
 142         if (IS_ERR(adc->clk)) {
 143                 dev_err(&pdev->dev, "error getting clock\n");
 144                 return PTR_ERR(adc->clk);
 145         }
 146 
 147         rate = clk_get_rate(adc->clk);
 148         clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET);
 149 
 150         adc->vref = devm_regulator_get(&pdev->dev, "vref");
 151         if (IS_ERR(adc->vref)) {
 152                 dev_err(&pdev->dev, "error getting regulator\n");
 153                 return PTR_ERR(adc->vref);
 154         }
 155 
 156         indio_dev->name = dev_name(&pdev->dev);
 157         indio_dev->dev.parent = &pdev->dev;
 158         indio_dev->info = &lpc18xx_adc_info;
 159         indio_dev->modes = INDIO_DIRECT_MODE;
 160         indio_dev->channels = lpc18xx_adc_iio_channels;
 161         indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels);
 162 
 163         ret = regulator_enable(adc->vref);
 164         if (ret) {
 165                 dev_err(&pdev->dev, "unable to enable regulator\n");
 166                 return ret;
 167         }
 168 
 169         ret = clk_prepare_enable(adc->clk);
 170         if (ret) {
 171                 dev_err(&pdev->dev, "unable to enable clock\n");
 172                 goto dis_reg;
 173         }
 174 
 175         adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) |
 176                         LPC18XX_ADC_CR_PDN;
 177         writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR);
 178 
 179         ret = iio_device_register(indio_dev);
 180         if (ret) {
 181                 dev_err(&pdev->dev, "unable to register device\n");
 182                 goto dis_clk;
 183         }
 184 
 185         return 0;
 186 
 187 dis_clk:
 188         writel(0, adc->base + LPC18XX_ADC_CR);
 189         clk_disable_unprepare(adc->clk);
 190 dis_reg:
 191         regulator_disable(adc->vref);
 192         return ret;
 193 }
 194 
 195 static int lpc18xx_adc_remove(struct platform_device *pdev)
 196 {
 197         struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 198         struct lpc18xx_adc *adc = iio_priv(indio_dev);
 199 
 200         iio_device_unregister(indio_dev);
 201 
 202         writel(0, adc->base + LPC18XX_ADC_CR);
 203         clk_disable_unprepare(adc->clk);
 204         regulator_disable(adc->vref);
 205 
 206         return 0;
 207 }
 208 
 209 static const struct of_device_id lpc18xx_adc_match[] = {
 210         { .compatible = "nxp,lpc1850-adc" },
 211         { /* sentinel */ }
 212 };
 213 MODULE_DEVICE_TABLE(of, lpc18xx_adc_match);
 214 
 215 static struct platform_driver lpc18xx_adc_driver = {
 216         .probe  = lpc18xx_adc_probe,
 217         .remove = lpc18xx_adc_remove,
 218         .driver = {
 219                 .name = "lpc18xx-adc",
 220                 .of_match_table = lpc18xx_adc_match,
 221         },
 222 };
 223 module_platform_driver(lpc18xx_adc_driver);
 224 
 225 MODULE_DESCRIPTION("LPC18xx ADC driver");
 226 MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
 227 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */