1ec04cb04SLars-Peter Clausen /* 2ec04cb04SLars-Peter Clausen * Common library for ADIS16XXX devices 3ec04cb04SLars-Peter Clausen * 4ec04cb04SLars-Peter Clausen * Copyright 2012 Analog Devices Inc. 5ec04cb04SLars-Peter Clausen * Author: Lars-Peter Clausen <lars@metafoo.de> 6ec04cb04SLars-Peter Clausen * 7ec04cb04SLars-Peter Clausen * Licensed under the GPL-2 or later. 8ec04cb04SLars-Peter Clausen */ 9ec04cb04SLars-Peter Clausen 10ec04cb04SLars-Peter Clausen #include <linux/export.h> 11ec04cb04SLars-Peter Clausen #include <linux/interrupt.h> 12ec04cb04SLars-Peter Clausen #include <linux/mutex.h> 13ec04cb04SLars-Peter Clausen #include <linux/kernel.h> 14ec04cb04SLars-Peter Clausen #include <linux/spi/spi.h> 15ec04cb04SLars-Peter Clausen #include <linux/slab.h> 16ec04cb04SLars-Peter Clausen 17ec04cb04SLars-Peter Clausen #include <linux/iio/iio.h> 18ec04cb04SLars-Peter Clausen #include <linux/iio/buffer.h> 19ec04cb04SLars-Peter Clausen #include <linux/iio/trigger_consumer.h> 20ec04cb04SLars-Peter Clausen #include <linux/iio/triggered_buffer.h> 21ec04cb04SLars-Peter Clausen #include <linux/iio/imu/adis.h> 22ec04cb04SLars-Peter Clausen 230e92e2d0SAlexandru Ardelean static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, 240e92e2d0SAlexandru Ardelean const unsigned long *scan_mask) 250e92e2d0SAlexandru Ardelean { 260e92e2d0SAlexandru Ardelean struct adis *adis = iio_device_get_drvdata(indio_dev); 270e92e2d0SAlexandru Ardelean unsigned int burst_length; 280e92e2d0SAlexandru Ardelean u8 *tx; 290e92e2d0SAlexandru Ardelean 300e92e2d0SAlexandru Ardelean /* All but the timestamp channel */ 310e92e2d0SAlexandru Ardelean burst_length = (indio_dev->num_channels - 1) * sizeof(u16); 320e92e2d0SAlexandru Ardelean burst_length += adis->burst->extra_len; 330e92e2d0SAlexandru Ardelean 340e92e2d0SAlexandru Ardelean adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL); 350e92e2d0SAlexandru Ardelean if (!adis->xfer) 360e92e2d0SAlexandru Ardelean return -ENOMEM; 370e92e2d0SAlexandru Ardelean 380e92e2d0SAlexandru Ardelean adis->buffer = kzalloc(burst_length + sizeof(u16), GFP_KERNEL); 390e92e2d0SAlexandru Ardelean if (!adis->buffer) 400e92e2d0SAlexandru Ardelean return -ENOMEM; 410e92e2d0SAlexandru Ardelean 420e92e2d0SAlexandru Ardelean tx = adis->buffer + burst_length; 430e92e2d0SAlexandru Ardelean tx[0] = ADIS_READ_REG(adis->burst->reg_cmd); 440e92e2d0SAlexandru Ardelean tx[1] = 0; 450e92e2d0SAlexandru Ardelean 460e92e2d0SAlexandru Ardelean adis->xfer[0].tx_buf = tx; 470e92e2d0SAlexandru Ardelean adis->xfer[0].bits_per_word = 8; 480e92e2d0SAlexandru Ardelean adis->xfer[0].len = 2; 490e92e2d0SAlexandru Ardelean adis->xfer[1].rx_buf = adis->buffer; 500e92e2d0SAlexandru Ardelean adis->xfer[1].bits_per_word = 8; 510e92e2d0SAlexandru Ardelean adis->xfer[1].len = burst_length; 520e92e2d0SAlexandru Ardelean 530e92e2d0SAlexandru Ardelean spi_message_init(&adis->msg); 540e92e2d0SAlexandru Ardelean spi_message_add_tail(&adis->xfer[0], &adis->msg); 550e92e2d0SAlexandru Ardelean spi_message_add_tail(&adis->xfer[1], &adis->msg); 560e92e2d0SAlexandru Ardelean 570e92e2d0SAlexandru Ardelean return 0; 580e92e2d0SAlexandru Ardelean } 590e92e2d0SAlexandru Ardelean 60ec04cb04SLars-Peter Clausen int adis_update_scan_mode(struct iio_dev *indio_dev, 61ec04cb04SLars-Peter Clausen const unsigned long *scan_mask) 62ec04cb04SLars-Peter Clausen { 63ec04cb04SLars-Peter Clausen struct adis *adis = iio_device_get_drvdata(indio_dev); 64ec04cb04SLars-Peter Clausen const struct iio_chan_spec *chan; 65ec04cb04SLars-Peter Clausen unsigned int scan_count; 66ec04cb04SLars-Peter Clausen unsigned int i, j; 67ec04cb04SLars-Peter Clausen __be16 *tx, *rx; 68ec04cb04SLars-Peter Clausen 69ec04cb04SLars-Peter Clausen kfree(adis->xfer); 70ec04cb04SLars-Peter Clausen kfree(adis->buffer); 71ec04cb04SLars-Peter Clausen 720e92e2d0SAlexandru Ardelean if (adis->burst && adis->burst->en) 730e92e2d0SAlexandru Ardelean return adis_update_scan_mode_burst(indio_dev, scan_mask); 740e92e2d0SAlexandru Ardelean 75ec04cb04SLars-Peter Clausen scan_count = indio_dev->scan_bytes / 2; 76ec04cb04SLars-Peter Clausen 77ec04cb04SLars-Peter Clausen adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL); 78ec04cb04SLars-Peter Clausen if (!adis->xfer) 79ec04cb04SLars-Peter Clausen return -ENOMEM; 80ec04cb04SLars-Peter Clausen 816396bb22SKees Cook adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL); 82ec04cb04SLars-Peter Clausen if (!adis->buffer) 83ec04cb04SLars-Peter Clausen return -ENOMEM; 84ec04cb04SLars-Peter Clausen 85ec04cb04SLars-Peter Clausen rx = adis->buffer; 86d590faf9SLars-Peter Clausen tx = rx + scan_count; 87ec04cb04SLars-Peter Clausen 88ec04cb04SLars-Peter Clausen spi_message_init(&adis->msg); 89ec04cb04SLars-Peter Clausen 90ec04cb04SLars-Peter Clausen for (j = 0; j <= scan_count; j++) { 91ec04cb04SLars-Peter Clausen adis->xfer[j].bits_per_word = 8; 92ec04cb04SLars-Peter Clausen if (j != scan_count) 93ec04cb04SLars-Peter Clausen adis->xfer[j].cs_change = 1; 94ec04cb04SLars-Peter Clausen adis->xfer[j].len = 2; 95ec04cb04SLars-Peter Clausen adis->xfer[j].delay_usecs = adis->data->read_delay; 96ec04cb04SLars-Peter Clausen if (j < scan_count) 97ec04cb04SLars-Peter Clausen adis->xfer[j].tx_buf = &tx[j]; 98ec04cb04SLars-Peter Clausen if (j >= 1) 99ec04cb04SLars-Peter Clausen adis->xfer[j].rx_buf = &rx[j - 1]; 100ec04cb04SLars-Peter Clausen spi_message_add_tail(&adis->xfer[j], &adis->msg); 101ec04cb04SLars-Peter Clausen } 102ec04cb04SLars-Peter Clausen 103ec04cb04SLars-Peter Clausen chan = indio_dev->channels; 104ec04cb04SLars-Peter Clausen for (i = 0; i < indio_dev->num_channels; i++, chan++) { 105ec04cb04SLars-Peter Clausen if (!test_bit(chan->scan_index, scan_mask)) 106ec04cb04SLars-Peter Clausen continue; 10757a1228aSLars-Peter Clausen if (chan->scan_type.storagebits == 32) 10857a1228aSLars-Peter Clausen *tx++ = cpu_to_be16((chan->address + 2) << 8); 109ec04cb04SLars-Peter Clausen *tx++ = cpu_to_be16(chan->address << 8); 110ec04cb04SLars-Peter Clausen } 111ec04cb04SLars-Peter Clausen 112ec04cb04SLars-Peter Clausen return 0; 113ec04cb04SLars-Peter Clausen } 114ec04cb04SLars-Peter Clausen EXPORT_SYMBOL_GPL(adis_update_scan_mode); 115ec04cb04SLars-Peter Clausen 116ec04cb04SLars-Peter Clausen static irqreturn_t adis_trigger_handler(int irq, void *p) 117ec04cb04SLars-Peter Clausen { 118ec04cb04SLars-Peter Clausen struct iio_poll_func *pf = p; 119ec04cb04SLars-Peter Clausen struct iio_dev *indio_dev = pf->indio_dev; 120ec04cb04SLars-Peter Clausen struct adis *adis = iio_device_get_drvdata(indio_dev); 121ec04cb04SLars-Peter Clausen int ret; 122ec04cb04SLars-Peter Clausen 123ec04cb04SLars-Peter Clausen if (!adis->buffer) 124ec04cb04SLars-Peter Clausen return -ENOMEM; 125ec04cb04SLars-Peter Clausen 126484a0bf0SLars-Peter Clausen if (adis->data->has_paging) { 127484a0bf0SLars-Peter Clausen mutex_lock(&adis->txrx_lock); 128484a0bf0SLars-Peter Clausen if (adis->current_page != 0) { 129484a0bf0SLars-Peter Clausen adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); 130484a0bf0SLars-Peter Clausen adis->tx[1] = 0; 131484a0bf0SLars-Peter Clausen spi_write(adis->spi, adis->tx, 2); 132484a0bf0SLars-Peter Clausen } 133484a0bf0SLars-Peter Clausen } 134484a0bf0SLars-Peter Clausen 135ec04cb04SLars-Peter Clausen ret = spi_sync(adis->spi, &adis->msg); 136ec04cb04SLars-Peter Clausen if (ret) 137ec04cb04SLars-Peter Clausen dev_err(&adis->spi->dev, "Failed to read data: %d", ret); 138ec04cb04SLars-Peter Clausen 139484a0bf0SLars-Peter Clausen 140484a0bf0SLars-Peter Clausen if (adis->data->has_paging) { 141484a0bf0SLars-Peter Clausen adis->current_page = 0; 142484a0bf0SLars-Peter Clausen mutex_unlock(&adis->txrx_lock); 143484a0bf0SLars-Peter Clausen } 144484a0bf0SLars-Peter Clausen 145419d8ce4SLars-Peter Clausen iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, 146419d8ce4SLars-Peter Clausen pf->timestamp); 147ec04cb04SLars-Peter Clausen 148ec04cb04SLars-Peter Clausen iio_trigger_notify_done(indio_dev->trig); 149ec04cb04SLars-Peter Clausen 150ec04cb04SLars-Peter Clausen return IRQ_HANDLED; 151ec04cb04SLars-Peter Clausen } 152ec04cb04SLars-Peter Clausen 153ec04cb04SLars-Peter Clausen /** 154ec04cb04SLars-Peter Clausen * adis_setup_buffer_and_trigger() - Sets up buffer and trigger for the adis device 155ec04cb04SLars-Peter Clausen * @adis: The adis device. 156ec04cb04SLars-Peter Clausen * @indio_dev: The IIO device. 157ec04cb04SLars-Peter Clausen * @trigger_handler: Optional trigger handler, may be NULL. 158ec04cb04SLars-Peter Clausen * 159ec04cb04SLars-Peter Clausen * Returns 0 on success, a negative error code otherwise. 160ec04cb04SLars-Peter Clausen * 161ec04cb04SLars-Peter Clausen * This function sets up the buffer and trigger for a adis devices. If 162ec04cb04SLars-Peter Clausen * 'trigger_handler' is NULL the default trigger handler will be used. The 163ec04cb04SLars-Peter Clausen * default trigger handler will simply read the registers assigned to the 164ec04cb04SLars-Peter Clausen * currently active channels. 165ec04cb04SLars-Peter Clausen * 166ec04cb04SLars-Peter Clausen * adis_cleanup_buffer_and_trigger() should be called to free the resources 167ec04cb04SLars-Peter Clausen * allocated by this function. 168ec04cb04SLars-Peter Clausen */ 169ec04cb04SLars-Peter Clausen int adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev, 170ec04cb04SLars-Peter Clausen irqreturn_t (*trigger_handler)(int, void *)) 171ec04cb04SLars-Peter Clausen { 172ec04cb04SLars-Peter Clausen int ret; 173ec04cb04SLars-Peter Clausen 174ec04cb04SLars-Peter Clausen if (!trigger_handler) 175ec04cb04SLars-Peter Clausen trigger_handler = adis_trigger_handler; 176ec04cb04SLars-Peter Clausen 177ec04cb04SLars-Peter Clausen ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, 178ec04cb04SLars-Peter Clausen trigger_handler, NULL); 179ec04cb04SLars-Peter Clausen if (ret) 180ec04cb04SLars-Peter Clausen return ret; 181ec04cb04SLars-Peter Clausen 182ec04cb04SLars-Peter Clausen if (adis->spi->irq) { 183ec04cb04SLars-Peter Clausen ret = adis_probe_trigger(adis, indio_dev); 184ec04cb04SLars-Peter Clausen if (ret) 185ec04cb04SLars-Peter Clausen goto error_buffer_cleanup; 186ec04cb04SLars-Peter Clausen } 187ec04cb04SLars-Peter Clausen return 0; 188ec04cb04SLars-Peter Clausen 189ec04cb04SLars-Peter Clausen error_buffer_cleanup: 190ec04cb04SLars-Peter Clausen iio_triggered_buffer_cleanup(indio_dev); 191ec04cb04SLars-Peter Clausen return ret; 192ec04cb04SLars-Peter Clausen } 193ec04cb04SLars-Peter Clausen EXPORT_SYMBOL_GPL(adis_setup_buffer_and_trigger); 194ec04cb04SLars-Peter Clausen 195ec04cb04SLars-Peter Clausen /** 196ec04cb04SLars-Peter Clausen * adis_cleanup_buffer_and_trigger() - Free buffer and trigger resources 197ec04cb04SLars-Peter Clausen * @adis: The adis device. 198ec04cb04SLars-Peter Clausen * @indio_dev: The IIO device. 199ec04cb04SLars-Peter Clausen * 200ec04cb04SLars-Peter Clausen * Frees resources allocated by adis_setup_buffer_and_trigger() 201ec04cb04SLars-Peter Clausen */ 202ec04cb04SLars-Peter Clausen void adis_cleanup_buffer_and_trigger(struct adis *adis, 203ec04cb04SLars-Peter Clausen struct iio_dev *indio_dev) 204ec04cb04SLars-Peter Clausen { 205ec04cb04SLars-Peter Clausen if (adis->spi->irq) 206ec04cb04SLars-Peter Clausen adis_remove_trigger(adis); 207ec04cb04SLars-Peter Clausen kfree(adis->buffer); 208ec04cb04SLars-Peter Clausen kfree(adis->xfer); 209ec04cb04SLars-Peter Clausen iio_triggered_buffer_cleanup(indio_dev); 210ec04cb04SLars-Peter Clausen } 211ec04cb04SLars-Peter Clausen EXPORT_SYMBOL_GPL(adis_cleanup_buffer_and_trigger); 212