diff options
Diffstat (limited to 'drivers/staging/iio/adc/ad7887_core.c')
-rw-r--r-- | drivers/staging/iio/adc/ad7887_core.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/drivers/staging/iio/adc/ad7887_core.c b/drivers/staging/iio/adc/ad7887_core.c new file mode 100644 index 00000000000..3d9121e5c37 --- /dev/null +++ b/drivers/staging/iio/adc/ad7887_core.c | |||
@@ -0,0 +1,266 @@ | |||
1 | /* | ||
2 | * AD7887 SPI ADC driver | ||
3 | * | ||
4 | * Copyright 2010-2011 Analog Devices Inc. | ||
5 | * | ||
6 | * Licensed under the GPL-2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/device.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/sysfs.h> | ||
13 | #include <linux/spi/spi.h> | ||
14 | #include <linux/regulator/consumer.h> | ||
15 | #include <linux/err.h> | ||
16 | |||
17 | #include "../iio.h" | ||
18 | #include "../sysfs.h" | ||
19 | #include "../ring_generic.h" | ||
20 | #include "adc.h" | ||
21 | |||
22 | #include "ad7887.h" | ||
23 | |||
24 | static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch) | ||
25 | { | ||
26 | int ret = spi_sync(st->spi, &st->msg[ch]); | ||
27 | if (ret) | ||
28 | return ret; | ||
29 | |||
30 | return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1]; | ||
31 | } | ||
32 | |||
33 | static int ad7887_read_raw(struct iio_dev *dev_info, | ||
34 | struct iio_chan_spec const *chan, | ||
35 | int *val, | ||
36 | int *val2, | ||
37 | long m) | ||
38 | { | ||
39 | int ret; | ||
40 | struct ad7887_state *st = iio_priv(dev_info); | ||
41 | unsigned int scale_uv; | ||
42 | |||
43 | switch (m) { | ||
44 | case 0: | ||
45 | mutex_lock(&dev_info->mlock); | ||
46 | if (iio_ring_enabled(dev_info)) | ||
47 | ret = ad7887_scan_from_ring(st, 1 << chan->address); | ||
48 | else | ||
49 | ret = ad7887_scan_direct(st, chan->address); | ||
50 | mutex_unlock(&dev_info->mlock); | ||
51 | |||
52 | if (ret < 0) | ||
53 | return ret; | ||
54 | *val = (ret >> st->chip_info->channel[0].scan_type.shift) & | ||
55 | RES_MASK(st->chip_info->channel[0].scan_type.realbits); | ||
56 | return IIO_VAL_INT; | ||
57 | case (1 << IIO_CHAN_INFO_SCALE_SHARED): | ||
58 | scale_uv = (st->int_vref_mv * 1000) | ||
59 | >> st->chip_info->channel[0].scan_type.realbits; | ||
60 | *val = scale_uv/1000; | ||
61 | *val2 = (scale_uv%1000)*1000; | ||
62 | return IIO_VAL_INT_PLUS_MICRO; | ||
63 | } | ||
64 | return -EINVAL; | ||
65 | } | ||
66 | |||
67 | |||
68 | static const struct ad7887_chip_info ad7887_chip_info_tbl[] = { | ||
69 | /* | ||
70 | * More devices added in future | ||
71 | */ | ||
72 | [ID_AD7887] = { | ||
73 | .channel[0] = IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 1, 0, | ||
74 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | ||
75 | 1, 1, IIO_ST('u', 12, 16, 0), 0), | ||
76 | |||
77 | .channel[1] = IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 0, 0, | ||
78 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | ||
79 | 0, 0, IIO_ST('u', 12, 16, 0), 0), | ||
80 | |||
81 | .channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2), | ||
82 | .int_vref_mv = 2500, | ||
83 | }, | ||
84 | }; | ||
85 | |||
86 | static const struct iio_info ad7887_info = { | ||
87 | .read_raw = &ad7887_read_raw, | ||
88 | .driver_module = THIS_MODULE, | ||
89 | }; | ||
90 | |||
91 | static int __devinit ad7887_probe(struct spi_device *spi) | ||
92 | { | ||
93 | struct ad7887_platform_data *pdata = spi->dev.platform_data; | ||
94 | struct ad7887_state *st; | ||
95 | int ret, voltage_uv = 0, regdone = 0; | ||
96 | struct iio_dev *indio_dev = iio_allocate_device(sizeof(*st)); | ||
97 | |||
98 | if (indio_dev == NULL) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | st = iio_priv(indio_dev); | ||
102 | |||
103 | st->reg = regulator_get(&spi->dev, "vcc"); | ||
104 | if (!IS_ERR(st->reg)) { | ||
105 | ret = regulator_enable(st->reg); | ||
106 | if (ret) | ||
107 | goto error_put_reg; | ||
108 | |||
109 | voltage_uv = regulator_get_voltage(st->reg); | ||
110 | } | ||
111 | |||
112 | st->chip_info = | ||
113 | &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data]; | ||
114 | |||
115 | spi_set_drvdata(spi, indio_dev); | ||
116 | st->spi = spi; | ||
117 | |||
118 | /* Estabilish that the iio_dev is a child of the spi device */ | ||
119 | indio_dev->dev.parent = &spi->dev; | ||
120 | indio_dev->name = spi_get_device_id(spi)->name; | ||
121 | indio_dev->info = &ad7887_info; | ||
122 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
123 | |||
124 | /* Setup default message */ | ||
125 | |||
126 | st->tx_cmd_buf[0] = AD7887_CH_AIN0 | AD7887_PM_MODE4 | | ||
127 | ((pdata && pdata->use_onchip_ref) ? | ||
128 | 0 : AD7887_REF_DIS); | ||
129 | |||
130 | st->xfer[0].rx_buf = &st->data[0]; | ||
131 | st->xfer[0].tx_buf = &st->tx_cmd_buf[0]; | ||
132 | st->xfer[0].len = 2; | ||
133 | |||
134 | spi_message_init(&st->msg[AD7887_CH0]); | ||
135 | spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]); | ||
136 | |||
137 | if (pdata && pdata->en_dual) { | ||
138 | st->tx_cmd_buf[0] |= AD7887_DUAL | AD7887_REF_DIS; | ||
139 | |||
140 | st->tx_cmd_buf[2] = AD7887_CH_AIN1 | AD7887_DUAL | | ||
141 | AD7887_REF_DIS | AD7887_PM_MODE4; | ||
142 | st->tx_cmd_buf[4] = AD7887_CH_AIN0 | AD7887_DUAL | | ||
143 | AD7887_REF_DIS | AD7887_PM_MODE4; | ||
144 | st->tx_cmd_buf[6] = AD7887_CH_AIN1 | AD7887_DUAL | | ||
145 | AD7887_REF_DIS | AD7887_PM_MODE4; | ||
146 | |||
147 | st->xfer[1].rx_buf = &st->data[0]; | ||
148 | st->xfer[1].tx_buf = &st->tx_cmd_buf[2]; | ||
149 | st->xfer[1].len = 2; | ||
150 | |||
151 | st->xfer[2].rx_buf = &st->data[2]; | ||
152 | st->xfer[2].tx_buf = &st->tx_cmd_buf[4]; | ||
153 | st->xfer[2].len = 2; | ||
154 | |||
155 | spi_message_init(&st->msg[AD7887_CH0_CH1]); | ||
156 | spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]); | ||
157 | spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]); | ||
158 | |||
159 | st->xfer[3].rx_buf = &st->data[0]; | ||
160 | st->xfer[3].tx_buf = &st->tx_cmd_buf[6]; | ||
161 | st->xfer[3].len = 2; | ||
162 | |||
163 | spi_message_init(&st->msg[AD7887_CH1]); | ||
164 | spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]); | ||
165 | |||
166 | if (pdata && pdata->vref_mv) | ||
167 | st->int_vref_mv = pdata->vref_mv; | ||
168 | else if (voltage_uv) | ||
169 | st->int_vref_mv = voltage_uv / 1000; | ||
170 | else | ||
171 | dev_warn(&spi->dev, "reference voltage unspecified\n"); | ||
172 | |||
173 | indio_dev->channels = st->chip_info->channel; | ||
174 | indio_dev->num_channels = 3; | ||
175 | } else { | ||
176 | if (pdata && pdata->vref_mv) | ||
177 | st->int_vref_mv = pdata->vref_mv; | ||
178 | else if (pdata && pdata->use_onchip_ref) | ||
179 | st->int_vref_mv = st->chip_info->int_vref_mv; | ||
180 | else | ||
181 | dev_warn(&spi->dev, "reference voltage unspecified\n"); | ||
182 | |||
183 | indio_dev->channels = &st->chip_info->channel[1]; | ||
184 | indio_dev->num_channels = 2; | ||
185 | } | ||
186 | |||
187 | ret = ad7887_register_ring_funcs_and_init(indio_dev); | ||
188 | if (ret) | ||
189 | goto error_disable_reg; | ||
190 | |||
191 | ret = iio_device_register(indio_dev); | ||
192 | if (ret) | ||
193 | goto error_disable_reg; | ||
194 | regdone = 1; | ||
195 | |||
196 | ret = iio_ring_buffer_register_ex(indio_dev->ring, 0, | ||
197 | indio_dev->channels, | ||
198 | indio_dev->num_channels); | ||
199 | if (ret) | ||
200 | goto error_cleanup_ring; | ||
201 | return 0; | ||
202 | |||
203 | error_cleanup_ring: | ||
204 | ad7887_ring_cleanup(indio_dev); | ||
205 | error_disable_reg: | ||
206 | if (!IS_ERR(st->reg)) | ||
207 | regulator_disable(st->reg); | ||
208 | error_put_reg: | ||
209 | if (!IS_ERR(st->reg)) | ||
210 | regulator_put(st->reg); | ||
211 | if (regdone) | ||
212 | iio_device_unregister(indio_dev); | ||
213 | else | ||
214 | iio_free_device(indio_dev); | ||
215 | |||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | static int ad7887_remove(struct spi_device *spi) | ||
220 | { | ||
221 | struct iio_dev *indio_dev = spi_get_drvdata(spi); | ||
222 | struct ad7887_state *st = iio_priv(indio_dev); | ||
223 | |||
224 | iio_ring_buffer_unregister(indio_dev->ring); | ||
225 | ad7887_ring_cleanup(indio_dev); | ||
226 | if (!IS_ERR(st->reg)) { | ||
227 | regulator_disable(st->reg); | ||
228 | regulator_put(st->reg); | ||
229 | } | ||
230 | iio_device_unregister(indio_dev); | ||
231 | |||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static const struct spi_device_id ad7887_id[] = { | ||
236 | {"ad7887", ID_AD7887}, | ||
237 | {} | ||
238 | }; | ||
239 | |||
240 | static struct spi_driver ad7887_driver = { | ||
241 | .driver = { | ||
242 | .name = "ad7887", | ||
243 | .bus = &spi_bus_type, | ||
244 | .owner = THIS_MODULE, | ||
245 | }, | ||
246 | .probe = ad7887_probe, | ||
247 | .remove = __devexit_p(ad7887_remove), | ||
248 | .id_table = ad7887_id, | ||
249 | }; | ||
250 | |||
251 | static int __init ad7887_init(void) | ||
252 | { | ||
253 | return spi_register_driver(&ad7887_driver); | ||
254 | } | ||
255 | module_init(ad7887_init); | ||
256 | |||
257 | static void __exit ad7887_exit(void) | ||
258 | { | ||
259 | spi_unregister_driver(&ad7887_driver); | ||
260 | } | ||
261 | module_exit(ad7887_exit); | ||
262 | |||
263 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
264 | MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); | ||
265 | MODULE_LICENSE("GPL v2"); | ||
266 | MODULE_ALIAS("spi:ad7887"); | ||