diff options
Diffstat (limited to 'drivers/iio/adc/ad_sigma_delta.c')
-rw-r--r-- | drivers/iio/adc/ad_sigma_delta.c | 297 |
1 files changed, 190 insertions, 107 deletions
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 6c37f8e21120..7852884703b0 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -7,24 +7,33 @@ */ #include <linux/align.h> -#include <linux/interrupt.h> +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/completion.h> #include <linux/device.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/spi/spi.h> #include <linux/err.h> +#include <linux/export.h> +#include <linux/find.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/spi/offload/consumer.h> +#include <linux/spi/spi.h> +#include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/unaligned.h> -#include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> +#include <linux/iio/adc/ad_sigma_delta.h> +#include <linux/iio/buffer-dmaengine.h> #include <linux/iio/buffer.h> -#include <linux/iio/trigger.h> +#include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> +#include <linux/iio/trigger.h> #include <linux/iio/triggered_buffer.h> -#include <linux/iio/adc/ad_sigma_delta.h> - -#include <linux/unaligned.h> - #define AD_SD_COMM_CHAN_MASK 0x3 @@ -40,7 +49,7 @@ * @sigma_delta: The sigma delta device * @comm: New value for the communications register */ -void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm) +void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, u8 comm) { /* Some variants use the lower two bits of the communications register * to select the channel */ @@ -61,7 +70,7 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA"); int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, unsigned int val) { - uint8_t *data = sigma_delta->tx_buf; + u8 *data = sigma_delta->tx_buf; struct spi_transfer t = { .tx_buf = data, .len = size + 1, @@ -100,10 +109,18 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, } EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, "IIO_AD_SIGMA_DELTA"); +static void ad_sd_set_read_reg_addr(struct ad_sigma_delta *sigma_delta, u8 reg, + u8 *data) +{ + data[0] = reg << sigma_delta->info->addr_shift; + data[0] |= sigma_delta->info->read_mask; + data[0] |= sigma_delta->comm; +} + static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, - unsigned int reg, unsigned int size, uint8_t *val) + unsigned int reg, unsigned int size, u8 *val) { - uint8_t *data = sigma_delta->tx_buf; + u8 *data = sigma_delta->tx_buf; int ret; struct spi_transfer t[] = { { @@ -120,9 +137,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, spi_message_init(&m); if (sigma_delta->info->has_registers) { - data[0] = reg << sigma_delta->info->addr_shift; - data[0] |= sigma_delta->info->read_mask; - data[0] |= sigma_delta->comm; + ad_sd_set_read_reg_addr(sigma_delta, reg, data); spi_message_add_tail(&t[0], &m); } spi_message_add_tail(&t[1], &m); @@ -187,11 +202,11 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); int ad_sd_reset(struct ad_sigma_delta *sigma_delta) { unsigned int reset_length = sigma_delta->info->num_resetclks; - uint8_t *buf; unsigned int size; + u8 *buf; int ret; - size = DIV_ROUND_UP(reset_length, 8); + size = BITS_TO_BYTES(reset_length); buf = kcalloc(size, sizeof(*buf), GFP_KERNEL); if (!buf) return -ENOMEM; @@ -281,9 +296,7 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta if (sigma_delta->info->has_registers) { unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA; - data[0] = data_reg << sigma_delta->info->addr_shift; - data[0] |= sigma_delta->info->read_mask; - data[0] |= sigma_delta->comm; + ad_sd_set_read_reg_addr(sigma_delta, data_reg, data); t[0].tx_buf = data; spi_message_add_tail(&t[0], &m); } @@ -420,7 +433,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, data_reg = AD_SD_REG_DATA; ret = ad_sd_read_reg(sigma_delta, data_reg, - DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8), + BITS_TO_BYTES(chan->scan_type.realbits + chan->scan_type.shift), &raw_sample); out: @@ -454,9 +467,10 @@ EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, "IIO_AD_SIGMA_DELTA"); static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - unsigned int i, slot, samples_buf_size; - unsigned int channel; - uint8_t *samples_buf; + const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; + struct spi_transfer *xfer = sigma_delta->sample_xfer; + unsigned int i, slot, channel; + u8 *samples_buf; int ret; if (sigma_delta->num_slots == 1) { @@ -483,20 +497,55 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) sigma_delta->active_slots = slot; sigma_delta->current_slot = 0; - if (sigma_delta->active_slots > 1) { - ret = ad_sigma_delta_append_status(sigma_delta, true); - if (ret) - return ret; + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + xfer[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM; + xfer[1].bits_per_word = scan_type->realbits; + xfer[1].len = spi_bpw_to_bytes(scan_type->realbits); + } else { + unsigned int samples_buf_size, scan_size; + + if (sigma_delta->active_slots > 1) { + ret = ad_sigma_delta_append_status(sigma_delta, true); + if (ret) + return ret; + } + + samples_buf_size = + ALIGN(slot * BITS_TO_BYTES(scan_type->storagebits), + sizeof(s64)); + samples_buf_size += sizeof(s64); + samples_buf = devm_krealloc(&sigma_delta->spi->dev, + sigma_delta->samples_buf, + samples_buf_size, GFP_KERNEL); + if (!samples_buf) + return -ENOMEM; + + sigma_delta->samples_buf = samples_buf; + scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); + /* For 24-bit data, there is an extra byte of padding. */ + xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0]; + xfer[1].len = scan_size + (sigma_delta->status_appended ? 1 : 0); } + xfer[1].cs_change = 1; - samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits, 8); - samples_buf_size += sizeof(int64_t); - samples_buf = devm_krealloc(&sigma_delta->spi->dev, sigma_delta->samples_buf, - samples_buf_size, GFP_KERNEL); - if (!samples_buf) - return -ENOMEM; + if (sigma_delta->info->has_registers) { + xfer[0].tx_buf = &sigma_delta->sample_addr; + xfer[0].len = 1; + + ad_sd_set_read_reg_addr(sigma_delta, + sigma_delta->info->data_reg ?: AD_SD_REG_DATA, + &sigma_delta->sample_addr); + spi_message_init_with_transfers(&sigma_delta->sample_msg, xfer, 2); + } else { + spi_message_init_with_transfers(&sigma_delta->sample_msg, + &xfer[1], 1); + } + + sigma_delta->sample_msg.offload = sigma_delta->offload; - sigma_delta->samples_buf = samples_buf; + ret = spi_optimize_message(sigma_delta->spi, &sigma_delta->sample_msg); + if (ret) + return ret; spi_bus_lock(sigma_delta->spi->controller); sigma_delta->bus_locked = true; @@ -510,24 +559,42 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) if (ret) goto err_unlock; - ad_sd_enable_irq(sigma_delta); + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + struct spi_offload_trigger_config config = { + .type = SPI_OFFLOAD_TRIGGER_DATA_READY, + }; + + ret = spi_offload_trigger_enable(sigma_delta->offload, + sigma_delta->offload_trigger, + &config); + if (ret) + goto err_unlock; + } else { + ad_sd_enable_irq(sigma_delta); + } return 0; err_unlock: spi_bus_unlock(sigma_delta->spi->controller); + spi_unoptimize_message(&sigma_delta->sample_msg); return ret; } -static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) +static int ad_sd_buffer_predisable(struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - reinit_completion(&sigma_delta->completion); - wait_for_completion_timeout(&sigma_delta->completion, HZ); + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + spi_offload_trigger_disable(sigma_delta->offload, + sigma_delta->offload_trigger); + } else { + reinit_completion(&sigma_delta->completion); + wait_for_completion_timeout(&sigma_delta->completion, HZ); - ad_sd_disable_irq(sigma_delta); + ad_sd_disable_irq(sigma_delta); + } sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); @@ -537,57 +604,32 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) ad_sigma_delta_disable_all(sigma_delta); sigma_delta->bus_locked = false; - return spi_bus_unlock(sigma_delta->spi->controller); + spi_bus_unlock(sigma_delta->spi->controller); + spi_unoptimize_message(&sigma_delta->sample_msg); + + return 0; } static irqreturn_t ad_sd_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; + const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - uint8_t *data = sigma_delta->rx_buf; - unsigned int transfer_size; + u8 *data = sigma_delta->rx_buf; unsigned int sample_size; unsigned int sample_pos; unsigned int status_pos; unsigned int reg_size; - unsigned int data_reg; - - reg_size = indio_dev->channels[0].scan_type.realbits + - indio_dev->channels[0].scan_type.shift; - reg_size = DIV_ROUND_UP(reg_size, 8); - - if (sigma_delta->info->data_reg != 0) - data_reg = sigma_delta->info->data_reg; - else - data_reg = AD_SD_REG_DATA; + int ret; - /* Status word will be appended to the sample during transfer */ - if (sigma_delta->status_appended) - transfer_size = reg_size + 1; - else - transfer_size = reg_size; + reg_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); + /* For 24-bit data, there is an extra byte of padding. */ + status_pos = reg_size + (reg_size == 3 ? 1 : 0); - switch (reg_size) { - case 4: - case 2: - case 1: - status_pos = reg_size; - ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[0]); - break; - case 3: - /* - * Data array after transfer will look like (if status is appended): - * data[] = { [0][sample][sample][sample][status] } - * Keeping the first byte 0 shifts the status position by 1 byte to the right. - */ - status_pos = reg_size + 1; - - /* We store 24 bit samples in a 32 bit word. Keep the upper - * byte set to zero. */ - ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[1]); - break; - } + ret = spi_sync_locked(sigma_delta->spi, &sigma_delta->sample_msg); + if (ret) + goto irq_handled; /* * For devices sampling only one channel at @@ -613,7 +655,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) } } - sample_size = indio_dev->channels[0].scan_type.storagebits / 8; + sample_size = BITS_TO_BYTES(scan_type->storagebits); sample_pos = sample_size * sigma_delta->current_slot; memcpy(&sigma_delta->samples_buf[sample_pos], data, sample_size); sigma_delta->current_slot++; @@ -640,7 +682,7 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { .postenable = &ad_sd_buffer_postenable, - .postdisable = &ad_sd_buffer_postdisable, + .predisable = &ad_sd_buffer_predisable, .validate_scan_mask = &ad_sd_validate_scan_mask, }; @@ -667,7 +709,8 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && ad_sd_disable_irq(sigma_delta)) { complete(&sigma_delta->completion); - iio_trigger_poll(sigma_delta->trig); + if (sigma_delta->trig) + iio_trigger_poll(sigma_delta->trig); return IRQ_HANDLED; } @@ -700,17 +743,6 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de unsigned long irq_flags = irq_get_trigger_type(sigma_delta->irq_line); int ret; - if (dev != &sigma_delta->spi->dev) { - dev_err(dev, "Trigger parent should be '%s', got '%s'\n", - dev_name(dev), dev_name(&sigma_delta->spi->dev)); - return -EFAULT; - } - - sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - iio_device_id(indio_dev)); - if (sigma_delta->trig == NULL) - return -ENOMEM; - init_completion(&sigma_delta->completion); sigma_delta->irq_dis = true; @@ -730,14 +762,33 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de if (ret) return ret; - iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + sigma_delta->offload_trigger = + devm_spi_offload_trigger_get(dev, sigma_delta->offload, + SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(sigma_delta->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(sigma_delta->offload_trigger), + "Failed to get SPI offload trigger\n"); + } else { + if (dev != &sigma_delta->spi->dev) + return dev_err_probe(dev, -EFAULT, + "Trigger parent should be '%s', got '%s'\n", + dev_name(dev), dev_name(&sigma_delta->spi->dev)); - ret = devm_iio_trigger_register(dev, sigma_delta->trig); - if (ret) - return ret; + sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, iio_device_id(indio_dev)); + if (!sigma_delta->trig) + return -ENOMEM; - /* select default trigger */ - indio_dev->trig = iio_trigger_get(sigma_delta->trig); + iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); + + ret = devm_iio_trigger_register(dev, sigma_delta->trig); + if (ret) + return ret; + + /* select default trigger */ + indio_dev->trig = iio_trigger_get(sigma_delta->trig); + } return 0; } @@ -757,12 +808,29 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi if (!sigma_delta->slots) return -ENOMEM; - ret = devm_iio_triggered_buffer_setup(dev, indio_dev, - &iio_pollfunc_store_time, - &ad_sd_trigger_handler, - &ad_sd_buffer_setup_ops); - if (ret) - return ret; + if (ad_sigma_delta_has_spi_offload(sigma_delta)) { + struct dma_chan *rx_dma; + + rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, + sigma_delta->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "Failed to get RX DMA channel\n"); + + ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); + if (ret) + return dev_err_probe(dev, ret, "Cannot setup DMA buffer\n"); + + indio_dev->setup_ops = &ad_sd_buffer_setup_ops; + } else { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad_sd_trigger_handler, + &ad_sd_buffer_setup_ops); + if (ret) + return ret; + } return devm_ad_sd_probe_trigger(dev, indio_dev); } @@ -825,6 +893,20 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, return sigma_delta->irq_line; } + if (info->supports_spi_offload) { + struct spi_offload_config offload_config = { + .capability_flags = SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, + }; + int ret; + + sigma_delta->offload = devm_spi_offload_get(&spi->dev, spi, + &offload_config); + ret = PTR_ERR_OR_ZERO(sigma_delta->offload); + if (ret && ret != -ENODEV) + return dev_err_probe(&spi->dev, ret, "Failed to get SPI offload\n"); + } + iio_device_set_drvdata(indio_dev, sigma_delta); return 0; @@ -834,3 +916,4 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_init, "IIO_AD_SIGMA_DELTA"); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER"); |