linux/drivers/staging/iio/adc/lpc32xx_adc.c
<<
>>
Prefs
   1/*
   2 *  lpc32xx_adc.c - Support for ADC in LPC32XX
   3 *
   4 *  3-channel, 10-bit ADC
   5 *
   6 *  Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
   7 *
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License as published by
  10 *  the Free Software Foundation; either version 2 of the License, or
  11 *  (at your option) any later version.
  12 *
  13 *  This program is distributed in the hope that it will be useful,
  14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 *  GNU General Public License for more details.
  17 *
  18 *  You should have received a copy of the GNU General Public License
  19 *  along with this program; if not, write to the Free Software
  20 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21 */
  22
  23#include <linux/module.h>
  24#include <linux/platform_device.h>
  25#include <linux/interrupt.h>
  26#include <linux/device.h>
  27#include <linux/kernel.h>
  28#include <linux/slab.h>
  29#include <linux/io.h>
  30#include <linux/clk.h>
  31#include <linux/err.h>
  32#include <linux/completion.h>
  33
  34#include "../iio.h"
  35#include "../sysfs.h"
  36
  37/*
  38 * LPC32XX registers definitions
  39 */
  40#define LPC32XX_ADC_SELECT(x)   ((x) + 0x04)
  41#define LPC32XX_ADC_CTRL(x)     ((x) + 0x08)
  42#define LPC32XX_ADC_VALUE(x)    ((x) + 0x48)
  43
  44/* Bit definitions for LPC32XX_ADC_SELECT: */
  45#define AD_REFm         0x00000200 /* constant, always write this value! */
  46#define AD_REFp         0x00000080 /* constant, always write this value! */
  47#define AD_IN           0x00000010 /* multiple of this is the */
  48                                   /* channel number: 0, 1, 2 */
  49#define AD_INTERNAL     0x00000004 /* constant, always write this value! */
  50
  51/* Bit definitions for LPC32XX_ADC_CTRL: */
  52#define AD_STROBE       0x00000002
  53#define AD_PDN_CTRL     0x00000004
  54
  55/* Bit definitions for LPC32XX_ADC_VALUE: */
  56#define ADC_VALUE_MASK  0x000003FF
  57
  58#define MOD_NAME "lpc32xx-adc"
  59
  60struct lpc32xx_adc_info {
  61        void __iomem *adc_base;
  62        struct clk *clk;
  63        struct completion completion;
  64
  65        u32 value;
  66};
  67
  68static int lpc32xx_read_raw(struct iio_dev *indio_dev,
  69                                struct iio_chan_spec const *chan,
  70                                int *val,
  71                                int *val2,
  72                                long mask)
  73{
  74        struct lpc32xx_adc_info *info = iio_priv(indio_dev);
  75
  76        if (mask == 0) {
  77                mutex_lock(&indio_dev->mlock);
  78                clk_enable(info->clk);
  79                /* Measurement setup */
  80                __raw_writel(AD_INTERNAL | (chan->address) | AD_REFp | AD_REFm,
  81                        LPC32XX_ADC_SELECT(info->adc_base));
  82                /* Trigger conversion */
  83                __raw_writel(AD_PDN_CTRL | AD_STROBE,
  84                        LPC32XX_ADC_CTRL(info->adc_base));
  85                wait_for_completion(&info->completion); /* set by ISR */
  86                clk_disable(info->clk);
  87                *val = info->value;
  88                mutex_unlock(&indio_dev->mlock);
  89
  90                return IIO_VAL_INT;
  91        }
  92
  93        return -EINVAL;
  94}
  95
  96static const struct iio_info lpc32xx_adc_iio_info = {
  97        .read_raw = &lpc32xx_read_raw,
  98        .driver_module = THIS_MODULE,
  99};
 100
 101#define LPC32XX_ADC_CHANNEL(_index) {           \
 102        .type = IIO_VOLTAGE,                    \
 103        .indexed = 1,                           \
 104        .channel = _index,                      \
 105        .address = AD_IN * _index,              \
 106        .scan_index = _index,                   \
 107}
 108
 109static struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
 110        LPC32XX_ADC_CHANNEL(0),
 111        LPC32XX_ADC_CHANNEL(1),
 112        LPC32XX_ADC_CHANNEL(2),
 113};
 114
 115static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
 116{
 117        struct lpc32xx_adc_info *info = (struct lpc32xx_adc_info *) dev_id;
 118
 119        /* Read value and clear irq */
 120        info->value = __raw_readl(LPC32XX_ADC_VALUE(info->adc_base)) &
 121                                ADC_VALUE_MASK;
 122        complete(&info->completion);
 123
 124        return IRQ_HANDLED;
 125}
 126
 127static int __devinit lpc32xx_adc_probe(struct platform_device *pdev)
 128{
 129        struct lpc32xx_adc_info *info = NULL;
 130        struct resource *res;
 131        int retval = -ENODEV;
 132        struct iio_dev *iodev = NULL;
 133        int irq;
 134
 135        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 136        if (!res) {
 137                dev_err(&pdev->dev, "failed to get platform I/O memory\n");
 138                retval = -EBUSY;
 139                goto errout1;
 140        }
 141
 142        iodev = iio_allocate_device(sizeof(struct lpc32xx_adc_info));
 143        if (!iodev) {
 144                dev_err(&pdev->dev, "failed allocating iio device\n");
 145                retval = -ENOMEM;
 146                goto errout1;
 147        }
 148
 149        info = iio_priv(iodev);
 150
 151        info->adc_base = ioremap(res->start, res->end - res->start + 1);
 152        if (!info->adc_base) {
 153                dev_err(&pdev->dev, "failed mapping memory\n");
 154                retval = -EBUSY;
 155                goto errout2;
 156        }
 157
 158        info->clk = clk_get(&pdev->dev, NULL);
 159        if (IS_ERR(info->clk)) {
 160                dev_err(&pdev->dev, "failed getting clock\n");
 161                goto errout3;
 162        }
 163
 164        irq = platform_get_irq(pdev, 0);
 165        if ((irq < 0) || (irq >= NR_IRQS)) {
 166                dev_err(&pdev->dev, "failed getting interrupt resource\n");
 167                retval = -EINVAL;
 168                goto errout4;
 169        }
 170
 171        retval = request_irq(irq, lpc32xx_adc_isr, 0, MOD_NAME, info);
 172        if (retval < 0) {
 173                dev_err(&pdev->dev, "failed requesting interrupt\n");
 174                goto errout4;
 175        }
 176
 177        platform_set_drvdata(pdev, iodev);
 178
 179        init_completion(&info->completion);
 180
 181        iodev->name = MOD_NAME;
 182        iodev->dev.parent = &pdev->dev;
 183        iodev->info = &lpc32xx_adc_iio_info;
 184        iodev->modes = INDIO_DIRECT_MODE;
 185        iodev->channels = lpc32xx_adc_iio_channels;
 186        iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
 187
 188        retval = iio_device_register(iodev);
 189        if (retval)
 190                goto errout5;
 191
 192        dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq);
 193
 194        return 0;
 195
 196errout5:
 197        free_irq(irq, iodev);
 198errout4:
 199        clk_put(info->clk);
 200errout3:
 201        iounmap(info->adc_base);
 202errout2:
 203        iio_free_device(iodev);
 204errout1:
 205        return retval;
 206}
 207
 208static int __devexit lpc32xx_adc_remove(struct platform_device *pdev)
 209{
 210        struct iio_dev *iodev = platform_get_drvdata(pdev);
 211        struct lpc32xx_adc_info *info = iio_priv(iodev);
 212        int irq = platform_get_irq(pdev, 0);
 213
 214        iio_device_unregister(iodev);
 215        free_irq(irq, iodev);
 216        platform_set_drvdata(pdev, NULL);
 217        clk_put(info->clk);
 218        iounmap(info->adc_base);
 219        iio_free_device(iodev);
 220
 221        return 0;
 222}
 223
 224static struct platform_driver lpc32xx_adc_driver = {
 225        .probe          = lpc32xx_adc_probe,
 226        .remove         = __devexit_p(lpc32xx_adc_remove),
 227        .driver         = {
 228                .name   = MOD_NAME,
 229                .owner  = THIS_MODULE,
 230        },
 231};
 232
 233module_platform_driver(lpc32xx_adc_driver);
 234
 235MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
 236MODULE_DESCRIPTION("LPC32XX ADC driver");
 237MODULE_LICENSE("GPL");
 238