180503b23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ec04cb04SLars-Peter Clausen /*
3ec04cb04SLars-Peter Clausen * Common library for ADIS16XXX devices
4ec04cb04SLars-Peter Clausen *
5ec04cb04SLars-Peter Clausen * Copyright 2012 Analog Devices Inc.
6ec04cb04SLars-Peter Clausen * Author: Lars-Peter Clausen <lars@metafoo.de>
7ec04cb04SLars-Peter Clausen */
8ec04cb04SLars-Peter Clausen
9ec04cb04SLars-Peter Clausen #include <linux/export.h>
10ec04cb04SLars-Peter Clausen #include <linux/interrupt.h>
11ec04cb04SLars-Peter Clausen #include <linux/mutex.h>
12ec04cb04SLars-Peter Clausen #include <linux/kernel.h>
13ec04cb04SLars-Peter Clausen #include <linux/spi/spi.h>
14ec04cb04SLars-Peter Clausen #include <linux/slab.h>
15ec04cb04SLars-Peter Clausen
16ec04cb04SLars-Peter Clausen #include <linux/iio/iio.h>
17ec04cb04SLars-Peter Clausen #include <linux/iio/buffer.h>
18ec04cb04SLars-Peter Clausen #include <linux/iio/trigger_consumer.h>
19ec04cb04SLars-Peter Clausen #include <linux/iio/triggered_buffer.h>
20ec04cb04SLars-Peter Clausen #include <linux/iio/imu/adis.h>
21ec04cb04SLars-Peter Clausen
adis_update_scan_mode_burst(struct iio_dev * indio_dev,const unsigned long * scan_mask)220e92e2d0SAlexandru Ardelean static int adis_update_scan_mode_burst(struct iio_dev *indio_dev,
230e92e2d0SAlexandru Ardelean const unsigned long *scan_mask)
240e92e2d0SAlexandru Ardelean {
250e92e2d0SAlexandru Ardelean struct adis *adis = iio_device_get_drvdata(indio_dev);
263e04cb60SNuno Sá unsigned int burst_length, burst_max_length;
270e92e2d0SAlexandru Ardelean u8 *tx;
280e92e2d0SAlexandru Ardelean
2936e322ecSNuno Sá burst_length = adis->data->burst_len + adis->burst_extra_len;
303e04cb60SNuno Sá
3136e322ecSNuno Sá if (adis->data->burst_max_len)
3236e322ecSNuno Sá burst_max_length = adis->data->burst_max_len;
333e04cb60SNuno Sá else
343e04cb60SNuno Sá burst_max_length = burst_length;
350e92e2d0SAlexandru Ardelean
360e92e2d0SAlexandru Ardelean adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
370e92e2d0SAlexandru Ardelean if (!adis->xfer)
380e92e2d0SAlexandru Ardelean return -ENOMEM;
390e92e2d0SAlexandru Ardelean
403e04cb60SNuno Sá adis->buffer = kzalloc(burst_max_length + sizeof(u16), GFP_KERNEL);
419c0530e8SNavid Emamdoost if (!adis->buffer) {
429c0530e8SNavid Emamdoost kfree(adis->xfer);
439c0530e8SNavid Emamdoost adis->xfer = NULL;
440e92e2d0SAlexandru Ardelean return -ENOMEM;
459c0530e8SNavid Emamdoost }
460e92e2d0SAlexandru Ardelean
473e04cb60SNuno Sá tx = adis->buffer + burst_max_length;
4836e322ecSNuno Sá tx[0] = ADIS_READ_REG(adis->data->burst_reg_cmd);
490e92e2d0SAlexandru Ardelean tx[1] = 0;
500e92e2d0SAlexandru Ardelean
510e92e2d0SAlexandru Ardelean adis->xfer[0].tx_buf = tx;
520e92e2d0SAlexandru Ardelean adis->xfer[0].bits_per_word = 8;
530e92e2d0SAlexandru Ardelean adis->xfer[0].len = 2;
54dbf20809SNuno Sa if (adis->data->burst_max_speed_hz)
55dbf20809SNuno Sa adis->xfer[0].speed_hz = adis->data->burst_max_speed_hz;
560e92e2d0SAlexandru Ardelean adis->xfer[1].rx_buf = adis->buffer;
570e92e2d0SAlexandru Ardelean adis->xfer[1].bits_per_word = 8;
580e92e2d0SAlexandru Ardelean adis->xfer[1].len = burst_length;
59dbf20809SNuno Sa if (adis->data->burst_max_speed_hz)
60dbf20809SNuno Sa adis->xfer[1].speed_hz = adis->data->burst_max_speed_hz;
610e92e2d0SAlexandru Ardelean
620e92e2d0SAlexandru Ardelean spi_message_init(&adis->msg);
630e92e2d0SAlexandru Ardelean spi_message_add_tail(&adis->xfer[0], &adis->msg);
640e92e2d0SAlexandru Ardelean spi_message_add_tail(&adis->xfer[1], &adis->msg);
650e92e2d0SAlexandru Ardelean
660e92e2d0SAlexandru Ardelean return 0;
670e92e2d0SAlexandru Ardelean }
680e92e2d0SAlexandru Ardelean
adis_update_scan_mode(struct iio_dev * indio_dev,const unsigned long * scan_mask)69ec04cb04SLars-Peter Clausen int adis_update_scan_mode(struct iio_dev *indio_dev,
70ec04cb04SLars-Peter Clausen const unsigned long *scan_mask)
71ec04cb04SLars-Peter Clausen {
72ec04cb04SLars-Peter Clausen struct adis *adis = iio_device_get_drvdata(indio_dev);
73ec04cb04SLars-Peter Clausen const struct iio_chan_spec *chan;
74ec04cb04SLars-Peter Clausen unsigned int scan_count;
75ec04cb04SLars-Peter Clausen unsigned int i, j;
76ec04cb04SLars-Peter Clausen __be16 *tx, *rx;
77ec04cb04SLars-Peter Clausen
78ec04cb04SLars-Peter Clausen kfree(adis->xfer);
79ec04cb04SLars-Peter Clausen kfree(adis->buffer);
80ec04cb04SLars-Peter Clausen
8136e322ecSNuno Sá if (adis->data->burst_len)
820e92e2d0SAlexandru Ardelean return adis_update_scan_mode_burst(indio_dev, scan_mask);
830e92e2d0SAlexandru Ardelean
84ec04cb04SLars-Peter Clausen scan_count = indio_dev->scan_bytes / 2;
85ec04cb04SLars-Peter Clausen
86ec04cb04SLars-Peter Clausen adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL);
87ec04cb04SLars-Peter Clausen if (!adis->xfer)
88ec04cb04SLars-Peter Clausen return -ENOMEM;
89ec04cb04SLars-Peter Clausen
906396bb22SKees Cook adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL);
91ab612b1dSNavid Emamdoost if (!adis->buffer) {
92ab612b1dSNavid Emamdoost kfree(adis->xfer);
93ab612b1dSNavid Emamdoost adis->xfer = NULL;
94ec04cb04SLars-Peter Clausen return -ENOMEM;
95ab612b1dSNavid Emamdoost }
96ec04cb04SLars-Peter Clausen
97ec04cb04SLars-Peter Clausen rx = adis->buffer;
98d590faf9SLars-Peter Clausen tx = rx + scan_count;
99ec04cb04SLars-Peter Clausen
100ec04cb04SLars-Peter Clausen spi_message_init(&adis->msg);
101ec04cb04SLars-Peter Clausen
102ec04cb04SLars-Peter Clausen for (j = 0; j <= scan_count; j++) {
103ec04cb04SLars-Peter Clausen adis->xfer[j].bits_per_word = 8;
104ec04cb04SLars-Peter Clausen if (j != scan_count)
105ec04cb04SLars-Peter Clausen adis->xfer[j].cs_change = 1;
106ec04cb04SLars-Peter Clausen adis->xfer[j].len = 2;
10761e618beSSergiu Cuciurean adis->xfer[j].delay.value = adis->data->read_delay;
10861e618beSSergiu Cuciurean adis->xfer[j].delay.unit = SPI_DELAY_UNIT_USECS;
109ec04cb04SLars-Peter Clausen if (j < scan_count)
110ec04cb04SLars-Peter Clausen adis->xfer[j].tx_buf = &tx[j];
111ec04cb04SLars-Peter Clausen if (j >= 1)
112ec04cb04SLars-Peter Clausen adis->xfer[j].rx_buf = &rx[j - 1];
113ec04cb04SLars-Peter Clausen spi_message_add_tail(&adis->xfer[j], &adis->msg);
114ec04cb04SLars-Peter Clausen }
115ec04cb04SLars-Peter Clausen
116ec04cb04SLars-Peter Clausen chan = indio_dev->channels;
117ec04cb04SLars-Peter Clausen for (i = 0; i < indio_dev->num_channels; i++, chan++) {
118ec04cb04SLars-Peter Clausen if (!test_bit(chan->scan_index, scan_mask))
119ec04cb04SLars-Peter Clausen continue;
12057a1228aSLars-Peter Clausen if (chan->scan_type.storagebits == 32)
12157a1228aSLars-Peter Clausen *tx++ = cpu_to_be16((chan->address + 2) << 8);
122ec04cb04SLars-Peter Clausen *tx++ = cpu_to_be16(chan->address << 8);
123ec04cb04SLars-Peter Clausen }
124ec04cb04SLars-Peter Clausen
125ec04cb04SLars-Peter Clausen return 0;
126ec04cb04SLars-Peter Clausen }
127*6c9304d6SJonathan Cameron EXPORT_SYMBOL_NS_GPL(adis_update_scan_mode, IIO_ADISLIB);
128ec04cb04SLars-Peter Clausen
adis_trigger_handler(int irq,void * p)129ec04cb04SLars-Peter Clausen static irqreturn_t adis_trigger_handler(int irq, void *p)
130ec04cb04SLars-Peter Clausen {
131ec04cb04SLars-Peter Clausen struct iio_poll_func *pf = p;
132ec04cb04SLars-Peter Clausen struct iio_dev *indio_dev = pf->indio_dev;
133ec04cb04SLars-Peter Clausen struct adis *adis = iio_device_get_drvdata(indio_dev);
134ec04cb04SLars-Peter Clausen int ret;
135ec04cb04SLars-Peter Clausen
136484a0bf0SLars-Peter Clausen if (adis->data->has_paging) {
1376a9afcb1SAlexandru Ardelean mutex_lock(&adis->state_lock);
138484a0bf0SLars-Peter Clausen if (adis->current_page != 0) {
139484a0bf0SLars-Peter Clausen adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
140484a0bf0SLars-Peter Clausen adis->tx[1] = 0;
14100f6742bSNuno Sa ret = spi_write(adis->spi, adis->tx, 2);
14200f6742bSNuno Sa if (ret) {
14300f6742bSNuno Sa dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret);
14400f6742bSNuno Sa mutex_unlock(&adis->state_lock);
14500f6742bSNuno Sa goto irq_done;
14600f6742bSNuno Sa }
1470ae15708SNuno Sa
1480ae15708SNuno Sa adis->current_page = 0;
149484a0bf0SLars-Peter Clausen }
150484a0bf0SLars-Peter Clausen }
151484a0bf0SLars-Peter Clausen
152ec04cb04SLars-Peter Clausen ret = spi_sync(adis->spi, &adis->msg);
1530ae15708SNuno Sa if (adis->data->has_paging)
1546a9afcb1SAlexandru Ardelean mutex_unlock(&adis->state_lock);
155669da56aSNuno Sa if (ret) {
156669da56aSNuno Sa dev_err(&adis->spi->dev, "Failed to read data: %d", ret);
157669da56aSNuno Sa goto irq_done;
158669da56aSNuno Sa }
159484a0bf0SLars-Peter Clausen
160419d8ce4SLars-Peter Clausen iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
161419d8ce4SLars-Peter Clausen pf->timestamp);
162ec04cb04SLars-Peter Clausen
16300f6742bSNuno Sa irq_done:
164ec04cb04SLars-Peter Clausen iio_trigger_notify_done(indio_dev->trig);
165ec04cb04SLars-Peter Clausen
166ec04cb04SLars-Peter Clausen return IRQ_HANDLED;
167ec04cb04SLars-Peter Clausen }
168ec04cb04SLars-Peter Clausen
adis_buffer_cleanup(void * arg)169fec86c6bSNuno Sá static void adis_buffer_cleanup(void *arg)
170fec86c6bSNuno Sá {
171fec86c6bSNuno Sá struct adis *adis = arg;
172fec86c6bSNuno Sá
173fec86c6bSNuno Sá kfree(adis->buffer);
174fec86c6bSNuno Sá kfree(adis->xfer);
175fec86c6bSNuno Sá }
176fec86c6bSNuno Sá
177ec04cb04SLars-Peter Clausen /**
178fec86c6bSNuno Sá * devm_adis_setup_buffer_and_trigger() - Sets up buffer and trigger for
179fec86c6bSNuno Sá * the managed adis device
180fec86c6bSNuno Sá * @adis: The adis device
181fec86c6bSNuno Sá * @indio_dev: The IIO device
182fec86c6bSNuno Sá * @trigger_handler: Optional trigger handler, may be NULL.
183fec86c6bSNuno Sá *
184fec86c6bSNuno Sá * Returns 0 on success, a negative error code otherwise.
185fec86c6bSNuno Sá *
186d8f0cd76SNuno Sá * This function sets up the buffer and trigger for a adis devices. If
187d8f0cd76SNuno Sá * 'trigger_handler' is NULL the default trigger handler will be used. The
188d8f0cd76SNuno Sá * default trigger handler will simply read the registers assigned to the
189d8f0cd76SNuno Sá * currently active channels.
190fec86c6bSNuno Sá */
191fec86c6bSNuno Sá int
devm_adis_setup_buffer_and_trigger(struct adis * adis,struct iio_dev * indio_dev,irq_handler_t trigger_handler)192fec86c6bSNuno Sá devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev,
193fec86c6bSNuno Sá irq_handler_t trigger_handler)
194fec86c6bSNuno Sá {
195fec86c6bSNuno Sá int ret;
196fec86c6bSNuno Sá
197fec86c6bSNuno Sá if (!trigger_handler)
198fec86c6bSNuno Sá trigger_handler = adis_trigger_handler;
199fec86c6bSNuno Sá
200fec86c6bSNuno Sá ret = devm_iio_triggered_buffer_setup(&adis->spi->dev, indio_dev,
201fec86c6bSNuno Sá &iio_pollfunc_store_time,
202fec86c6bSNuno Sá trigger_handler, NULL);
203fec86c6bSNuno Sá if (ret)
204fec86c6bSNuno Sá return ret;
205fec86c6bSNuno Sá
206fec86c6bSNuno Sá if (adis->spi->irq) {
207fec86c6bSNuno Sá ret = devm_adis_probe_trigger(adis, indio_dev);
208fec86c6bSNuno Sá if (ret)
209fec86c6bSNuno Sá return ret;
210fec86c6bSNuno Sá }
211fec86c6bSNuno Sá
212fec86c6bSNuno Sá return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup,
213fec86c6bSNuno Sá adis);
214fec86c6bSNuno Sá }
215*6c9304d6SJonathan Cameron EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger, IIO_ADISLIB);
216fec86c6bSNuno Sá
217