162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Common library for ADIS16XXX devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2012 Analog Devices Inc. 662306a36Sopenharmony_ci * Author: Lars-Peter Clausen <lars@metafoo.de> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/mutex.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/spi/spi.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/iio/iio.h> 1762306a36Sopenharmony_ci#include <linux/iio/buffer.h> 1862306a36Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 1962306a36Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 2062306a36Sopenharmony_ci#include <linux/iio/imu/adis.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int adis_update_scan_mode_burst(struct iio_dev *indio_dev, 2362306a36Sopenharmony_ci const unsigned long *scan_mask) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct adis *adis = iio_device_get_drvdata(indio_dev); 2662306a36Sopenharmony_ci unsigned int burst_length, burst_max_length; 2762306a36Sopenharmony_ci u8 *tx; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci burst_length = adis->data->burst_len + adis->burst_extra_len; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (adis->data->burst_max_len) 3262306a36Sopenharmony_ci burst_max_length = adis->data->burst_max_len; 3362306a36Sopenharmony_ci else 3462306a36Sopenharmony_ci burst_max_length = burst_length; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL); 3762306a36Sopenharmony_ci if (!adis->xfer) 3862306a36Sopenharmony_ci return -ENOMEM; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci adis->buffer = kzalloc(burst_max_length + sizeof(u16), GFP_KERNEL); 4162306a36Sopenharmony_ci if (!adis->buffer) { 4262306a36Sopenharmony_ci kfree(adis->xfer); 4362306a36Sopenharmony_ci adis->xfer = NULL; 4462306a36Sopenharmony_ci return -ENOMEM; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci tx = adis->buffer + burst_max_length; 4862306a36Sopenharmony_ci tx[0] = ADIS_READ_REG(adis->data->burst_reg_cmd); 4962306a36Sopenharmony_ci tx[1] = 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci adis->xfer[0].tx_buf = tx; 5262306a36Sopenharmony_ci adis->xfer[0].bits_per_word = 8; 5362306a36Sopenharmony_ci adis->xfer[0].len = 2; 5462306a36Sopenharmony_ci if (adis->data->burst_max_speed_hz) 5562306a36Sopenharmony_ci adis->xfer[0].speed_hz = adis->data->burst_max_speed_hz; 5662306a36Sopenharmony_ci adis->xfer[1].rx_buf = adis->buffer; 5762306a36Sopenharmony_ci adis->xfer[1].bits_per_word = 8; 5862306a36Sopenharmony_ci adis->xfer[1].len = burst_length; 5962306a36Sopenharmony_ci if (adis->data->burst_max_speed_hz) 6062306a36Sopenharmony_ci adis->xfer[1].speed_hz = adis->data->burst_max_speed_hz; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci spi_message_init(&adis->msg); 6362306a36Sopenharmony_ci spi_message_add_tail(&adis->xfer[0], &adis->msg); 6462306a36Sopenharmony_ci spi_message_add_tail(&adis->xfer[1], &adis->msg); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciint adis_update_scan_mode(struct iio_dev *indio_dev, 7062306a36Sopenharmony_ci const unsigned long *scan_mask) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct adis *adis = iio_device_get_drvdata(indio_dev); 7362306a36Sopenharmony_ci const struct iio_chan_spec *chan; 7462306a36Sopenharmony_ci unsigned int scan_count; 7562306a36Sopenharmony_ci unsigned int i, j; 7662306a36Sopenharmony_ci __be16 *tx, *rx; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci kfree(adis->xfer); 7962306a36Sopenharmony_ci kfree(adis->buffer); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (adis->data->burst_len) 8262306a36Sopenharmony_ci return adis_update_scan_mode_burst(indio_dev, scan_mask); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci scan_count = indio_dev->scan_bytes / 2; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL); 8762306a36Sopenharmony_ci if (!adis->xfer) 8862306a36Sopenharmony_ci return -ENOMEM; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL); 9162306a36Sopenharmony_ci if (!adis->buffer) { 9262306a36Sopenharmony_ci kfree(adis->xfer); 9362306a36Sopenharmony_ci adis->xfer = NULL; 9462306a36Sopenharmony_ci return -ENOMEM; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci rx = adis->buffer; 9862306a36Sopenharmony_ci tx = rx + scan_count; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci spi_message_init(&adis->msg); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (j = 0; j <= scan_count; j++) { 10362306a36Sopenharmony_ci adis->xfer[j].bits_per_word = 8; 10462306a36Sopenharmony_ci if (j != scan_count) 10562306a36Sopenharmony_ci adis->xfer[j].cs_change = 1; 10662306a36Sopenharmony_ci adis->xfer[j].len = 2; 10762306a36Sopenharmony_ci adis->xfer[j].delay.value = adis->data->read_delay; 10862306a36Sopenharmony_ci adis->xfer[j].delay.unit = SPI_DELAY_UNIT_USECS; 10962306a36Sopenharmony_ci if (j < scan_count) 11062306a36Sopenharmony_ci adis->xfer[j].tx_buf = &tx[j]; 11162306a36Sopenharmony_ci if (j >= 1) 11262306a36Sopenharmony_ci adis->xfer[j].rx_buf = &rx[j - 1]; 11362306a36Sopenharmony_ci spi_message_add_tail(&adis->xfer[j], &adis->msg); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci chan = indio_dev->channels; 11762306a36Sopenharmony_ci for (i = 0; i < indio_dev->num_channels; i++, chan++) { 11862306a36Sopenharmony_ci if (!test_bit(chan->scan_index, scan_mask)) 11962306a36Sopenharmony_ci continue; 12062306a36Sopenharmony_ci if (chan->scan_type.storagebits == 32) 12162306a36Sopenharmony_ci *tx++ = cpu_to_be16((chan->address + 2) << 8); 12262306a36Sopenharmony_ci *tx++ = cpu_to_be16(chan->address << 8); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(adis_update_scan_mode, IIO_ADISLIB); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic irqreturn_t adis_trigger_handler(int irq, void *p) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct iio_poll_func *pf = p; 13262306a36Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 13362306a36Sopenharmony_ci struct adis *adis = iio_device_get_drvdata(indio_dev); 13462306a36Sopenharmony_ci int ret; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (adis->data->has_paging) { 13762306a36Sopenharmony_ci mutex_lock(&adis->state_lock); 13862306a36Sopenharmony_ci if (adis->current_page != 0) { 13962306a36Sopenharmony_ci adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); 14062306a36Sopenharmony_ci adis->tx[1] = 0; 14162306a36Sopenharmony_ci ret = spi_write(adis->spi, adis->tx, 2); 14262306a36Sopenharmony_ci if (ret) { 14362306a36Sopenharmony_ci dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret); 14462306a36Sopenharmony_ci mutex_unlock(&adis->state_lock); 14562306a36Sopenharmony_ci goto irq_done; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci adis->current_page = 0; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ret = spi_sync(adis->spi, &adis->msg); 15362306a36Sopenharmony_ci if (adis->data->has_paging) 15462306a36Sopenharmony_ci mutex_unlock(&adis->state_lock); 15562306a36Sopenharmony_ci if (ret) { 15662306a36Sopenharmony_ci dev_err(&adis->spi->dev, "Failed to read data: %d", ret); 15762306a36Sopenharmony_ci goto irq_done; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, 16162306a36Sopenharmony_ci pf->timestamp); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciirq_done: 16462306a36Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return IRQ_HANDLED; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void adis_buffer_cleanup(void *arg) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct adis *adis = arg; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci kfree(adis->buffer); 17462306a36Sopenharmony_ci kfree(adis->xfer); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * devm_adis_setup_buffer_and_trigger() - Sets up buffer and trigger for 17962306a36Sopenharmony_ci * the managed adis device 18062306a36Sopenharmony_ci * @adis: The adis device 18162306a36Sopenharmony_ci * @indio_dev: The IIO device 18262306a36Sopenharmony_ci * @trigger_handler: Optional trigger handler, may be NULL. 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * Returns 0 on success, a negative error code otherwise. 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * This function sets up the buffer and trigger for a adis devices. If 18762306a36Sopenharmony_ci * 'trigger_handler' is NULL the default trigger handler will be used. The 18862306a36Sopenharmony_ci * default trigger handler will simply read the registers assigned to the 18962306a36Sopenharmony_ci * currently active channels. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ciint 19262306a36Sopenharmony_cidevm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, 19362306a36Sopenharmony_ci irq_handler_t trigger_handler) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci int ret; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (!trigger_handler) 19862306a36Sopenharmony_ci trigger_handler = adis_trigger_handler; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(&adis->spi->dev, indio_dev, 20162306a36Sopenharmony_ci &iio_pollfunc_store_time, 20262306a36Sopenharmony_ci trigger_handler, NULL); 20362306a36Sopenharmony_ci if (ret) 20462306a36Sopenharmony_ci return ret; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (adis->spi->irq) { 20762306a36Sopenharmony_ci ret = devm_adis_probe_trigger(adis, indio_dev); 20862306a36Sopenharmony_ci if (ret) 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup, 21362306a36Sopenharmony_ci adis); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciEXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger, IIO_ADISLIB); 21662306a36Sopenharmony_ci 217