1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Common library for ADIS16XXX devices 4 * 5 * Copyright 2012 Analog Devices Inc. 6 * Author: Lars-Peter Clausen <lars@metafoo.de> 7 */ 8 9 #include <linux/export.h> 10 #include <linux/interrupt.h> 11 #include <linux/mutex.h> 12 #include <linux/kernel.h> 13 #include <linux/spi/spi.h> 14 #include <linux/slab.h> 15 16 #include <linux/iio/iio.h> 17 #include <linux/iio/buffer.h> 18 #include <linux/iio/trigger_consumer.h> 19 #include <linux/iio/triggered_buffer.h> 20 #include <linux/iio/imu/adis.h> 21 22 static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, 23 const unsigned long *scan_mask) 24 { 25 struct adis *adis = iio_device_get_drvdata(indio_dev); 26 unsigned int burst_length, burst_max_length; 27 u8 *tx; 28 29 burst_length = adis->data->burst_len + adis->burst_extra_len; 30 31 if (adis->data->burst_max_len) 32 burst_max_length = adis->data->burst_max_len; 33 else 34 burst_max_length = burst_length; 35 36 adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL); 37 if (!adis->xfer) 38 return -ENOMEM; 39 40 adis->buffer = kzalloc(burst_max_length + sizeof(u16), GFP_KERNEL); 41 if (!adis->buffer) { 42 kfree(adis->xfer); 43 adis->xfer = NULL; 44 return -ENOMEM; 45 } 46 47 tx = adis->buffer + burst_max_length; 48 tx[0] = ADIS_READ_REG(adis->data->burst_reg_cmd); 49 tx[1] = 0; 50 51 adis->xfer[0].tx_buf = tx; 52 adis->xfer[0].len = 2; 53 if (adis->data->burst_max_speed_hz) 54 adis->xfer[0].speed_hz = adis->data->burst_max_speed_hz; 55 adis->xfer[1].rx_buf = adis->buffer; 56 adis->xfer[1].len = burst_length; 57 if (adis->data->burst_max_speed_hz) 58 adis->xfer[1].speed_hz = adis->data->burst_max_speed_hz; 59 60 spi_message_init(&adis->msg); 61 spi_message_add_tail(&adis->xfer[0], &adis->msg); 62 spi_message_add_tail(&adis->xfer[1], &adis->msg); 63 64 return 0; 65 } 66 67 int adis_update_scan_mode(struct iio_dev *indio_dev, 68 const unsigned long *scan_mask) 69 { 70 struct adis *adis = iio_device_get_drvdata(indio_dev); 71 const struct iio_chan_spec *chan; 72 unsigned int scan_count; 73 unsigned int i, j; 74 __be16 *tx, *rx; 75 76 kfree(adis->xfer); 77 kfree(adis->buffer); 78 79 if (adis->data->burst_len) 80 return adis_update_scan_mode_burst(indio_dev, scan_mask); 81 82 scan_count = indio_dev->scan_bytes / 2; 83 84 adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL); 85 if (!adis->xfer) 86 return -ENOMEM; 87 88 adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL); 89 if (!adis->buffer) { 90 kfree(adis->xfer); 91 adis->xfer = NULL; 92 return -ENOMEM; 93 } 94 95 rx = adis->buffer; 96 tx = rx + scan_count; 97 98 spi_message_init(&adis->msg); 99 100 for (j = 0; j <= scan_count; j++) { 101 if (j != scan_count) 102 adis->xfer[j].cs_change = 1; 103 adis->xfer[j].len = 2; 104 adis->xfer[j].delay.value = adis->data->read_delay; 105 adis->xfer[j].delay.unit = SPI_DELAY_UNIT_USECS; 106 if (j < scan_count) 107 adis->xfer[j].tx_buf = &tx[j]; 108 if (j >= 1) 109 adis->xfer[j].rx_buf = &rx[j - 1]; 110 spi_message_add_tail(&adis->xfer[j], &adis->msg); 111 } 112 113 chan = indio_dev->channels; 114 for (i = 0; i < indio_dev->num_channels; i++, chan++) { 115 if (!test_bit(chan->scan_index, scan_mask)) 116 continue; 117 if (chan->scan_type.storagebits == 32) 118 *tx++ = cpu_to_be16((chan->address + 2) << 8); 119 *tx++ = cpu_to_be16(chan->address << 8); 120 } 121 122 return 0; 123 } 124 EXPORT_SYMBOL_NS_GPL(adis_update_scan_mode, "IIO_ADISLIB"); 125 126 static int adis_paging_trigger_handler(struct adis *adis) 127 { 128 int ret; 129 130 guard(mutex)(&adis->state_lock); 131 if (adis->current_page != 0) { 132 adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); 133 adis->tx[1] = 0; 134 ret = spi_write(adis->spi, adis->tx, 2); 135 if (ret) { 136 dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret); 137 return ret; 138 } 139 140 adis->current_page = 0; 141 } 142 143 return spi_sync(adis->spi, &adis->msg); 144 } 145 146 static irqreturn_t adis_trigger_handler(int irq, void *p) 147 { 148 struct iio_poll_func *pf = p; 149 struct iio_dev *indio_dev = pf->indio_dev; 150 struct adis *adis = iio_device_get_drvdata(indio_dev); 151 int ret; 152 153 if (adis->data->has_paging) 154 ret = adis_paging_trigger_handler(adis); 155 else 156 ret = spi_sync(adis->spi, &adis->msg); 157 if (ret) { 158 dev_err(&adis->spi->dev, "Failed to read data: %d", ret); 159 goto irq_done; 160 } 161 162 iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, 163 pf->timestamp); 164 165 irq_done: 166 iio_trigger_notify_done(indio_dev->trig); 167 168 return IRQ_HANDLED; 169 } 170 171 static void adis_buffer_cleanup(void *arg) 172 { 173 struct adis *adis = arg; 174 175 kfree(adis->buffer); 176 kfree(adis->xfer); 177 } 178 179 /** 180 * devm_adis_setup_buffer_and_trigger_with_attrs() - Sets up buffer and trigger 181 * for the managed adis device with buffer attributes. 182 * @adis: The adis device 183 * @indio_dev: The IIO device 184 * @trigger_handler: Trigger handler: should handle the buffer readings. 185 * @ops: Optional buffer setup functions, may be NULL. 186 * @buffer_attrs: Extra buffer attributes. 187 * 188 * Returns 0 on success, a negative error code otherwise. 189 * 190 * This function sets up the buffer (with buffer setup functions and extra 191 * buffer attributes) and trigger for a adis devices with buffer attributes. 192 */ 193 int 194 devm_adis_setup_buffer_and_trigger_with_attrs(struct adis *adis, struct iio_dev *indio_dev, 195 irq_handler_t trigger_handler, 196 const struct iio_buffer_setup_ops *ops, 197 const struct iio_dev_attr **buffer_attrs) 198 { 199 int ret; 200 201 if (!trigger_handler) 202 trigger_handler = adis_trigger_handler; 203 204 ret = devm_iio_triggered_buffer_setup_ext(&adis->spi->dev, indio_dev, 205 &iio_pollfunc_store_time, 206 trigger_handler, 207 IIO_BUFFER_DIRECTION_IN, 208 ops, 209 buffer_attrs); 210 if (ret) 211 return ret; 212 213 if (adis->spi->irq) { 214 ret = devm_adis_probe_trigger(adis, indio_dev); 215 if (ret) 216 return ret; 217 } 218 219 return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup, 220 adis); 221 } 222 EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger_with_attrs, "IIO_ADISLIB"); 223