1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* TI ADS124S0X chip family driver |
3 | * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ |
4 | */ |
5 | |
6 | #include <linux/err.h> |
7 | #include <linux/delay.h> |
8 | #include <linux/device.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/module.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/sysfs.h> |
14 | |
15 | #include <linux/gpio/consumer.h> |
16 | #include <linux/spi/spi.h> |
17 | |
18 | #include <linux/iio/iio.h> |
19 | #include <linux/iio/buffer.h> |
20 | #include <linux/iio/trigger_consumer.h> |
21 | #include <linux/iio/triggered_buffer.h> |
22 | #include <linux/iio/sysfs.h> |
23 | |
24 | #include <asm/unaligned.h> |
25 | |
26 | /* Commands */ |
27 | #define ADS124S08_CMD_NOP 0x00 |
28 | #define ADS124S08_CMD_WAKEUP 0x02 |
29 | #define ADS124S08_CMD_PWRDWN 0x04 |
30 | #define ADS124S08_CMD_RESET 0x06 |
31 | #define ADS124S08_CMD_START 0x08 |
32 | #define ADS124S08_CMD_STOP 0x0a |
33 | #define ADS124S08_CMD_SYOCAL 0x16 |
34 | #define ADS124S08_CMD_SYGCAL 0x17 |
35 | #define ADS124S08_CMD_SFOCAL 0x19 |
36 | #define ADS124S08_CMD_RDATA 0x12 |
37 | #define ADS124S08_CMD_RREG 0x20 |
38 | #define ADS124S08_CMD_WREG 0x40 |
39 | |
40 | /* Registers */ |
41 | #define ADS124S08_ID_REG 0x00 |
42 | #define ADS124S08_STATUS 0x01 |
43 | #define ADS124S08_INPUT_MUX 0x02 |
44 | #define ADS124S08_PGA 0x03 |
45 | #define ADS124S08_DATA_RATE 0x04 |
46 | #define ADS124S08_REF 0x05 |
47 | #define ADS124S08_IDACMAG 0x06 |
48 | #define ADS124S08_IDACMUX 0x07 |
49 | #define ADS124S08_VBIAS 0x08 |
50 | #define ADS124S08_SYS 0x09 |
51 | #define ADS124S08_OFCAL0 0x0a |
52 | #define ADS124S08_OFCAL1 0x0b |
53 | #define ADS124S08_OFCAL2 0x0c |
54 | #define ADS124S08_FSCAL0 0x0d |
55 | #define ADS124S08_FSCAL1 0x0e |
56 | #define ADS124S08_FSCAL2 0x0f |
57 | #define ADS124S08_GPIODAT 0x10 |
58 | #define ADS124S08_GPIOCON 0x11 |
59 | |
60 | /* ADS124S0x common channels */ |
61 | #define ADS124S08_AIN0 0x00 |
62 | #define ADS124S08_AIN1 0x01 |
63 | #define ADS124S08_AIN2 0x02 |
64 | #define ADS124S08_AIN3 0x03 |
65 | #define ADS124S08_AIN4 0x04 |
66 | #define ADS124S08_AIN5 0x05 |
67 | #define ADS124S08_AINCOM 0x0c |
68 | /* ADS124S08 only channels */ |
69 | #define ADS124S08_AIN6 0x06 |
70 | #define ADS124S08_AIN7 0x07 |
71 | #define ADS124S08_AIN8 0x08 |
72 | #define ADS124S08_AIN9 0x09 |
73 | #define ADS124S08_AIN10 0x0a |
74 | #define ADS124S08_AIN11 0x0b |
75 | #define ADS124S08_MAX_CHANNELS 12 |
76 | |
77 | #define ADS124S08_POS_MUX_SHIFT 0x04 |
78 | #define ADS124S08_INT_REF 0x09 |
79 | |
80 | #define ADS124S08_START_REG_MASK 0x1f |
81 | #define ADS124S08_NUM_BYTES_MASK 0x1f |
82 | |
83 | #define ADS124S08_START_CONV 0x01 |
84 | #define ADS124S08_STOP_CONV 0x00 |
85 | |
86 | enum ads124s_id { |
87 | ADS124S08_ID, |
88 | ADS124S06_ID, |
89 | }; |
90 | |
91 | struct ads124s_chip_info { |
92 | const struct iio_chan_spec *channels; |
93 | unsigned int num_channels; |
94 | }; |
95 | |
96 | struct ads124s_private { |
97 | const struct ads124s_chip_info *chip_info; |
98 | struct gpio_desc *reset_gpio; |
99 | struct spi_device *spi; |
100 | struct mutex lock; |
101 | /* |
102 | * Used to correctly align data. |
103 | * Ensure timestamp is naturally aligned. |
104 | * Note that the full buffer length may not be needed if not |
105 | * all channels are enabled, as long as the alignment of the |
106 | * timestamp is maintained. |
107 | */ |
108 | u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u32)] __aligned(8); |
109 | u8 data[5] __aligned(IIO_DMA_MINALIGN); |
110 | }; |
111 | |
112 | #define ADS124S08_CHAN(index) \ |
113 | { \ |
114 | .type = IIO_VOLTAGE, \ |
115 | .indexed = 1, \ |
116 | .channel = index, \ |
117 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
118 | .scan_index = index, \ |
119 | .scan_type = { \ |
120 | .sign = 'u', \ |
121 | .realbits = 32, \ |
122 | .storagebits = 32, \ |
123 | }, \ |
124 | } |
125 | |
126 | static const struct iio_chan_spec ads124s06_channels[] = { |
127 | ADS124S08_CHAN(0), |
128 | ADS124S08_CHAN(1), |
129 | ADS124S08_CHAN(2), |
130 | ADS124S08_CHAN(3), |
131 | ADS124S08_CHAN(4), |
132 | ADS124S08_CHAN(5), |
133 | }; |
134 | |
135 | static const struct iio_chan_spec ads124s08_channels[] = { |
136 | ADS124S08_CHAN(0), |
137 | ADS124S08_CHAN(1), |
138 | ADS124S08_CHAN(2), |
139 | ADS124S08_CHAN(3), |
140 | ADS124S08_CHAN(4), |
141 | ADS124S08_CHAN(5), |
142 | ADS124S08_CHAN(6), |
143 | ADS124S08_CHAN(7), |
144 | ADS124S08_CHAN(8), |
145 | ADS124S08_CHAN(9), |
146 | ADS124S08_CHAN(10), |
147 | ADS124S08_CHAN(11), |
148 | }; |
149 | |
150 | static const struct ads124s_chip_info ads124s_chip_info_tbl[] = { |
151 | [ADS124S08_ID] = { |
152 | .channels = ads124s08_channels, |
153 | .num_channels = ARRAY_SIZE(ads124s08_channels), |
154 | }, |
155 | [ADS124S06_ID] = { |
156 | .channels = ads124s06_channels, |
157 | .num_channels = ARRAY_SIZE(ads124s06_channels), |
158 | }, |
159 | }; |
160 | |
161 | static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command) |
162 | { |
163 | struct ads124s_private *priv = iio_priv(indio_dev); |
164 | |
165 | priv->data[0] = command; |
166 | |
167 | return spi_write(spi: priv->spi, buf: &priv->data[0], len: 1); |
168 | } |
169 | |
170 | static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data) |
171 | { |
172 | struct ads124s_private *priv = iio_priv(indio_dev); |
173 | |
174 | priv->data[0] = ADS124S08_CMD_WREG | reg; |
175 | priv->data[1] = 0x0; |
176 | priv->data[2] = data; |
177 | |
178 | return spi_write(spi: priv->spi, buf: &priv->data[0], len: 3); |
179 | } |
180 | |
181 | static int ads124s_reset(struct iio_dev *indio_dev) |
182 | { |
183 | struct ads124s_private *priv = iio_priv(indio_dev); |
184 | |
185 | if (priv->reset_gpio) { |
186 | gpiod_set_value(desc: priv->reset_gpio, value: 0); |
187 | udelay(200); |
188 | gpiod_set_value(desc: priv->reset_gpio, value: 1); |
189 | } else { |
190 | return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET); |
191 | } |
192 | |
193 | return 0; |
194 | }; |
195 | |
196 | static int ads124s_read(struct iio_dev *indio_dev) |
197 | { |
198 | struct ads124s_private *priv = iio_priv(indio_dev); |
199 | int ret; |
200 | struct spi_transfer t[] = { |
201 | { |
202 | .tx_buf = &priv->data[0], |
203 | .len = 4, |
204 | .cs_change = 1, |
205 | }, { |
206 | .tx_buf = &priv->data[1], |
207 | .rx_buf = &priv->data[1], |
208 | .len = 4, |
209 | }, |
210 | }; |
211 | |
212 | priv->data[0] = ADS124S08_CMD_RDATA; |
213 | memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data) - 1); |
214 | |
215 | ret = spi_sync_transfer(spi: priv->spi, xfers: t, ARRAY_SIZE(t)); |
216 | if (ret < 0) |
217 | return ret; |
218 | |
219 | return get_unaligned_be24(p: &priv->data[2]); |
220 | } |
221 | |
222 | static int ads124s_read_raw(struct iio_dev *indio_dev, |
223 | struct iio_chan_spec const *chan, |
224 | int *val, int *val2, long m) |
225 | { |
226 | struct ads124s_private *priv = iio_priv(indio_dev); |
227 | int ret; |
228 | |
229 | mutex_lock(&priv->lock); |
230 | switch (m) { |
231 | case IIO_CHAN_INFO_RAW: |
232 | ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, |
233 | data: chan->channel); |
234 | if (ret) { |
235 | dev_err(&priv->spi->dev, "Set ADC CH failed\n" ); |
236 | goto out; |
237 | } |
238 | |
239 | ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); |
240 | if (ret) { |
241 | dev_err(&priv->spi->dev, "Start conversions failed\n" ); |
242 | goto out; |
243 | } |
244 | |
245 | ret = ads124s_read(indio_dev); |
246 | if (ret < 0) { |
247 | dev_err(&priv->spi->dev, "Read ADC failed\n" ); |
248 | goto out; |
249 | } |
250 | |
251 | *val = ret; |
252 | |
253 | ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); |
254 | if (ret) { |
255 | dev_err(&priv->spi->dev, "Stop conversions failed\n" ); |
256 | goto out; |
257 | } |
258 | |
259 | ret = IIO_VAL_INT; |
260 | break; |
261 | default: |
262 | ret = -EINVAL; |
263 | break; |
264 | } |
265 | out: |
266 | mutex_unlock(lock: &priv->lock); |
267 | return ret; |
268 | } |
269 | |
270 | static const struct iio_info ads124s_info = { |
271 | .read_raw = &ads124s_read_raw, |
272 | }; |
273 | |
274 | static irqreturn_t ads124s_trigger_handler(int irq, void *p) |
275 | { |
276 | struct iio_poll_func *pf = p; |
277 | struct iio_dev *indio_dev = pf->indio_dev; |
278 | struct ads124s_private *priv = iio_priv(indio_dev); |
279 | int scan_index, j = 0; |
280 | int ret; |
281 | |
282 | for_each_set_bit(scan_index, indio_dev->active_scan_mask, |
283 | indio_dev->masklength) { |
284 | ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, |
285 | data: scan_index); |
286 | if (ret) |
287 | dev_err(&priv->spi->dev, "Set ADC CH failed\n" ); |
288 | |
289 | ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); |
290 | if (ret) |
291 | dev_err(&priv->spi->dev, "Start ADC conversions failed\n" ); |
292 | |
293 | priv->buffer[j] = ads124s_read(indio_dev); |
294 | ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); |
295 | if (ret) |
296 | dev_err(&priv->spi->dev, "Stop ADC conversions failed\n" ); |
297 | |
298 | j++; |
299 | } |
300 | |
301 | iio_push_to_buffers_with_timestamp(indio_dev, data: priv->buffer, |
302 | timestamp: pf->timestamp); |
303 | |
304 | iio_trigger_notify_done(trig: indio_dev->trig); |
305 | |
306 | return IRQ_HANDLED; |
307 | } |
308 | |
309 | static int ads124s_probe(struct spi_device *spi) |
310 | { |
311 | struct ads124s_private *ads124s_priv; |
312 | struct iio_dev *indio_dev; |
313 | const struct spi_device_id *spi_id = spi_get_device_id(sdev: spi); |
314 | int ret; |
315 | |
316 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*ads124s_priv)); |
317 | if (indio_dev == NULL) |
318 | return -ENOMEM; |
319 | |
320 | ads124s_priv = iio_priv(indio_dev); |
321 | |
322 | ads124s_priv->reset_gpio = devm_gpiod_get_optional(dev: &spi->dev, |
323 | con_id: "reset" , flags: GPIOD_OUT_LOW); |
324 | if (IS_ERR(ptr: ads124s_priv->reset_gpio)) |
325 | dev_info(&spi->dev, "Reset GPIO not defined\n" ); |
326 | |
327 | ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; |
328 | |
329 | ads124s_priv->spi = spi; |
330 | |
331 | indio_dev->name = spi_id->name; |
332 | indio_dev->modes = INDIO_DIRECT_MODE; |
333 | indio_dev->channels = ads124s_priv->chip_info->channels; |
334 | indio_dev->num_channels = ads124s_priv->chip_info->num_channels; |
335 | indio_dev->info = &ads124s_info; |
336 | |
337 | mutex_init(&ads124s_priv->lock); |
338 | |
339 | ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, |
340 | ads124s_trigger_handler, NULL); |
341 | if (ret) { |
342 | dev_err(&spi->dev, "iio triggered buffer setup failed\n" ); |
343 | return ret; |
344 | } |
345 | |
346 | ads124s_reset(indio_dev); |
347 | |
348 | return devm_iio_device_register(&spi->dev, indio_dev); |
349 | } |
350 | |
351 | static const struct spi_device_id ads124s_id[] = { |
352 | { "ads124s06" , ADS124S06_ID }, |
353 | { "ads124s08" , ADS124S08_ID }, |
354 | { } |
355 | }; |
356 | MODULE_DEVICE_TABLE(spi, ads124s_id); |
357 | |
358 | static const struct of_device_id ads124s_of_table[] = { |
359 | { .compatible = "ti,ads124s06" }, |
360 | { .compatible = "ti,ads124s08" }, |
361 | { }, |
362 | }; |
363 | MODULE_DEVICE_TABLE(of, ads124s_of_table); |
364 | |
365 | static struct spi_driver ads124s_driver = { |
366 | .driver = { |
367 | .name = "ads124s08" , |
368 | .of_match_table = ads124s_of_table, |
369 | }, |
370 | .probe = ads124s_probe, |
371 | .id_table = ads124s_id, |
372 | }; |
373 | module_spi_driver(ads124s_driver); |
374 | |
375 | MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>" ); |
376 | MODULE_DESCRIPTION("TI TI_ADS12S0X ADC" ); |
377 | MODULE_LICENSE("GPL v2" ); |
378 | |