aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNaveen Krishna Chatradhi <ch.naveen@samsung.com>2013-02-15 01:56:00 -0500
committerJonathan Cameron <jic23@kernel.org>2013-03-16 06:18:15 -0400
commit10f5b14811023df0ba1a936b14880eabb6d9c199 (patch)
treec525ef06cf65c56f5a10fa1e9a12b82ed997ee7d
parent43bb786ad2886ea38364e57924c19e9d29f37201 (diff)
iio: adc: add exynos adc driver under iio framwork
This patch adds New driver to support: 1. Supports ADC IF found on EXYNOS4412/EXYNOS5250 and future SoCs from Samsung 2. Add ADC driver under iio/adc framework 3. Also adds the Documentation for device tree bindings Signed-off-by: Naveen Krishna Chatradhi <ch.naveen@samsung.com> Reviewed-by: Lars-Peter Clausen <lars@metafoo.de> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r--Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt52
-rw-r--r--drivers/iio/adc/Kconfig7
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/exynos_adc.c440
4 files changed, 500 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
new file mode 100644
index 000000000000..f68637861b05
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/samsung/exynos-adc.txt
@@ -0,0 +1,52 @@
1Samsung Exynos Analog to Digital Converter bindings
2
3This devicetree binding are for the new adc driver written fori
4Exynos4 and upward SoCs from Samsung.
5
6New driver handles the following
71. Supports ADC IF found on EXYNOS4412/EXYNOS5250
8 and future SoCs from Samsung
92. Add ADC driver under iio/adc framework
103. Also adds the Documentation for device tree bindings
11
12Required properties:
13- compatible: Must be "samsung,exynos-adc-v1"
14 for exynos4412/5250 controllers.
15 Must be "samsung,exynos-adc-v2" for
16 future controllers.
17- reg: Contains ADC register address range (base address and
18 length).
19- interrupts: Contains the interrupt information for the timer. The
20 format is being dependent on which interrupt controller
21 the Samsung device uses.
22- #io-channel-cells = <1>; As ADC has multiple outputs
23
24Note: child nodes can be added for auto probing from device tree.
25
26Example: adding device info in dtsi file
27
28adc: adc@12D10000 {
29 compatible = "samsung,exynos-adc-v1";
30 reg = <0x12D10000 0x100>;
31 interrupts = <0 106 0>;
32 #io-channel-cells = <1>;
33 io-channel-ranges;
34};
35
36
37Example: Adding child nodes in dts file
38
39adc@12D10000 {
40
41 /* NTC thermistor is a hwmon device */
42 ncp15wb473@0 {
43 compatible = "ntc,ncp15wb473";
44 pullup-uV = <1800000>;
45 pullup-ohm = <47000>;
46 pulldown-ohm = <0>;
47 io-channels = <&adc 4>;
48 };
49};
50
51Note: Does not apply to ADC driver under arch/arm/plat-samsung/
52Note: The child node can be added under the adc node or seperately.
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 00213eaf8aa9..a40d3c29f0cb 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -103,6 +103,13 @@ config AT91_ADC
103 help 103 help
104 Say yes here to build support for Atmel AT91 ADC. 104 Say yes here to build support for Atmel AT91 ADC.
105 105
106config EXYNOS_ADC
107 bool "Exynos ADC driver support"
108 help
109 Core support for the ADC block found in the Samsung EXYNOS series
110 of SoCs for drivers such as the touchscreen and hwmon to use to share
111 this resource.
112
106config LP8788_ADC 113config LP8788_ADC
107 bool "LP8788 ADC driver" 114 bool "LP8788 ADC driver"
108 depends on MFD_LP8788 115 depends on MFD_LP8788
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index ab910ba4664c..0a825bed43f6 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
11obj-$(CONFIG_AD7793) += ad7793.o 11obj-$(CONFIG_AD7793) += ad7793.o
12obj-$(CONFIG_AD7887) += ad7887.o 12obj-$(CONFIG_AD7887) += ad7887.o
13obj-$(CONFIG_AT91_ADC) += at91_adc.o 13obj-$(CONFIG_AT91_ADC) += at91_adc.o
14obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
14obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o 15obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
15obj-$(CONFIG_MAX1363) += max1363.o 16obj-$(CONFIG_MAX1363) += max1363.o
16obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o 17obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
new file mode 100644
index 000000000000..ed6fdd7e5212
--- /dev/null
+++ b/drivers/iio/adc/exynos_adc.c
@@ -0,0 +1,440 @@
1/*
2 * exynos_adc.c - Support for ADC in EXYNOS SoCs
3 *
4 * 8 ~ 10 channel, 10/12-bit ADC
5 *
6 * Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.com>
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/delay.h>
27#include <linux/kernel.h>
28#include <linux/slab.h>
29#include <linux/io.h>
30#include <linux/clk.h>
31#include <linux/completion.h>
32#include <linux/of.h>
33#include <linux/of_irq.h>
34#include <linux/regulator/consumer.h>
35#include <linux/of_platform.h>
36
37#include <linux/iio/iio.h>
38#include <linux/iio/machine.h>
39#include <linux/iio/driver.h>
40
41enum adc_version {
42 ADC_V1,
43 ADC_V2
44};
45
46/* EXYNOS4412/5250 ADC_V1 registers definitions */
47#define ADC_V1_CON(x) ((x) + 0x00)
48#define ADC_V1_DLY(x) ((x) + 0x08)
49#define ADC_V1_DATX(x) ((x) + 0x0C)
50#define ADC_V1_INTCLR(x) ((x) + 0x18)
51#define ADC_V1_MUX(x) ((x) + 0x1c)
52
53/* Future ADC_V2 registers definitions */
54#define ADC_V2_CON1(x) ((x) + 0x00)
55#define ADC_V2_CON2(x) ((x) + 0x04)
56#define ADC_V2_STAT(x) ((x) + 0x08)
57#define ADC_V2_INT_EN(x) ((x) + 0x10)
58#define ADC_V2_INT_ST(x) ((x) + 0x14)
59#define ADC_V2_VER(x) ((x) + 0x20)
60
61/* Bit definitions for ADC_V1 */
62#define ADC_V1_CON_RES (1u << 16)
63#define ADC_V1_CON_PRSCEN (1u << 14)
64#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6)
65#define ADC_V1_CON_STANDBY (1u << 2)
66
67/* Bit definitions for ADC_V2 */
68#define ADC_V2_CON1_SOFT_RESET (1u << 2)
69
70#define ADC_V2_CON2_OSEL (1u << 10)
71#define ADC_V2_CON2_ESEL (1u << 9)
72#define ADC_V2_CON2_HIGHF (1u << 8)
73#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4)
74#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0)
75#define ADC_V2_CON2_ACH_MASK 0xF
76
77#define MAX_ADC_V2_CHANNELS 10
78#define MAX_ADC_V1_CHANNELS 8
79
80/* Bit definitions common for ADC_V1 and ADC_V2 */
81#define ADC_CON_EN_START (1u << 0)
82#define ADC_DATX_MASK 0xFFF
83
84#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(1000))
85
86struct exynos_adc {
87 void __iomem *regs;
88 struct clk *clk;
89 unsigned int irq;
90 struct regulator *vdd;
91
92 struct completion completion;
93
94 u32 value;
95 unsigned int version;
96};
97
98static const struct of_device_id exynos_adc_match[] = {
99 { .compatible = "samsung,exynos-adc-v1", .data = (void *)ADC_V1 },
100 { .compatible = "samsung,exynos-adc-v2", .data = (void *)ADC_V2 },
101 {},
102};
103MODULE_DEVICE_TABLE(of, exynos_adc_match);
104
105static inline unsigned int exynos_adc_get_version(struct platform_device *pdev)
106{
107 const struct of_device_id *match;
108
109 match = of_match_node(exynos_adc_match, pdev->dev.of_node);
110 return (unsigned int)match->data;
111}
112
113static int exynos_read_raw(struct iio_dev *indio_dev,
114 struct iio_chan_spec const *chan,
115 int *val,
116 int *val2,
117 long mask)
118{
119 struct exynos_adc *info = iio_priv(indio_dev);
120 unsigned long timeout;
121 u32 con1, con2;
122
123 if (mask != IIO_CHAN_INFO_RAW)
124 return -EINVAL;
125
126 mutex_lock(&indio_dev->mlock);
127
128 /* Select the channel to be used and Trigger conversion */
129 if (info->version == ADC_V2) {
130 con2 = readl(ADC_V2_CON2(info->regs));
131 con2 &= ~ADC_V2_CON2_ACH_MASK;
132 con2 |= ADC_V2_CON2_ACH_SEL(chan->address);
133 writel(con2, ADC_V2_CON2(info->regs));
134
135 con1 = readl(ADC_V2_CON1(info->regs));
136 writel(con1 | ADC_CON_EN_START,
137 ADC_V2_CON1(info->regs));
138 } else {
139 writel(chan->address, ADC_V1_MUX(info->regs));
140
141 con1 = readl(ADC_V1_CON(info->regs));
142 writel(con1 | ADC_CON_EN_START,
143 ADC_V1_CON(info->regs));
144 }
145
146 timeout = wait_for_completion_interruptible_timeout
147 (&info->completion, EXYNOS_ADC_TIMEOUT);
148 *val = info->value;
149
150 mutex_unlock(&indio_dev->mlock);
151
152 if (timeout == 0)
153 return -ETIMEDOUT;
154
155 return IIO_VAL_INT;
156}
157
158static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
159{
160 struct exynos_adc *info = (struct exynos_adc *)dev_id;
161
162 /* Read value */
163 info->value = readl(ADC_V1_DATX(info->regs)) &
164 ADC_DATX_MASK;
165 /* clear irq */
166 if (info->version == ADC_V2)
167 writel(1, ADC_V2_INT_ST(info->regs));
168 else
169 writel(1, ADC_V1_INTCLR(info->regs));
170
171 complete(&info->completion);
172
173 return IRQ_HANDLED;
174}
175
176static int exynos_adc_reg_access(struct iio_dev *indio_dev,
177 unsigned reg, unsigned writeval,
178 unsigned *readval)
179{
180 struct exynos_adc *info = iio_priv(indio_dev);
181
182 if (readval == NULL)
183 return -EINVAL;
184
185 *readval = readl(info->regs + reg);
186
187 return 0;
188}
189
190static const struct iio_info exynos_adc_iio_info = {
191 .read_raw = &exynos_read_raw,
192 .debugfs_reg_access = &exynos_adc_reg_access,
193 .driver_module = THIS_MODULE,
194};
195
196#define ADC_CHANNEL(_index, _id) { \
197 .type = IIO_VOLTAGE, \
198 .indexed = 1, \
199 .channel = _index, \
200 .address = _index, \
201 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
202 .datasheet_name = _id, \
203}
204
205static const struct iio_chan_spec exynos_adc_iio_channels[] = {
206 ADC_CHANNEL(0, "adc0"),
207 ADC_CHANNEL(1, "adc1"),
208 ADC_CHANNEL(2, "adc2"),
209 ADC_CHANNEL(3, "adc3"),
210 ADC_CHANNEL(4, "adc4"),
211 ADC_CHANNEL(5, "adc5"),
212 ADC_CHANNEL(6, "adc6"),
213 ADC_CHANNEL(7, "adc7"),
214 ADC_CHANNEL(8, "adc8"),
215 ADC_CHANNEL(9, "adc9"),
216};
217
218static int exynos_adc_remove_devices(struct device *dev, void *c)
219{
220 struct platform_device *pdev = to_platform_device(dev);
221
222 platform_device_unregister(pdev);
223
224 return 0;
225}
226
227static void exynos_adc_hw_init(struct exynos_adc *info)
228{
229 u32 con1, con2;
230
231 if (info->version == ADC_V2) {
232 con1 = ADC_V2_CON1_SOFT_RESET;
233 writel(con1, ADC_V2_CON1(info->regs));
234
235 con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
236 ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0);
237 writel(con2, ADC_V2_CON2(info->regs));
238
239 /* Enable interrupts */
240 writel(1, ADC_V2_INT_EN(info->regs));
241 } else {
242 /* set default prescaler values and Enable prescaler */
243 con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
244
245 /* Enable 12-bit ADC resolution */
246 con1 |= ADC_V1_CON_RES;
247 writel(con1, ADC_V1_CON(info->regs));
248 }
249}
250
251static int exynos_adc_probe(struct platform_device *pdev)
252{
253 struct exynos_adc *info = NULL;
254 struct device_node *np = pdev->dev.of_node;
255 struct iio_dev *indio_dev = NULL;
256 struct resource *mem;
257 int ret = -ENODEV;
258 int irq;
259
260 if (!np)
261 return ret;
262
263 indio_dev = iio_device_alloc(sizeof(struct exynos_adc));
264 if (!indio_dev) {
265 dev_err(&pdev->dev, "failed allocating iio device\n");
266 return -ENOMEM;
267 }
268
269 info = iio_priv(indio_dev);
270
271 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
272
273 info->regs = devm_request_and_ioremap(&pdev->dev, mem);
274 if (!info->regs) {
275 ret = -ENOMEM;
276 goto err_iio;
277 }
278
279 irq = platform_get_irq(pdev, 0);
280 if (irq < 0) {
281 dev_err(&pdev->dev, "no irq resource?\n");
282 ret = irq;
283 goto err_iio;
284 }
285
286 info->irq = irq;
287
288 init_completion(&info->completion);
289
290 ret = request_irq(info->irq, exynos_adc_isr,
291 0, dev_name(&pdev->dev), info);
292 if (ret < 0) {
293 dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
294 info->irq);
295 goto err_iio;
296 }
297
298 info->clk = devm_clk_get(&pdev->dev, "adc");
299 if (IS_ERR(info->clk)) {
300 dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
301 PTR_ERR(info->clk));
302 ret = PTR_ERR(info->clk);
303 goto err_irq;
304 }
305
306 info->vdd = devm_regulator_get(&pdev->dev, "vdd");
307 if (IS_ERR(info->vdd)) {
308 dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
309 PTR_ERR(info->vdd));
310 ret = PTR_ERR(info->vdd);
311 goto err_irq;
312 }
313
314 info->version = exynos_adc_get_version(pdev);
315
316 platform_set_drvdata(pdev, indio_dev);
317
318 indio_dev->name = dev_name(&pdev->dev);
319 indio_dev->dev.parent = &pdev->dev;
320 indio_dev->dev.of_node = pdev->dev.of_node;
321 indio_dev->info = &exynos_adc_iio_info;
322 indio_dev->modes = INDIO_DIRECT_MODE;
323 indio_dev->channels = exynos_adc_iio_channels;
324
325 if (info->version == ADC_V1)
326 indio_dev->num_channels = MAX_ADC_V1_CHANNELS;
327 else
328 indio_dev->num_channels = MAX_ADC_V2_CHANNELS;
329
330 ret = iio_device_register(indio_dev);
331 if (ret)
332 goto err_irq;
333
334 ret = regulator_enable(info->vdd);
335 if (ret)
336 goto err_iio_dev;
337
338 clk_prepare_enable(info->clk);
339
340 exynos_adc_hw_init(info);
341
342 ret = of_platform_populate(np, exynos_adc_match, NULL, &pdev->dev);
343 if (ret < 0) {
344 dev_err(&pdev->dev, "failed adding child nodes\n");
345 goto err_of_populate;
346 }
347
348 return 0;
349
350err_of_populate:
351 device_for_each_child(&pdev->dev, NULL,
352 exynos_adc_remove_devices);
353 regulator_disable(info->vdd);
354 clk_disable_unprepare(info->clk);
355err_iio_dev:
356 iio_device_unregister(indio_dev);
357err_irq:
358 free_irq(info->irq, info);
359err_iio:
360 iio_device_free(indio_dev);
361 return ret;
362}
363
364static int exynos_adc_remove(struct platform_device *pdev)
365{
366 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
367 struct exynos_adc *info = iio_priv(indio_dev);
368
369 device_for_each_child(&pdev->dev, NULL,
370 exynos_adc_remove_devices);
371 regulator_disable(info->vdd);
372 clk_disable_unprepare(info->clk);
373 iio_device_unregister(indio_dev);
374 free_irq(info->irq, info);
375 iio_device_free(indio_dev);
376
377 return 0;
378}
379
380#ifdef CONFIG_PM_SLEEP
381static int exynos_adc_suspend(struct device *dev)
382{
383 struct platform_device *pdev = to_platform_device(dev);
384 struct exynos_adc *info = platform_get_drvdata(pdev);
385 u32 con;
386
387 if (info->version == ADC_V2) {
388 con = readl(ADC_V2_CON1(info->regs));
389 con &= ~ADC_CON_EN_START;
390 writel(con, ADC_V2_CON1(info->regs));
391 } else {
392 con = readl(ADC_V1_CON(info->regs));
393 con |= ADC_V1_CON_STANDBY;
394 writel(con, ADC_V1_CON(info->regs));
395 }
396
397 clk_disable_unprepare(info->clk);
398 regulator_disable(info->vdd);
399
400 return 0;
401}
402
403static int exynos_adc_resume(struct device *dev)
404{
405 struct platform_device *pdev = to_platform_device(dev);
406 struct exynos_adc *info = platform_get_drvdata(pdev);
407 int ret;
408
409 ret = regulator_enable(info->vdd);
410 if (ret)
411 return ret;
412
413 clk_prepare_enable(info->clk);
414
415 exynos_adc_hw_init(info);
416
417 return 0;
418}
419#endif
420
421static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops,
422 exynos_adc_suspend,
423 exynos_adc_resume);
424
425static struct platform_driver exynos_adc_driver = {
426 .probe = exynos_adc_probe,
427 .remove = exynos_adc_remove,
428 .driver = {
429 .name = "exynos-adc",
430 .owner = THIS_MODULE,
431 .of_match_table = of_match_ptr(exynos_adc_match),
432 .pm = &exynos_adc_pm_ops,
433 },
434};
435
436module_platform_driver(exynos_adc_driver);
437
438MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
439MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
440MODULE_LICENSE("GPL v2");