diff options
author | Roland Stigge <stigge@antcom.de> | 2012-02-14 13:44:56 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-02-15 20:21:43 -0500 |
commit | 906ecf69ad90e0f8c338caf34c55b3a870ccf5bc (patch) | |
tree | b2ad3c8bcd4a2854b8c68fdbda6311e265453a87 /drivers | |
parent | 413db8c1c75a276c046ea476fee9364ca8df4d12 (diff) |
staging: iio: LPC32xx: ADC driver
This patch adds a 3-channel ADC driver for the LPC32xx ARM SoC
Signed-off-by: Roland Stigge <stigge@antcom.de>
Acked-by: Jonathan Cameron <jic23@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/iio/adc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/staging/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/iio/adc/lpc32xx_adc.c | 237 |
3 files changed, 247 insertions, 0 deletions
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index d9decea4fa6..592eabd85f3 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig | |||
@@ -193,4 +193,13 @@ config MAX1363_RING_BUFFER | |||
193 | Say yes here to include ring buffer support in the MAX1363 | 193 | Say yes here to include ring buffer support in the MAX1363 |
194 | ADC driver. | 194 | ADC driver. |
195 | 195 | ||
196 | config LPC32XX_ADC | ||
197 | tristate "NXP LPC32XX ADC" | ||
198 | depends on ARCH_LPC32XX && !TOUCHSCREEN_LPC32XX | ||
199 | help | ||
200 | Say yes here to build support for the integrated ADC inside the | ||
201 | LPC32XX SoC. Note that this feature uses the same hardware as the | ||
202 | touchscreen driver, so you can only select one of the two drivers | ||
203 | (lpc32xx_adc or lpc32xx_ts). Provides direct access via sysfs. | ||
204 | |||
196 | endmenu | 205 | endmenu |
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index ceee7f3c306..f83ab9551d8 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile | |||
@@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o | |||
37 | obj-$(CONFIG_ADT7310) += adt7310.o | 37 | obj-$(CONFIG_ADT7310) += adt7310.o |
38 | obj-$(CONFIG_ADT7410) += adt7410.o | 38 | obj-$(CONFIG_ADT7410) += adt7410.o |
39 | obj-$(CONFIG_AD7280) += ad7280a.o | 39 | obj-$(CONFIG_AD7280) += ad7280a.o |
40 | obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o | ||
diff --git a/drivers/staging/iio/adc/lpc32xx_adc.c b/drivers/staging/iio/adc/lpc32xx_adc.c new file mode 100644 index 00000000000..dfc9033843a --- /dev/null +++ b/drivers/staging/iio/adc/lpc32xx_adc.c | |||
@@ -0,0 +1,237 @@ | |||
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 | |||
60 | struct lpc32xx_adc_info { | ||
61 | void __iomem *adc_base; | ||
62 | struct clk *clk; | ||
63 | struct completion completion; | ||
64 | |||
65 | u32 value; | ||
66 | }; | ||
67 | |||
68 | static 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 | |||
96 | static 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 | |||
109 | static 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 | |||
115 | static 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 | |||
127 | static 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 | |||
196 | errout5: | ||
197 | free_irq(irq, iodev); | ||
198 | errout4: | ||
199 | clk_put(info->clk); | ||
200 | errout3: | ||
201 | iounmap(info->adc_base); | ||
202 | errout2: | ||
203 | iio_free_device(iodev); | ||
204 | errout1: | ||
205 | return retval; | ||
206 | } | ||
207 | |||
208 | static 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 | |||
224 | static 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 | |||
233 | module_platform_driver(lpc32xx_adc_driver); | ||
234 | |||
235 | MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); | ||
236 | MODULE_DESCRIPTION("LPC32XX ADC driver"); | ||
237 | MODULE_LICENSE("GPL"); | ||