xref: /openbmc/linux/drivers/iio/imu/adis_buffer.c (revision 00f6742b)
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 
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;
540e92e2d0SAlexandru Ardelean 	adis->xfer[1].rx_buf = adis->buffer;
550e92e2d0SAlexandru Ardelean 	adis->xfer[1].bits_per_word = 8;
560e92e2d0SAlexandru Ardelean 	adis->xfer[1].len = burst_length;
570e92e2d0SAlexandru Ardelean 
580e92e2d0SAlexandru Ardelean 	spi_message_init(&adis->msg);
590e92e2d0SAlexandru Ardelean 	spi_message_add_tail(&adis->xfer[0], &adis->msg);
600e92e2d0SAlexandru Ardelean 	spi_message_add_tail(&adis->xfer[1], &adis->msg);
610e92e2d0SAlexandru Ardelean 
620e92e2d0SAlexandru Ardelean 	return 0;
630e92e2d0SAlexandru Ardelean }
640e92e2d0SAlexandru Ardelean 
65ec04cb04SLars-Peter Clausen int adis_update_scan_mode(struct iio_dev *indio_dev,
66ec04cb04SLars-Peter Clausen 	const unsigned long *scan_mask)
67ec04cb04SLars-Peter Clausen {
68ec04cb04SLars-Peter Clausen 	struct adis *adis = iio_device_get_drvdata(indio_dev);
69ec04cb04SLars-Peter Clausen 	const struct iio_chan_spec *chan;
70ec04cb04SLars-Peter Clausen 	unsigned int scan_count;
71ec04cb04SLars-Peter Clausen 	unsigned int i, j;
72ec04cb04SLars-Peter Clausen 	__be16 *tx, *rx;
73ec04cb04SLars-Peter Clausen 
74ec04cb04SLars-Peter Clausen 	kfree(adis->xfer);
75ec04cb04SLars-Peter Clausen 	kfree(adis->buffer);
76ec04cb04SLars-Peter Clausen 
7736e322ecSNuno Sá 	if (adis->data->burst_len)
780e92e2d0SAlexandru Ardelean 		return adis_update_scan_mode_burst(indio_dev, scan_mask);
790e92e2d0SAlexandru Ardelean 
80ec04cb04SLars-Peter Clausen 	scan_count = indio_dev->scan_bytes / 2;
81ec04cb04SLars-Peter Clausen 
82ec04cb04SLars-Peter Clausen 	adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL);
83ec04cb04SLars-Peter Clausen 	if (!adis->xfer)
84ec04cb04SLars-Peter Clausen 		return -ENOMEM;
85ec04cb04SLars-Peter Clausen 
866396bb22SKees Cook 	adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL);
87ab612b1dSNavid Emamdoost 	if (!adis->buffer) {
88ab612b1dSNavid Emamdoost 		kfree(adis->xfer);
89ab612b1dSNavid Emamdoost 		adis->xfer = NULL;
90ec04cb04SLars-Peter Clausen 		return -ENOMEM;
91ab612b1dSNavid Emamdoost 	}
92ec04cb04SLars-Peter Clausen 
93ec04cb04SLars-Peter Clausen 	rx = adis->buffer;
94d590faf9SLars-Peter Clausen 	tx = rx + scan_count;
95ec04cb04SLars-Peter Clausen 
96ec04cb04SLars-Peter Clausen 	spi_message_init(&adis->msg);
97ec04cb04SLars-Peter Clausen 
98ec04cb04SLars-Peter Clausen 	for (j = 0; j <= scan_count; j++) {
99ec04cb04SLars-Peter Clausen 		adis->xfer[j].bits_per_word = 8;
100ec04cb04SLars-Peter Clausen 		if (j != scan_count)
101ec04cb04SLars-Peter Clausen 			adis->xfer[j].cs_change = 1;
102ec04cb04SLars-Peter Clausen 		adis->xfer[j].len = 2;
10361e618beSSergiu Cuciurean 		adis->xfer[j].delay.value = adis->data->read_delay;
10461e618beSSergiu Cuciurean 		adis->xfer[j].delay.unit = SPI_DELAY_UNIT_USECS;
105ec04cb04SLars-Peter Clausen 		if (j < scan_count)
106ec04cb04SLars-Peter Clausen 			adis->xfer[j].tx_buf = &tx[j];
107ec04cb04SLars-Peter Clausen 		if (j >= 1)
108ec04cb04SLars-Peter Clausen 			adis->xfer[j].rx_buf = &rx[j - 1];
109ec04cb04SLars-Peter Clausen 		spi_message_add_tail(&adis->xfer[j], &adis->msg);
110ec04cb04SLars-Peter Clausen 	}
111ec04cb04SLars-Peter Clausen 
112ec04cb04SLars-Peter Clausen 	chan = indio_dev->channels;
113ec04cb04SLars-Peter Clausen 	for (i = 0; i < indio_dev->num_channels; i++, chan++) {
114ec04cb04SLars-Peter Clausen 		if (!test_bit(chan->scan_index, scan_mask))
115ec04cb04SLars-Peter Clausen 			continue;
11657a1228aSLars-Peter Clausen 		if (chan->scan_type.storagebits == 32)
11757a1228aSLars-Peter Clausen 			*tx++ = cpu_to_be16((chan->address + 2) << 8);
118ec04cb04SLars-Peter Clausen 		*tx++ = cpu_to_be16(chan->address << 8);
119ec04cb04SLars-Peter Clausen 	}
120ec04cb04SLars-Peter Clausen 
121ec04cb04SLars-Peter Clausen 	return 0;
122ec04cb04SLars-Peter Clausen }
123ec04cb04SLars-Peter Clausen EXPORT_SYMBOL_GPL(adis_update_scan_mode);
124ec04cb04SLars-Peter Clausen 
125ec04cb04SLars-Peter Clausen static irqreturn_t adis_trigger_handler(int irq, void *p)
126ec04cb04SLars-Peter Clausen {
127ec04cb04SLars-Peter Clausen 	struct iio_poll_func *pf = p;
128ec04cb04SLars-Peter Clausen 	struct iio_dev *indio_dev = pf->indio_dev;
129ec04cb04SLars-Peter Clausen 	struct adis *adis = iio_device_get_drvdata(indio_dev);
130ec04cb04SLars-Peter Clausen 	int ret;
131ec04cb04SLars-Peter Clausen 
132484a0bf0SLars-Peter Clausen 	if (adis->data->has_paging) {
1336a9afcb1SAlexandru Ardelean 		mutex_lock(&adis->state_lock);
134484a0bf0SLars-Peter Clausen 		if (adis->current_page != 0) {
135484a0bf0SLars-Peter Clausen 			adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID);
136484a0bf0SLars-Peter Clausen 			adis->tx[1] = 0;
137*00f6742bSNuno Sa 			ret = spi_write(adis->spi, adis->tx, 2);
138*00f6742bSNuno Sa 			if (ret) {
139*00f6742bSNuno Sa 				dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret);
140*00f6742bSNuno Sa 				mutex_unlock(&adis->state_lock);
141*00f6742bSNuno Sa 				goto irq_done;
142*00f6742bSNuno Sa 			}
143484a0bf0SLars-Peter Clausen 		}
144484a0bf0SLars-Peter Clausen 	}
145484a0bf0SLars-Peter Clausen 
146ec04cb04SLars-Peter Clausen 	ret = spi_sync(adis->spi, &adis->msg);
147ec04cb04SLars-Peter Clausen 	if (ret)
148ec04cb04SLars-Peter Clausen 		dev_err(&adis->spi->dev, "Failed to read data: %d", ret);
149ec04cb04SLars-Peter Clausen 
150484a0bf0SLars-Peter Clausen 
151484a0bf0SLars-Peter Clausen 	if (adis->data->has_paging) {
152484a0bf0SLars-Peter Clausen 		adis->current_page = 0;
1536a9afcb1SAlexandru Ardelean 		mutex_unlock(&adis->state_lock);
154484a0bf0SLars-Peter Clausen 	}
155484a0bf0SLars-Peter Clausen 
156419d8ce4SLars-Peter Clausen 	iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
157419d8ce4SLars-Peter Clausen 		pf->timestamp);
158ec04cb04SLars-Peter Clausen 
159*00f6742bSNuno Sa irq_done:
160ec04cb04SLars-Peter Clausen 	iio_trigger_notify_done(indio_dev->trig);
161ec04cb04SLars-Peter Clausen 
162ec04cb04SLars-Peter Clausen 	return IRQ_HANDLED;
163ec04cb04SLars-Peter Clausen }
164ec04cb04SLars-Peter Clausen 
165fec86c6bSNuno Sá static void adis_buffer_cleanup(void *arg)
166fec86c6bSNuno Sá {
167fec86c6bSNuno Sá 	struct adis *adis = arg;
168fec86c6bSNuno Sá 
169fec86c6bSNuno Sá 	kfree(adis->buffer);
170fec86c6bSNuno Sá 	kfree(adis->xfer);
171fec86c6bSNuno Sá }
172fec86c6bSNuno Sá 
173ec04cb04SLars-Peter Clausen /**
174fec86c6bSNuno Sá  * devm_adis_setup_buffer_and_trigger() - Sets up buffer and trigger for
175fec86c6bSNuno Sá  *					  the managed adis device
176fec86c6bSNuno Sá  * @adis: The adis device
177fec86c6bSNuno Sá  * @indio_dev: The IIO device
178fec86c6bSNuno Sá  * @trigger_handler: Optional trigger handler, may be NULL.
179fec86c6bSNuno Sá  *
180fec86c6bSNuno Sá  * Returns 0 on success, a negative error code otherwise.
181fec86c6bSNuno Sá  *
182d8f0cd76SNuno Sá  * This function sets up the buffer and trigger for a adis devices.  If
183d8f0cd76SNuno Sá  * 'trigger_handler' is NULL the default trigger handler will be used. The
184d8f0cd76SNuno Sá  * default trigger handler will simply read the registers assigned to the
185d8f0cd76SNuno Sá  * currently active channels.
186fec86c6bSNuno Sá  */
187fec86c6bSNuno Sá int
188fec86c6bSNuno Sá devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev,
189fec86c6bSNuno Sá 				   irq_handler_t trigger_handler)
190fec86c6bSNuno Sá {
191fec86c6bSNuno Sá 	int ret;
192fec86c6bSNuno Sá 
193fec86c6bSNuno Sá 	if (!trigger_handler)
194fec86c6bSNuno Sá 		trigger_handler = adis_trigger_handler;
195fec86c6bSNuno Sá 
196fec86c6bSNuno Sá 	ret = devm_iio_triggered_buffer_setup(&adis->spi->dev, indio_dev,
197fec86c6bSNuno Sá 					      &iio_pollfunc_store_time,
198fec86c6bSNuno Sá 					      trigger_handler, NULL);
199fec86c6bSNuno Sá 	if (ret)
200fec86c6bSNuno Sá 		return ret;
201fec86c6bSNuno Sá 
202fec86c6bSNuno Sá 	if (adis->spi->irq) {
203fec86c6bSNuno Sá 		ret = devm_adis_probe_trigger(adis, indio_dev);
204fec86c6bSNuno Sá 		if (ret)
205fec86c6bSNuno Sá 			return ret;
206fec86c6bSNuno Sá 	}
207fec86c6bSNuno Sá 
208fec86c6bSNuno Sá 	return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup,
209fec86c6bSNuno Sá 					adis);
210fec86c6bSNuno Sá }
211fec86c6bSNuno Sá EXPORT_SYMBOL_GPL(devm_adis_setup_buffer_and_trigger);
212fec86c6bSNuno Sá 
213