diff options
Diffstat (limited to 'drivers/iio/adc/ad_sigma_delta.c')
| -rw-r--r-- | drivers/iio/adc/ad_sigma_delta.c | 532 |
1 files changed, 381 insertions, 151 deletions
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a2b87f6b7a07..7852884703b0 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -7,43 +7,55 @@ */ #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 <asm/unaligned.h> - #define AD_SD_COMM_CHAN_MASK 0x3 #define AD_SD_REG_COMM 0x00 +#define AD_SD_REG_STATUS 0x00 #define AD_SD_REG_DATA 0x03 +#define AD_SD_REG_STATUS_RDY 0x80 + /** * ad_sd_set_comm() - Set communications register * * @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 */ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; } -EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_write_reg() - Write a register @@ -58,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, @@ -95,12 +107,20 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, IIO_AD_SIGMA_DELTA); +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[] = { { @@ -109,7 +129,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, }, { .rx_buf = val, .len = size, - .cs_change = sigma_delta->bus_locked, + .cs_change = sigma_delta->keep_cs_asserted, }, }; struct spi_message m; @@ -117,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); @@ -172,24 +190,23 @@ int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, out: return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_reset() - Reset the serial interface * * @sigma_delta: The sigma delta device - * @reset_length: Number of SCLKs with DIN = 1 * * Returns 0 on success, an error code otherwise. **/ -int ad_sd_reset(struct ad_sigma_delta *sigma_delta, - unsigned int reset_length) +int ad_sd_reset(struct ad_sigma_delta *sigma_delta) { - uint8_t *buf; + unsigned int reset_length = sigma_delta->info->num_resetclks; 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; @@ -200,7 +217,106 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_reset, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_reset, "IIO_AD_SIGMA_DELTA"); + +static bool ad_sd_disable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + /* It's already off, return false to indicate nothing was changed */ + if (sigma_delta->irq_dis) + return false; + + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->irq_line); + return true; +} + +static void ad_sd_enable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->irq_line); +} + +#define AD_SD_CLEAR_DATA_BUFLEN 9 + +/* Called with `sigma_delta->bus_locked == true` only. */ +static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta) +{ + bool pending_event; + unsigned int data_read_len = BITS_TO_BYTES(sigma_delta->info->num_resetclks); + u8 *data; + struct spi_transfer t[] = { + { + .len = 1, + }, { + .len = data_read_len, + } + }; + struct spi_message m; + int ret; + + /* + * Read R̅D̅Y̅ pin (if possible) or status register to check if there is an + * old event. + */ + if (sigma_delta->rdy_gpiod) { + pending_event = gpiod_get_value(sigma_delta->rdy_gpiod); + } else { + unsigned int status_reg; + + ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_STATUS, 1, &status_reg); + if (ret) + return ret; + + pending_event = !(status_reg & AD_SD_REG_STATUS_RDY); + } + + if (!pending_event) + return 0; + + /* + * In general the size of the data register is unknown. It varies from + * device to device, might be one byte longer if CONTROL.DATA_STATUS is + * set and even varies on some devices depending on which input is + * selected. So send one byte to start reading the data register and + * then just clock for some bytes with DIN (aka MOSI) high to not + * confuse the register access state machine after the data register was + * completely read. Note however that the sequence length must be + * shorter than the reset procedure. + */ + + data = kzalloc(data_read_len + 1, GFP_KERNEL); + if (!data) + return -ENOMEM; + + spi_message_init(&m); + if (sigma_delta->info->has_registers) { + unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA; + + ad_sd_set_read_reg_addr(sigma_delta, data_reg, data); + t[0].tx_buf = data; + spi_message_add_tail(&t[0], &m); + } + + /* + * The first transferred byte is part of the real data register, + * so this doesn't need to be 0xff. In the remaining + * `data_read_len - 1` bytes are less than $num_resetclks ones. + */ + t[1].tx_buf = data + 1; + data[1] = 0x00; + memset(data + 2, 0xff, data_read_len - 1); + spi_message_add_tail(&t[1], &m); + + ret = spi_sync_locked(sigma_delta->spi, &m); + + kfree(data); + + return ret; +} int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) @@ -217,16 +333,18 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out; + ret = ad_sigma_delta_set_mode(sigma_delta, mode); if (ret < 0) goto out; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); if (time_left == 0) { - sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->irq_line); + ad_sd_disable_irq(sigma_delta); ret = -EIO; } else { ret = 0; @@ -234,12 +352,13 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, out: sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, channel); sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); return ret; } -EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_calibrate_all() - Performs channel calibration @@ -263,7 +382,7 @@ int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, return 0; } -EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_calibrate_all, "IIO_AD_SIGMA_DELTA"); /** * ad_sigma_delta_single_conversion() - Performs a single data conversion @@ -281,21 +400,25 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, unsigned int data_reg; int ret = 0; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - ad_sigma_delta_set_channel(sigma_delta, chan->address); + ret = ad_sigma_delta_set_channel(sigma_delta, chan->address); + if (ret) + goto out_release; spi_bus_lock(sigma_delta->spi->controller); sigma_delta->bus_locked = true; sigma_delta->keep_cs_asserted = true; reinit_completion(&sigma_delta->completion); + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto out_unlock; + ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); @@ -310,20 +433,21 @@ 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: - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); - sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); + ad_sigma_delta_disable_one(sigma_delta, chan->address); + +out_unlock: + sigma_delta->keep_cs_asserted = false; sigma_delta->bus_locked = false; spi_bus_unlock(sigma_delta->spi->controller); - iio_device_release_direct_mode(indio_dev); +out_release: + iio_device_release_direct(indio_dev); if (ret) return ret; @@ -338,19 +462,20 @@ out: return IIO_VAL_INT; } -EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, IIO_AD_SIGMA_DELTA); +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) { channel = find_first_bit(indio_dev->active_scan_mask, - indio_dev->masklength); + iio_get_masklength(indio_dev)); ret = ad_sigma_delta_set_channel(sigma_delta, indio_dev->channels[channel].address); if (ret) @@ -363,7 +488,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) * implementation is mandatory. */ slot = 0; - for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) { + iio_for_each_active_channel(indio_dev, i) { sigma_delta->slots[slot] = indio_dev->channels[i].address; slot++; } @@ -372,50 +497,103 @@ 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; sigma_delta->keep_cs_asserted = true; + ret = ad_sigma_delta_clear_pending_event(sigma_delta); + if (ret) + goto err_unlock; + ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS); if (ret) goto err_unlock; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + 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); - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; + ad_sd_disable_irq(sigma_delta); } sigma_delta->keep_cs_asserted = false; @@ -426,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; - - /* 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; + int ret; - 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 postion by 1 byte to the right. - */ - status_pos = reg_size + 1; + 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); - /* 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 @@ -502,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++; @@ -515,8 +668,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) irq_handled: iio_trigger_notify_done(indio_dev->trig); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); return IRQ_HANDLED; } @@ -525,12 +677,12 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - return bitmap_weight(mask, indio_dev->masklength) <= sigma_delta->num_slots; + return bitmap_weight(mask, iio_get_masklength(indio_dev)) <= sigma_delta->num_slots; } 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, }; @@ -538,12 +690,32 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) { struct ad_sigma_delta *sigma_delta = private; - complete(&sigma_delta->completion); - disable_irq_nosync(irq); - sigma_delta->irq_dis = true; - iio_trigger_poll(sigma_delta->trig); + /* + * AD7124 and a few others use the same physical line for interrupt + * reporting (R̅D̅Y̅) and MISO. + * As MISO toggles when reading a register, this likely results in a + * pending interrupt. This has two consequences: a) The irq might + * trigger immediately after it's enabled even though the conversion + * isn't done yet; and b) checking the STATUS register's R̅D̅Y̅ flag is + * off-limits as reading that would trigger another irq event. + * + * So read the MOSI line as GPIO (if available) and only trigger the irq + * if the line is active. Without such a GPIO assume this is a valid + * interrupt. + * + * Also as disable_irq_nosync() is used to disable the irq, only act if + * the irq wasn't disabled before. + */ + if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && + ad_sd_disable_irq(sigma_delta)) { + complete(&sigma_delta->completion); + if (sigma_delta->trig) + iio_trigger_poll(sigma_delta->trig); - return IRQ_HANDLED; + return IRQ_HANDLED; + } + + return IRQ_NONE; } /** @@ -563,25 +735,14 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) return 0; } -EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, "IIO_AD_SIGMA_DELTA"); static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - unsigned long irq_flags = irq_get_trigger_type(sigma_delta->spi->irq); + 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; @@ -601,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; + + 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); + /* select default trigger */ + indio_dev->trig = iio_trigger_get(sigma_delta->trig); + } return 0; } @@ -628,16 +808,33 @@ 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); } -EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, IIO_AD_SIGMA_DELTA); +EXPORT_SYMBOL_NS_GPL(devm_ad_sd_setup_buffer_and_trigger, "IIO_AD_SIGMA_DELTA"); /** * ad_sd_init() - Initializes a ad_sigma_delta struct @@ -655,7 +852,7 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, sigma_delta->spi = spi; sigma_delta->info = info; - /* If the field is unset in ad_sigma_delta_info, asume there can only be 1 slot. */ + /* If the field is unset in ad_sigma_delta_info, assume there can only be 1 slot. */ if (!info->num_slots) sigma_delta->num_slots = 1; else @@ -673,17 +870,50 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, } } - if (info->irq_line) - sigma_delta->irq_line = info->irq_line; - else + spin_lock_init(&sigma_delta->irq_lock); + + if (info->has_named_irqs) { + sigma_delta->irq_line = fwnode_irq_get_byname(dev_fwnode(&spi->dev), + "rdy"); + if (sigma_delta->irq_line < 0) + return dev_err_probe(&spi->dev, sigma_delta->irq_line, + "Interrupt 'rdy' is required\n"); + } else { sigma_delta->irq_line = spi->irq; + } + + sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN); + if (IS_ERR(sigma_delta->rdy_gpiod)) + return dev_err_probe(&spi->dev, PTR_ERR(sigma_delta->rdy_gpiod), + "Failed to find rdy gpio\n"); + + if (sigma_delta->rdy_gpiod && !sigma_delta->irq_line) { + sigma_delta->irq_line = gpiod_to_irq(sigma_delta->rdy_gpiod); + if (sigma_delta->irq_line < 0) + 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; } -EXPORT_SYMBOL_NS_GPL(ad_sd_init, IIO_AD_SIGMA_DELTA); +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"); |
