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; 54*dbf20809SNuno Sa if (adis->data->burst_max_speed_hz) 55*dbf20809SNuno 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; 59*dbf20809SNuno Sa if (adis->data->burst_max_speed_hz) 60*dbf20809SNuno 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 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 } 127ec04cb04SLars-Peter Clausen EXPORT_SYMBOL_GPL(adis_update_scan_mode); 128ec04cb04SLars-Peter Clausen 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 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 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á } 215fec86c6bSNuno Sá EXPORT_SYMBOL_GPL(devm_adis_setup_buffer_and_trigger); 216fec86c6bSNuno Sá 217