Commit | Line | Data |
---|---|---|
7c31b984 MH |
1 | /* |
2 | * AD7298 SPI ADC driver | |
3 | * | |
4 | * Copyright 2011 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2. | |
7 | */ | |
8 | ||
7c31b984 MH |
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 | #include <linux/delay.h> | |
99c97852 | 17 | #include <linux/module.h> |
7c31b984 MH |
18 | |
19 | #include "../iio.h" | |
20 | #include "../sysfs.h" | |
21 | #include "../ring_generic.h" | |
7c31b984 MH |
22 | |
23 | #include "ad7298.h" | |
24 | ||
01a99e18 MH |
25 | static struct iio_chan_spec ad7298_channels[] = { |
26 | IIO_CHAN(IIO_TEMP, 0, 1, 0, NULL, 0, 0, | |
27 | (1 << IIO_CHAN_INFO_SCALE_SEPARATE), | |
28 | 9, AD7298_CH_TEMP, IIO_ST('s', 32, 32, 0), 0), | |
29 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 0, 0, | |
30 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
31 | 0, 0, IIO_ST('u', 12, 16, 0), 0), | |
32 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 1, 0, | |
33 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
34 | 1, 1, IIO_ST('u', 12, 16, 0), 0), | |
35 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 2, 0, | |
36 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
37 | 2, 2, IIO_ST('u', 12, 16, 0), 0), | |
38 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 3, 0, | |
39 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
40 | 3, 3, IIO_ST('u', 12, 16, 0), 0), | |
41 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 4, 0, | |
42 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
43 | 4, 4, IIO_ST('u', 12, 16, 0), 0), | |
44 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 5, 0, | |
45 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
46 | 5, 5, IIO_ST('u', 12, 16, 0), 0), | |
47 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 6, 0, | |
48 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
49 | 6, 6, IIO_ST('u', 12, 16, 0), 0), | |
50 | IIO_CHAN(IIO_IN, 0, 1, 0, NULL, 7, 0, | |
51 | (1 << IIO_CHAN_INFO_SCALE_SHARED), | |
52 | 7, 7, IIO_ST('u', 12, 16, 0), 0), | |
53 | IIO_CHAN_SOFT_TIMESTAMP(8), | |
54 | }; | |
55 | ||
7c31b984 MH |
56 | static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch) |
57 | { | |
58 | int ret; | |
59 | st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref | | |
60 | (AD7298_CH(0) >> ch)); | |
61 | ||
62 | ret = spi_sync(st->spi, &st->scan_single_msg); | |
63 | if (ret) | |
64 | return ret; | |
65 | ||
66 | return be16_to_cpu(st->rx_buf[0]); | |
67 | } | |
68 | ||
01a99e18 | 69 | static int ad7298_scan_temp(struct ad7298_state *st, int *val) |
7c31b984 | 70 | { |
01a99e18 | 71 | int tmp, ret; |
7c31b984 | 72 | |
01a99e18 MH |
73 | tmp = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE | |
74 | AD7298_TAVG | st->ext_ref); | |
7c31b984 | 75 | |
01a99e18 MH |
76 | ret = spi_write(st->spi, (u8 *)&tmp, 2); |
77 | if (ret) | |
7c31b984 MH |
78 | return ret; |
79 | ||
01a99e18 | 80 | tmp = 0; |
7c31b984 | 81 | |
01a99e18 MH |
82 | ret = spi_write(st->spi, (u8 *)&tmp, 2); |
83 | if (ret) | |
84 | return ret; | |
7c31b984 | 85 | |
7c31b984 | 86 | usleep_range(101, 1000); /* sleep > 100us */ |
01a99e18 MH |
87 | |
88 | ret = spi_read(st->spi, (u8 *)&tmp, 2); | |
89 | if (ret) | |
90 | return ret; | |
7c31b984 MH |
91 | |
92 | tmp = be16_to_cpu(tmp) & RES_MASK(AD7298_BITS); | |
93 | ||
94 | /* | |
95 | * One LSB of the ADC corresponds to 0.25 deg C. | |
96 | * The temperature reading is in 12-bit twos complement format | |
97 | */ | |
98 | ||
99 | if (tmp & (1 << (AD7298_BITS - 1))) { | |
100 | tmp = (4096 - tmp) * 250; | |
101 | tmp -= (2 * tmp); | |
102 | ||
103 | } else { | |
104 | tmp *= 250; /* temperature in milli degrees Celsius */ | |
105 | } | |
106 | ||
01a99e18 | 107 | *val = tmp; |
7c31b984 | 108 | |
01a99e18 MH |
109 | return 0; |
110 | } | |
7c31b984 | 111 | |
01a99e18 MH |
112 | static int ad7298_read_raw(struct iio_dev *dev_info, |
113 | struct iio_chan_spec const *chan, | |
114 | int *val, | |
115 | int *val2, | |
116 | long m) | |
7c31b984 | 117 | { |
01a99e18 | 118 | int ret; |
cc4a48e4 | 119 | struct ad7298_state *st = iio_priv(dev_info); |
01a99e18 MH |
120 | unsigned int scale_uv; |
121 | ||
122 | switch (m) { | |
123 | case 0: | |
124 | mutex_lock(&dev_info->mlock); | |
125 | if (iio_ring_enabled(dev_info)) { | |
126 | if (chan->address == AD7298_CH_TEMP) | |
127 | ret = -ENODEV; | |
128 | else | |
cc4a48e4 MH |
129 | ret = ad7298_scan_from_ring(dev_info, |
130 | chan->address); | |
01a99e18 MH |
131 | } else { |
132 | if (chan->address == AD7298_CH_TEMP) | |
133 | ret = ad7298_scan_temp(st, val); | |
134 | else | |
135 | ret = ad7298_scan_direct(st, chan->address); | |
136 | } | |
137 | mutex_unlock(&dev_info->mlock); | |
138 | ||
139 | if (ret < 0) | |
140 | return ret; | |
141 | ||
142 | if (chan->address != AD7298_CH_TEMP) | |
143 | *val = ret & RES_MASK(AD7298_BITS); | |
144 | ||
145 | return IIO_VAL_INT; | |
146 | case (1 << IIO_CHAN_INFO_SCALE_SHARED): | |
147 | scale_uv = (st->int_vref_mv * 1000) >> AD7298_BITS; | |
148 | *val = scale_uv / 1000; | |
149 | *val2 = (scale_uv % 1000) * 1000; | |
150 | return IIO_VAL_INT_PLUS_MICRO; | |
151 | case (1 << IIO_CHAN_INFO_SCALE_SEPARATE): | |
152 | *val = 1; | |
153 | *val2 = 0; | |
154 | return IIO_VAL_INT_PLUS_MICRO; | |
155 | } | |
156 | return -EINVAL; | |
7c31b984 | 157 | } |
7c31b984 | 158 | |
6fe8135f JC |
159 | static const struct iio_info ad7298_info = { |
160 | .read_raw = &ad7298_read_raw, | |
161 | .driver_module = THIS_MODULE, | |
162 | }; | |
163 | ||
7c31b984 MH |
164 | static int __devinit ad7298_probe(struct spi_device *spi) |
165 | { | |
166 | struct ad7298_platform_data *pdata = spi->dev.platform_data; | |
167 | struct ad7298_state *st; | |
cc4a48e4 MH |
168 | int ret, regdone = 0; |
169 | struct iio_dev *indio_dev = iio_allocate_device(sizeof(*st)); | |
7c31b984 | 170 | |
cc4a48e4 MH |
171 | if (indio_dev == NULL) |
172 | return -ENOMEM; | |
173 | ||
174 | st = iio_priv(indio_dev); | |
7c31b984 MH |
175 | |
176 | st->reg = regulator_get(&spi->dev, "vcc"); | |
177 | if (!IS_ERR(st->reg)) { | |
178 | ret = regulator_enable(st->reg); | |
179 | if (ret) | |
180 | goto error_put_reg; | |
181 | } | |
182 | ||
cc4a48e4 | 183 | spi_set_drvdata(spi, indio_dev); |
7c31b984 | 184 | |
7c31b984 MH |
185 | st->spi = spi; |
186 | ||
cc4a48e4 MH |
187 | indio_dev->name = spi_get_device_id(spi)->name; |
188 | indio_dev->dev.parent = &spi->dev; | |
cc4a48e4 MH |
189 | indio_dev->modes = INDIO_DIRECT_MODE; |
190 | indio_dev->channels = ad7298_channels; | |
191 | indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); | |
6fe8135f | 192 | indio_dev->info = &ad7298_info; |
7c31b984 MH |
193 | |
194 | /* Setup default message */ | |
195 | ||
196 | st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; | |
197 | st->scan_single_xfer[0].len = 2; | |
198 | st->scan_single_xfer[0].cs_change = 1; | |
199 | st->scan_single_xfer[1].tx_buf = &st->tx_buf[1]; | |
200 | st->scan_single_xfer[1].len = 2; | |
201 | st->scan_single_xfer[1].cs_change = 1; | |
202 | st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; | |
203 | st->scan_single_xfer[2].len = 2; | |
204 | ||
205 | spi_message_init(&st->scan_single_msg); | |
206 | spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); | |
207 | spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); | |
208 | spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg); | |
209 | ||
210 | if (pdata && pdata->vref_mv) { | |
211 | st->int_vref_mv = pdata->vref_mv; | |
212 | st->ext_ref = AD7298_EXTREF; | |
213 | } else { | |
214 | st->int_vref_mv = AD7298_INTREF_mV; | |
215 | } | |
216 | ||
cc4a48e4 | 217 | ret = ad7298_register_ring_funcs_and_init(indio_dev); |
7c31b984 | 218 | if (ret) |
cc4a48e4 | 219 | goto error_disable_reg; |
7c31b984 | 220 | |
cc4a48e4 | 221 | ret = iio_device_register(indio_dev); |
7c31b984 | 222 | if (ret) |
cc4a48e4 MH |
223 | goto error_disable_reg; |
224 | regdone = 1; | |
7c31b984 | 225 | |
1aa04278 | 226 | ret = iio_ring_buffer_register_ex(indio_dev, 0, |
01a99e18 MH |
227 | &ad7298_channels[1], /* skip temp0 */ |
228 | ARRAY_SIZE(ad7298_channels) - 1); | |
7c31b984 MH |
229 | if (ret) |
230 | goto error_cleanup_ring; | |
cc4a48e4 | 231 | |
7c31b984 MH |
232 | return 0; |
233 | ||
234 | error_cleanup_ring: | |
cc4a48e4 | 235 | ad7298_ring_cleanup(indio_dev); |
7c31b984 MH |
236 | error_disable_reg: |
237 | if (!IS_ERR(st->reg)) | |
238 | regulator_disable(st->reg); | |
239 | error_put_reg: | |
240 | if (!IS_ERR(st->reg)) | |
241 | regulator_put(st->reg); | |
cc4a48e4 MH |
242 | |
243 | if (regdone) | |
244 | iio_device_unregister(indio_dev); | |
245 | else | |
246 | iio_free_device(indio_dev); | |
247 | ||
7c31b984 MH |
248 | return ret; |
249 | } | |
250 | ||
251 | static int __devexit ad7298_remove(struct spi_device *spi) | |
252 | { | |
cc4a48e4 MH |
253 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
254 | struct ad7298_state *st = iio_priv(indio_dev); | |
7c31b984 | 255 | |
1aa04278 | 256 | iio_ring_buffer_unregister(indio_dev); |
7c31b984 MH |
257 | ad7298_ring_cleanup(indio_dev); |
258 | iio_device_unregister(indio_dev); | |
259 | if (!IS_ERR(st->reg)) { | |
260 | regulator_disable(st->reg); | |
261 | regulator_put(st->reg); | |
262 | } | |
cc4a48e4 MH |
263 | iio_device_unregister(indio_dev); |
264 | ||
7c31b984 MH |
265 | return 0; |
266 | } | |
267 | ||
268 | static const struct spi_device_id ad7298_id[] = { | |
269 | {"ad7298", 0}, | |
270 | {} | |
271 | }; | |
272 | ||
273 | static struct spi_driver ad7298_driver = { | |
274 | .driver = { | |
275 | .name = "ad7298", | |
276 | .bus = &spi_bus_type, | |
277 | .owner = THIS_MODULE, | |
278 | }, | |
279 | .probe = ad7298_probe, | |
280 | .remove = __devexit_p(ad7298_remove), | |
281 | .id_table = ad7298_id, | |
282 | }; | |
283 | ||
284 | static int __init ad7298_init(void) | |
285 | { | |
286 | return spi_register_driver(&ad7298_driver); | |
287 | } | |
288 | module_init(ad7298_init); | |
289 | ||
290 | static void __exit ad7298_exit(void) | |
291 | { | |
292 | spi_unregister_driver(&ad7298_driver); | |
293 | } | |
294 | module_exit(ad7298_exit); | |
295 | ||
296 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | |
297 | MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); | |
298 | MODULE_LICENSE("GPL v2"); | |
299 | MODULE_ALIAS("spi:ad7298"); |