aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatil, Rachna <rachna@ti.com>2012-10-16 03:25:45 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-11-05 17:50:27 -0500
commit5e53a69b44e893227b046a7bc74db3cb40d7f39b (patch)
tree4586fb856eb6709e40df4c38b0840177adbf4d7c
parent2b99bafab19145a72e2c557326fc4662a864a162 (diff)
IIO : ADC: tiadc: Add support of TI's ADC driver
This patch adds support for TI's ADC driver. This is a multifunctional device. Analog input lines are provided on which voltage measurements can be carried out. You can have upto 8 input lines. Signed-off-by: Patil, Rachna <rachna@ti.com> Acked-by: Jonathan Cameron <jic23@kernel.org> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--drivers/iio/adc/Kconfig7
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c260
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c18
-rw-r--r--include/linux/mfd/ti_am335x_tscadc.h9
-rw-r--r--include/linux/platform_data/ti_am335x_adc.h14
6 files changed, 307 insertions, 2 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 492758120338..1401ed1af39f 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -60,4 +60,11 @@ config LP8788_ADC
60 help 60 help
61 Say yes here to build support for TI LP8788 ADC. 61 Say yes here to build support for TI LP8788 ADC.
62 62
63config TI_AM335X_ADC
64 tristate "TI's ADC driver"
65 depends on MFD_TI_AM335X_TSCADC
66 help
67 Say yes here to build support for Texas Instruments ADC
68 driver which is also a MFD client.
69
63endmenu 70endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 900995d5e179..4410a90fc84b 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_AD7476) += ad7476.o
8obj-$(CONFIG_AD7791) += ad7791.o 8obj-$(CONFIG_AD7791) += ad7791.o
9obj-$(CONFIG_AT91_ADC) += at91_adc.o 9obj-$(CONFIG_AT91_ADC) += at91_adc.o
10obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o 10obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
11obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_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/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index d812be4b61df..e947dd8bbcc3 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -25,6 +25,7 @@
25 25
26#include <linux/mfd/ti_am335x_tscadc.h> 26#include <linux/mfd/ti_am335x_tscadc.h>
27#include <linux/input/ti_am335x_tsc.h> 27#include <linux/input/ti_am335x_tsc.h>
28#include <linux/platform_data/ti_am335x_adc.h>
28 29
29static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg) 30static unsigned int tscadc_readl(struct ti_tscadc_dev *tsadc, unsigned int reg)
30{ 31{
@@ -67,14 +68,23 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev)
67 int irq; 68 int irq;
68 int err, ctrl; 69 int err, ctrl;
69 int clk_value, clock_rate; 70 int clk_value, clock_rate;
70 int tsc_wires; 71 int tsc_wires, adc_channels = 0, total_channels;
71 72
72 if (!pdata) { 73 if (!pdata) {
73 dev_err(&pdev->dev, "Could not find platform data\n"); 74 dev_err(&pdev->dev, "Could not find platform data\n");
74 return -EINVAL; 75 return -EINVAL;
75 } 76 }
76 77
78 if (pdata->adc_init)
79 adc_channels = pdata->adc_init->adc_channels;
80
77 tsc_wires = pdata->tsc_init->wires; 81 tsc_wires = pdata->tsc_init->wires;
82 total_channels = tsc_wires + adc_channels;
83
84 if (total_channels > 8) {
85 dev_err(&pdev->dev, "Number of i/p channels more than 8\n");
86 return -EINVAL;
87 }
78 88
79 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 89 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
80 if (!res) { 90 if (!res) {
@@ -172,6 +182,12 @@ static int __devinit ti_tscadc_probe(struct platform_device *pdev)
172 cell->platform_data = tscadc; 182 cell->platform_data = tscadc;
173 cell->pdata_size = sizeof(*tscadc); 183 cell->pdata_size = sizeof(*tscadc);
174 184
185 /* ADC Cell */
186 cell = &tscadc->cells[ADC_CELL];
187 cell->name = "tiadc";
188 cell->platform_data = tscadc;
189 cell->pdata_size = sizeof(*tscadc);
190
175 err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells, 191 err = mfd_add_devices(&pdev->dev, pdev->id, tscadc->cells,
176 TSCADC_CELLS, NULL, 0, NULL); 192 TSCADC_CELLS, NULL, 0, NULL);
177 if (err < 0) 193 if (err < 0)
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index fc18b2ef753f..c79ad5d2f271 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -120,15 +120,19 @@
120 120
121#define ADC_CLK 3000000 121#define ADC_CLK 3000000
122#define MAX_CLK_DIV 7 122#define MAX_CLK_DIV 7
123#define TOTAL_STEPS 16
124#define TOTAL_CHANNELS 8
123 125
124#define TSCADC_CELLS 1 126#define TSCADC_CELLS 2
125 127
126enum tscadc_cells { 128enum tscadc_cells {
127 TSC_CELL, 129 TSC_CELL,
130 ADC_CELL,
128}; 131};
129 132
130struct mfd_tscadc_board { 133struct mfd_tscadc_board {
131 struct tsc_data *tsc_init; 134 struct tsc_data *tsc_init;
135 struct adc_data *adc_init;
132}; 136};
133 137
134struct ti_tscadc_dev { 138struct ti_tscadc_dev {
@@ -140,6 +144,9 @@ struct ti_tscadc_dev {
140 144
141 /* tsc device */ 145 /* tsc device */
142 struct titsc *tsc; 146 struct titsc *tsc;
147
148 /* adc device */
149 struct adc_device *adc;
143}; 150};
144 151
145#endif 152#endif
diff --git a/include/linux/platform_data/ti_am335x_adc.h b/include/linux/platform_data/ti_am335x_adc.h
new file mode 100644
index 000000000000..e41d5834cb84
--- /dev/null
+++ b/include/linux/platform_data/ti_am335x_adc.h
@@ -0,0 +1,14 @@
1#ifndef __LINUX_TI_AM335X_ADC_H
2#define __LINUX_TI_AM335X_ADC_H
3
4/**
5 * struct adc_data ADC Input information
6 * @adc_channels: Number of analog inputs
7 * available for ADC.
8 */
9
10struct adc_data {
11 unsigned int adc_channels;
12};
13
14#endif