aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio/adc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-12-16 21:55:20 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-16 21:55:20 -0500
commit2dfea3803dcf70983d14ce1dcbb3e97a7459a28b (patch)
tree59bffc7389ff554585f79d7cc06021790dc2b317 /drivers/iio/adc
parentaed606e3bc1f10753254db308d3fd8c053c41328 (diff)
parent1881b68b8961a86d40c3c5c205e533515a2dc9c6 (diff)
Merge tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
Pull MFS update from Samuel Ortiz: "This is the MFD patch set for the 3.8 merge window. We have several new drivers, most of the time coming with their sub devices drivers: - Austria Microsystem's AS3711 - Nano River's viperboard - TI's TPS80031, AM335x TS/ADC, - Realtek's MMC/memstick card reader - Nokia's retu We also got some notable cleanups and improvements: - tps6586x got converted to IRQ domains. - tps65910 and tps65090 moved to the regmap IRQ API. - STMPE is now Device Tree aware. - A general twl6040 and twl-core cleanup, with moves to the regmap I/O and IRQ APIs and a conversion to the recently added PWM framework. - sta2x11 gained regmap support. Then the rest is mostly tiny cleanups and fixes, among which we have Mark's wm5xxx and wm8xxx patchset." Far amount of annoying but largely trivial conflicts. Many due to __devinit/exit removal, others due to one or two of the new drivers also having come in through another tree. * tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (119 commits) mfd: tps6507x: Convert to devm_kzalloc mfd: stmpe: Update DT support for stmpe driver mfd: wm5102: Add readback of DSP status 3 register mfd: arizona: Log if we fail to create the primary IRQ domain mfd: tps80031: MFD_TPS80031 needs to select REGMAP_IRQ mfd: tps80031: Add terminating entry for tps80031_id_table mfd: sta2x11: Fix potential NULL pointer dereference in __sta2x11_mfd_mask() mfd: wm5102: Add tuning for revision B mfd: arizona: Defer patch initialistation until after first device boot mfd: tps65910: Fix wrong ack_base register mfd: tps65910: Remove unused data mfd: stmpe: Get rid of irq_invert_polarity mfd: ab8500-core: Fix invalid free of devm_ allocated data mfd: wm5102: Mark DSP memory regions as volatile mfd: wm5102: Correct default for LDO1_CONTROL_2 mfd: arizona: Register haptics devices mfd: wm8994: Make current device behaviour the default mfd: tps65090: MFD_TPS65090 needs to select REGMAP_IRQ mfd: Fix stmpe.c build when OF is not enabled mfd: jz4740-adc: Use devm_kzalloc ...
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r--drivers/iio/adc/Kconfig14
-rw-r--r--drivers/iio/adc/Makefile3
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c260
-rw-r--r--drivers/iio/adc/viperboard_adc.c181
4 files changed, 457 insertions, 1 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 961b8d0a4bac..fe822a14d130 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -125,4 +125,18 @@ config TI_ADC081C
125 This driver can also be built as a module. If so, the module will be 125 This driver can also be built as a module. If so, the module will be
126 called ti-adc081c. 126 called ti-adc081c.
127 127
128config TI_AM335X_ADC
129 tristate "TI's ADC driver"
130 depends on MFD_TI_AM335X_TSCADC
131 help
132 Say yes here to build support for Texas Instruments ADC
133 driver which is also a MFD client.
134
135config VIPERBOARD_ADC
136 tristate "Viperboard ADC support"
137 depends on MFD_VIPERBOARD && USB
138 help
139 Say yes here to access the ADC part of the Nano River
140 Technologies Viperboard.
141
128endmenu 142endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 472fd7cd2417..2d5f10080d8d 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -13,4 +13,5 @@ obj-$(CONFIG_AT91_ADC) += at91_adc.o
13obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o 13obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
14obj-$(CONFIG_MAX1363) += max1363.o 14obj-$(CONFIG_MAX1363) += max1363.o
15obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o 15obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
16 16obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
17obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
new file mode 100644
index 000000000000..02a43c87a8a3
--- /dev/null
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -0,0 +1,260 @@
1/*
2 * TI ADC MFD driver
3 *
4 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation version 2.
9 *
10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11 * kind, whether express or implied; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/err.h>
19#include <linux/module.h>
20#include <linux/slab.h>
21#include <linux/interrupt.h>
22#include <linux/platform_device.h>
23#include <linux/io.h>
24#include <linux/iio/iio.h>
25
26#include <linux/mfd/ti_am335x_tscadc.h>
27#include <linux/platform_data/ti_am335x_adc.h>
28
29struct tiadc_device {
30 struct ti_tscadc_dev *mfd_tscadc;
31 int channels;
32};
33
34static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
35{
36 return readl(adc->mfd_tscadc->tscadc_base + reg);
37}
38
39static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
40 unsigned int val)
41{
42 writel(val, adc->mfd_tscadc->tscadc_base + reg);
43}
44
45static void tiadc_step_config(struct tiadc_device *adc_dev)
46{
47 unsigned int stepconfig;
48 int i, channels = 0, steps;
49
50 /*
51 * There are 16 configurable steps and 8 analog input
52 * lines available which are shared between Touchscreen and ADC.
53 *
54 * Steps backwards i.e. from 16 towards 0 are used by ADC
55 * depending on number of input lines needed.
56 * Channel would represent which analog input
57 * needs to be given to ADC to digitalize data.
58 */
59
60 steps = TOTAL_STEPS - adc_dev->channels;
61 channels = TOTAL_CHANNELS - adc_dev->channels;
62
63 stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
64
65 for (i = (steps + 1); i <= TOTAL_STEPS; i++) {
66 tiadc_writel(adc_dev, REG_STEPCONFIG(i),
67 stepconfig | STEPCONFIG_INP(channels));
68 tiadc_writel(adc_dev, REG_STEPDELAY(i),
69 STEPCONFIG_OPENDLY);
70 channels++;
71 }
72 tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
73}
74
75static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
76{
77 struct iio_chan_spec *chan_array;
78 int i;
79
80 indio_dev->num_channels = channels;
81 chan_array = kcalloc(indio_dev->num_channels,
82 sizeof(struct iio_chan_spec), GFP_KERNEL);
83
84 if (chan_array == NULL)
85 return -ENOMEM;
86
87 for (i = 0; i < (indio_dev->num_channels); i++) {
88 struct iio_chan_spec *chan = chan_array + i;
89 chan->type = IIO_VOLTAGE;
90 chan->indexed = 1;
91 chan->channel = i;
92 chan->info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT;
93 }
94
95 indio_dev->channels = chan_array;
96
97 return indio_dev->num_channels;
98}
99
100static void tiadc_channels_remove(struct iio_dev *indio_dev)
101{
102 kfree(indio_dev->channels);
103}
104
105static int tiadc_read_raw(struct iio_dev *indio_dev,
106 struct iio_chan_spec const *chan,
107 int *val, int *val2, long mask)
108{
109 struct tiadc_device *adc_dev = iio_priv(indio_dev);
110 int i;
111 unsigned int fifo1count, readx1;
112
113 /*
114 * When the sub-system is first enabled,
115 * the sequencer will always start with the
116 * lowest step (1) and continue until step (16).
117 * For ex: If we have enabled 4 ADC channels and
118 * currently use only 1 out of them, the
119 * sequencer still configures all the 4 steps,
120 * leading to 3 unwanted data.
121 * Hence we need to flush out this data.
122 */
123
124 fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
125 for (i = 0; i < fifo1count; i++) {
126 readx1 = tiadc_readl(adc_dev, REG_FIFO1);
127 if (i == chan->channel)
128 *val = readx1 & 0xfff;
129 }
130 tiadc_writel(adc_dev, REG_SE, STPENB_STEPENB);
131
132 return IIO_VAL_INT;
133}
134
135static const struct iio_info tiadc_info = {
136 .read_raw = &tiadc_read_raw,
137};
138
139static int __devinit tiadc_probe(struct platform_device *pdev)
140{
141 struct iio_dev *indio_dev;
142 struct tiadc_device *adc_dev;
143 struct ti_tscadc_dev *tscadc_dev = pdev->dev.platform_data;
144 struct mfd_tscadc_board *pdata;
145 int err;
146
147 pdata = tscadc_dev->dev->platform_data;
148 if (!pdata || !pdata->adc_init) {
149 dev_err(&pdev->dev, "Could not find platform data\n");
150 return -EINVAL;
151 }
152
153 indio_dev = iio_device_alloc(sizeof(struct tiadc_device));
154 if (indio_dev == NULL) {
155 dev_err(&pdev->dev, "failed to allocate iio device\n");
156 err = -ENOMEM;
157 goto err_ret;
158 }
159 adc_dev = iio_priv(indio_dev);
160
161 adc_dev->mfd_tscadc = tscadc_dev;
162 adc_dev->channels = pdata->adc_init->adc_channels;
163
164 indio_dev->dev.parent = &pdev->dev;
165 indio_dev->name = dev_name(&pdev->dev);
166 indio_dev->modes = INDIO_DIRECT_MODE;
167 indio_dev->info = &tiadc_info;
168
169 tiadc_step_config(adc_dev);
170
171 err = tiadc_channel_init(indio_dev, adc_dev->channels);
172 if (err < 0)
173 goto err_free_device;
174
175 err = iio_device_register(indio_dev);
176 if (err)
177 goto err_free_channels;
178
179 platform_set_drvdata(pdev, indio_dev);
180
181 return 0;
182
183err_free_channels:
184 tiadc_channels_remove(indio_dev);
185err_free_device:
186 iio_device_free(indio_dev);
187err_ret:
188 return err;
189}
190
191static int __devexit tiadc_remove(struct platform_device *pdev)
192{
193 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
194
195 iio_device_unregister(indio_dev);
196 tiadc_channels_remove(indio_dev);
197
198 iio_device_free(indio_dev);
199
200 return 0;
201}
202
203#ifdef CONFIG_PM
204static int tiadc_suspend(struct device *dev)
205{
206 struct iio_dev *indio_dev = dev_get_drvdata(dev);
207 struct tiadc_device *adc_dev = iio_priv(indio_dev);
208 struct ti_tscadc_dev *tscadc_dev = dev->platform_data;
209 unsigned int idle;
210
211 if (!device_may_wakeup(tscadc_dev->dev)) {
212 idle = tiadc_readl(adc_dev, REG_CTRL);
213 idle &= ~(CNTRLREG_TSCSSENB);
214 tiadc_writel(adc_dev, REG_CTRL, (idle |
215 CNTRLREG_POWERDOWN));
216 }
217
218 return 0;
219}
220
221static int tiadc_resume(struct device *dev)
222{
223 struct iio_dev *indio_dev = dev_get_drvdata(dev);
224 struct tiadc_device *adc_dev = iio_priv(indio_dev);
225 unsigned int restore;
226
227 /* Make sure ADC is powered up */
228 restore = tiadc_readl(adc_dev, REG_CTRL);
229 restore &= ~(CNTRLREG_POWERDOWN);
230 tiadc_writel(adc_dev, REG_CTRL, restore);
231
232 tiadc_step_config(adc_dev);
233
234 return 0;
235}
236
237static const struct dev_pm_ops tiadc_pm_ops = {
238 .suspend = tiadc_suspend,
239 .resume = tiadc_resume,
240};
241#define TIADC_PM_OPS (&tiadc_pm_ops)
242#else
243#define TIADC_PM_OPS NULL
244#endif
245
246static struct platform_driver tiadc_driver = {
247 .driver = {
248 .name = "tiadc",
249 .owner = THIS_MODULE,
250 .pm = TIADC_PM_OPS,
251 },
252 .probe = tiadc_probe,
253 .remove = __devexit_p(tiadc_remove),
254};
255
256module_platform_driver(tiadc_driver);
257
258MODULE_DESCRIPTION("TI ADC controller driver");
259MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
260MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c
new file mode 100644
index 000000000000..10136a8b20d4
--- /dev/null
+++ b/drivers/iio/adc/viperboard_adc.c
@@ -0,0 +1,181 @@
1/*
2 * Nano River Technologies viperboard IIO ADC driver
3 *
4 * (C) 2012 by Lemonage GmbH
5 * Author: Lars Poeschel <poeschel@lemonage.de>
6 * All rights reserved.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 */
14
15#include <linux/kernel.h>
16#include <linux/errno.h>
17#include <linux/module.h>
18#include <linux/slab.h>
19#include <linux/types.h>
20#include <linux/mutex.h>
21#include <linux/platform_device.h>
22
23#include <linux/usb.h>
24#include <linux/iio/iio.h>
25
26#include <linux/mfd/viperboard.h>
27
28#define VPRBRD_ADC_CMD_GET 0x00
29
30struct vprbrd_adc_msg {
31 u8 cmd;
32 u8 chan;
33 u8 val;
34} __packed;
35
36struct vprbrd_adc {
37 struct vprbrd *vb;
38};
39
40#define VPRBRD_ADC_CHANNEL(_index) { \
41 .type = IIO_VOLTAGE, \
42 .indexed = 1, \
43 .channel = _index, \
44 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
45 .scan_index = _index, \
46 .scan_type = { \
47 .sign = 'u', \
48 .realbits = 8, \
49 .storagebits = 8, \
50 }, \
51}
52
53static struct iio_chan_spec const vprbrd_adc_iio_channels[] = {
54 VPRBRD_ADC_CHANNEL(0),
55 VPRBRD_ADC_CHANNEL(1),
56 VPRBRD_ADC_CHANNEL(2),
57 VPRBRD_ADC_CHANNEL(3),
58};
59
60static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,
61 struct iio_chan_spec const *chan,
62 int *val,
63 int *val2,
64 long info)
65{
66 int ret, error = 0;
67 struct vprbrd_adc *adc = iio_priv(iio_dev);
68 struct vprbrd *vb = adc->vb;
69 struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf;
70
71 switch (info) {
72 case IIO_CHAN_INFO_RAW:
73 mutex_lock(&vb->lock);
74
75 admsg->cmd = VPRBRD_ADC_CMD_GET;
76 admsg->chan = chan->scan_index;
77 admsg->val = 0x00;
78
79 ret = usb_control_msg(vb->usb_dev,
80 usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
81 VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg,
82 sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
83 if (ret != sizeof(struct vprbrd_adc_msg)) {
84 dev_err(&iio_dev->dev, "usb send error on adc read\n");
85 error = -EREMOTEIO;
86 }
87
88 ret = usb_control_msg(vb->usb_dev,
89 usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
90 VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg,
91 sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
92
93 *val = admsg->val;
94
95 mutex_unlock(&vb->lock);
96
97 if (ret != sizeof(struct vprbrd_adc_msg)) {
98 dev_err(&iio_dev->dev, "usb recv error on adc read\n");
99 error = -EREMOTEIO;
100 }
101
102 if (error)
103 goto error;
104
105 return IIO_VAL_INT;
106 default:
107 error = -EINVAL;
108 break;
109 }
110error:
111 return error;
112}
113
114static const struct iio_info vprbrd_adc_iio_info = {
115 .read_raw = &vprbrd_iio_read_raw,
116 .driver_module = THIS_MODULE,
117};
118
119static int __devinit vprbrd_adc_probe(struct platform_device *pdev)
120{
121 struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
122 struct vprbrd_adc *adc;
123 struct iio_dev *indio_dev;
124 int ret;
125
126 /* registering iio */
127 indio_dev = iio_device_alloc(sizeof(*adc));
128 if (!indio_dev) {
129 dev_err(&pdev->dev, "failed allocating iio device\n");
130 return -ENOMEM;
131 }
132
133 adc = iio_priv(indio_dev);
134 adc->vb = vb;
135 indio_dev->name = "viperboard adc";
136 indio_dev->dev.parent = &pdev->dev;
137 indio_dev->info = &vprbrd_adc_iio_info;
138 indio_dev->modes = INDIO_DIRECT_MODE;
139 indio_dev->channels = vprbrd_adc_iio_channels;
140 indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels);
141
142 ret = iio_device_register(indio_dev);
143 if (ret) {
144 dev_err(&pdev->dev, "could not register iio (adc)");
145 goto error;
146 }
147
148 platform_set_drvdata(pdev, indio_dev);
149
150 return 0;
151
152error:
153 iio_device_free(indio_dev);
154 return ret;
155}
156
157static int __devexit vprbrd_adc_remove(struct platform_device *pdev)
158{
159 struct iio_dev *indio_dev = platform_get_drvdata(pdev);
160
161 iio_device_unregister(indio_dev);
162 iio_device_free(indio_dev);
163
164 return 0;
165}
166
167static struct platform_driver vprbrd_adc_driver = {
168 .driver = {
169 .name = "viperboard-adc",
170 .owner = THIS_MODULE,
171 },
172 .probe = vprbrd_adc_probe,
173 .remove = __devexit_p(vprbrd_adc_remove),
174};
175
176module_platform_driver(vprbrd_adc_driver);
177
178MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
179MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard");
180MODULE_LICENSE("GPL");
181MODULE_ALIAS("platform:viperboard-adc");