diff options
Diffstat (limited to 'drivers/iio/imu/adis_buffer.c')
| -rw-r--r-- | drivers/iio/imu/adis_buffer.c | 185 |
1 files changed, 118 insertions, 67 deletions
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 76643c5571aa..cd3db2388164 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Common library for ADIS16XXX devices * * Copyright 2012 Analog Devices Inc. * Author: Lars-Peter Clausen <lars@metafoo.de> - * - * Licensed under the GPL-2 or later. */ #include <linux/export.h> @@ -20,8 +19,53 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/imu/adis.h> +static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct adis *adis = iio_device_get_drvdata(indio_dev); + unsigned int burst_length, burst_max_length; + u8 *tx; + + burst_length = adis->data->burst_len + adis->burst_extra_len; + + if (adis->data->burst_max_len) + burst_max_length = adis->data->burst_max_len; + else + burst_max_length = burst_length; + + adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL); + if (!adis->xfer) + return -ENOMEM; + + adis->buffer = kzalloc(burst_max_length + sizeof(u16), GFP_KERNEL); + if (!adis->buffer) { + kfree(adis->xfer); + adis->xfer = NULL; + return -ENOMEM; + } + + tx = adis->buffer + burst_max_length; + tx[0] = ADIS_READ_REG(adis->data->burst_reg_cmd); + tx[1] = 0; + + adis->xfer[0].tx_buf = tx; + adis->xfer[0].len = 2; + if (adis->data->burst_max_speed_hz) + adis->xfer[0].speed_hz = adis->data->burst_max_speed_hz; + adis->xfer[1].rx_buf = adis->buffer; + adis->xfer[1].len = burst_length; + if (adis->data->burst_max_speed_hz) + adis->xfer[1].speed_hz = adis->data->burst_max_speed_hz; + + spi_message_init(&adis->msg); + spi_message_add_tail(&adis->xfer[0], &adis->msg); + spi_message_add_tail(&adis->xfer[1], &adis->msg); + + return 0; +} + int adis_update_scan_mode(struct iio_dev *indio_dev, - const unsigned long *scan_mask) + const unsigned long *scan_mask) { struct adis *adis = iio_device_get_drvdata(indio_dev); const struct iio_chan_spec *chan; @@ -32,6 +76,9 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, kfree(adis->xfer); kfree(adis->buffer); + if (adis->data->burst_len) + return adis_update_scan_mode_burst(indio_dev, scan_mask); + scan_count = indio_dev->scan_bytes / 2; adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL); @@ -39,8 +86,11 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, return -ENOMEM; adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL); - if (!adis->buffer) + if (!adis->buffer) { + kfree(adis->xfer); + adis->xfer = NULL; return -ENOMEM; + } rx = adis->buffer; tx = rx + scan_count; @@ -48,11 +98,11 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, spi_message_init(&adis->msg); for (j = 0; j <= scan_count; j++) { - adis->xfer[j].bits_per_word = 8; if (j != scan_count) adis->xfer[j].cs_change = 1; adis->xfer[j].len = 2; - adis->xfer[j].delay_usecs = adis->data->read_delay; + adis->xfer[j].delay.value = adis->data->read_delay; + adis->xfer[j].delay.unit = SPI_DELAY_UNIT_USECS; if (j < scan_count) adis->xfer[j].tx_buf = &tx[j]; if (j >= 1) @@ -71,101 +121,102 @@ int adis_update_scan_mode(struct iio_dev *indio_dev, return 0; } -EXPORT_SYMBOL_GPL(adis_update_scan_mode); +EXPORT_SYMBOL_NS_GPL(adis_update_scan_mode, "IIO_ADISLIB"); -static irqreturn_t adis_trigger_handler(int irq, void *p) +static int adis_paging_trigger_handler(struct adis *adis) { - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct adis *adis = iio_device_get_drvdata(indio_dev); int ret; - if (!adis->buffer) - return -ENOMEM; - - if (adis->data->has_paging) { - mutex_lock(&adis->txrx_lock); - if (adis->current_page != 0) { - adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); - adis->tx[1] = 0; - spi_write(adis->spi, adis->tx, 2); + guard(mutex)(&adis->state_lock); + if (adis->current_page != 0) { + adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); + adis->tx[1] = 0; + ret = spi_write(adis->spi, adis->tx, 2); + if (ret) { + dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret); + return ret; } + + adis->current_page = 0; } - ret = spi_sync(adis->spi, &adis->msg); - if (ret) - dev_err(&adis->spi->dev, "Failed to read data: %d", ret); + return spi_sync(adis->spi, &adis->msg); +} +static irqreturn_t adis_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adis *adis = iio_device_get_drvdata(indio_dev); + int ret; - if (adis->data->has_paging) { - adis->current_page = 0; - mutex_unlock(&adis->txrx_lock); + if (adis->data->has_paging) + ret = adis_paging_trigger_handler(adis); + else + ret = spi_sync(adis->spi, &adis->msg); + if (ret) { + dev_err(&adis->spi->dev, "Failed to read data: %d", ret); + goto irq_done; } iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, - pf->timestamp); + pf->timestamp); +irq_done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } +static void adis_buffer_cleanup(void *arg) +{ + struct adis *adis = arg; + + kfree(adis->buffer); + kfree(adis->xfer); +} + /** - * adis_setup_buffer_and_trigger() - Sets up buffer and trigger for the adis device - * @adis: The adis device. - * @indio_dev: The IIO device. - * @trigger_handler: Optional trigger handler, may be NULL. + * devm_adis_setup_buffer_and_trigger_with_attrs() - Sets up buffer and trigger + * for the managed adis device with buffer attributes. + * @adis: The adis device + * @indio_dev: The IIO device + * @trigger_handler: Trigger handler: should handle the buffer readings. + * @ops: Optional buffer setup functions, may be NULL. + * @buffer_attrs: Extra buffer attributes. * * Returns 0 on success, a negative error code otherwise. * - * This function sets up the buffer and trigger for a adis devices. If - * 'trigger_handler' is NULL the default trigger handler will be used. The - * default trigger handler will simply read the registers assigned to the - * currently active channels. - * - * adis_cleanup_buffer_and_trigger() should be called to free the resources - * allocated by this function. + * This function sets up the buffer (with buffer setup functions and extra + * buffer attributes) and trigger for a adis devices with buffer attributes. */ -int adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, - irqreturn_t (*trigger_handler)(int, void *)) +int +devm_adis_setup_buffer_and_trigger_with_attrs(struct adis *adis, struct iio_dev *indio_dev, + irq_handler_t trigger_handler, + const struct iio_buffer_setup_ops *ops, + const struct iio_dev_attr **buffer_attrs) { int ret; if (!trigger_handler) trigger_handler = adis_trigger_handler; - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup_ext(&adis->spi->dev, indio_dev, + &iio_pollfunc_store_time, + trigger_handler, + IIO_BUFFER_DIRECTION_IN, + ops, + buffer_attrs); if (ret) return ret; if (adis->spi->irq) { - ret = adis_probe_trigger(adis, indio_dev); + ret = devm_adis_probe_trigger(adis, indio_dev); if (ret) - goto error_buffer_cleanup; + return ret; } - return 0; - -error_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); - return ret; -} -EXPORT_SYMBOL_GPL(adis_setup_buffer_and_trigger); -/** - * adis_cleanup_buffer_and_trigger() - Free buffer and trigger resources - * @adis: The adis device. - * @indio_dev: The IIO device. - * - * Frees resources allocated by adis_setup_buffer_and_trigger() - */ -void adis_cleanup_buffer_and_trigger(struct adis *adis, - struct iio_dev *indio_dev) -{ - if (adis->spi->irq) - adis_remove_trigger(adis); - kfree(adis->buffer); - kfree(adis->xfer); - iio_triggered_buffer_cleanup(indio_dev); + return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup, + adis); } -EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger); +EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger_with_attrs, "IIO_ADISLIB"); |
