From a09b5adb8f7151f90ee16ef008983ace078d6d57 Mon Sep 17 00:00:00 2001 From: Purva Yeshi Date: Thu, 10 Apr 2025 22:34:08 +0530 Subject: [PATCH 01/28] iio: adc: ad_sigma_delta: Fix use of uninitialized status_pos Fix Smatch-detected issue: drivers/iio/adc/ad_sigma_delta.c:604 ad_sd_trigger_handler() error: uninitialized symbol 'status_pos'. The variable `status_pos` was only initialized in specific switch cases (1, 2, 3, 4), which could leave it uninitialized if `reg_size` had an unexpected value. Fix by adding a default case to the switch block to catch unexpected values of `reg_size`. Use `dev_err_ratelimited()` for error logging and `goto irq_handled` instead of returning early. Signed-off-by: Purva Yeshi Link: https://patch.msgid.link/20250410170408.8585-1-purvayeshi550@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 1bdfc813567ec0..16e0964c722808 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -648,6 +648,10 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) * byte set to zero. */ ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[1]); break; + + default: + dev_err_ratelimited(&indio_dev->dev, "Unsupported reg_size: %u\n", reg_size); + goto irq_handled; } /* From 06c9d2cba18505a2bf431286c3857c620d92d0fc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:49 -0500 Subject: [PATCH 02/28] iio: adc: ad_sigma_delta: don't overallocate scan buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix overallocating the size of the scan buffer by converting bits to bytes. The size is meant to be in bytes, so scanbits needs to be divided by 8. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-1-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 16e0964c722808..ee0a2fc3ea3ba5 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -538,7 +538,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return ret; } - samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits, 8); + samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits / 8, 8); samples_buf_size += sizeof(int64_t); samples_buf = devm_krealloc(&sigma_delta->spi->dev, sigma_delta->samples_buf, samples_buf_size, GFP_KERNEL); From e2451cd0b4e790a8cdfc8964d114eec12700b305 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:50 -0500 Subject: [PATCH 03/28] iio: adc: ad_sigma_delta: sort includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort includes in alphabetical order and fix grouping before we add more. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-2-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index ee0a2fc3ea3ba5..a8fe8481bde98e 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -7,24 +7,22 @@ */ #include -#include #include +#include +#include #include +#include #include #include -#include -#include +#include +#include +#include #include #include -#include -#include #include +#include #include -#include - -#include - #include #define AD_SD_COMM_CHAN_MASK 0x3 From 483825675f180121a2786c59832e5a9e6cde92ee Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:51 -0500 Subject: [PATCH 04/28] iio: adc: ad_sigma_delta: use u8 instead of uint8_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace uint8_t with u8 in the ad_sigma_delta driver. Technically, uint8_t comes from the C standard library, while u8 is a Linux kernel type. Since we don't use the C standard library in the kernel, we should use the kernel types instead. There is also one instance where int64_t is replaced with s64. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-3-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 17 +++++++++-------- include/linux/iio/adc/ad_sigma_delta.h | 10 +++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a8fe8481bde98e..02f39d8913e598 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -39,7 +40,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 */ @@ -60,7 +61,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,9 +101,9 @@ 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 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[] = { { @@ -186,8 +187,8 @@ 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); @@ -502,7 +503,7 @@ 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; + u8 *samples_buf; int ret; if (sigma_delta->num_slots == 1) { @@ -537,7 +538,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) } samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits / 8, 8); - samples_buf_size += sizeof(int64_t); + 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) @@ -604,7 +605,7 @@ 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; struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - uint8_t *data = sigma_delta->rx_buf; + u8 *data = sigma_delta->rx_buf; unsigned int transfer_size; unsigned int sample_size; unsigned int sample_pos; diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 33880e494bea1f..11184400bad87d 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -94,7 +94,7 @@ struct ad_sigma_delta { bool bus_locked; bool keep_cs_asserted; - uint8_t comm; + u8 comm; const struct ad_sigma_delta_info *info; unsigned int active_slots; @@ -105,7 +105,7 @@ struct ad_sigma_delta { bool status_appended; /* map slots to channels in order to know what to expect from devices */ unsigned int *slots; - uint8_t *samples_buf; + u8 *samples_buf; /* * DMA (thus cache coherency maintenance) requires the @@ -114,8 +114,8 @@ struct ad_sigma_delta { * 'rx_buf' is up to 32 bits per sample + 64 bit timestamp, * rounded to 16 bytes to take into account padding. */ - uint8_t tx_buf[4] __aligned(IIO_DMA_MINALIGN); - uint8_t rx_buf[16] __aligned(8); + u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN); + u8 rx_buf[16] __aligned(8); }; static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd, @@ -177,7 +177,7 @@ static inline int ad_sigma_delta_postprocess_sample(struct ad_sigma_delta *sd, return 0; } -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); int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, unsigned int val); int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, From d527c57bd358afa2981bda534c886756a89bf5d1 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:52 -0500 Subject: [PATCH 05/28] iio: adc: ad_sigma_delta: use sizeof() in ALIGN() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use sizeof() instead of hardcoding the size of the timestamp in the ALIGN() macro. This makes it a bit more obvious what the intention of the code is. Suggested-by: Andy Shevchenko Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-4-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 02f39d8913e598..81dbafdd8494f4 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -537,7 +537,8 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return ret; } - samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits / 8, 8); + samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits / 8, + sizeof(s64)); samples_buf_size += sizeof(s64); samples_buf = devm_krealloc(&sigma_delta->spi->dev, sigma_delta->samples_buf, samples_buf_size, GFP_KERNEL); From 1dce75cd2dbd3c1e1f83361c36c0623db99d00d3 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:53 -0500 Subject: [PATCH 06/28] iio: adc: ad_sigma_delta: use BITS_TO_BYTES() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the BITS_TO_BYTES() macro instead of dividing by 8 to convert bits to bytes. This makes it more obvious what unit conversion is taking place. In one instance, we also avoid the temporary assignment to a variable as it was confusing that reg_size was being used with two different units (bits and bytes). scan_type is factored out to reduce line wrapping. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-5-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 81dbafdd8494f4..3d08a14ae1b8a2 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -191,7 +192,7 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta) 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; @@ -421,7 +422,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: @@ -501,6 +502,7 @@ static void ad_sd_prepare_and_enable_spi_engine_msg(struct ad_sigma_delta *sigma static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type; unsigned int i, slot, samples_buf_size; unsigned int channel; u8 *samples_buf; @@ -537,7 +539,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) return ret; } - samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits / 8, + 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, @@ -605,6 +607,7 @@ 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); u8 *data = sigma_delta->rx_buf; unsigned int transfer_size; @@ -614,9 +617,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) 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); + reg_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift); if (sigma_delta->info->data_reg != 0) data_reg = sigma_delta->info->data_reg; @@ -678,7 +679,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++; From 4411fac509ab63f01f799d7c23c7f335ae807f16 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:54 -0500 Subject: [PATCH 07/28] iio: adc: ad_sigma_delta: audit included headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop linux/iio/sysfs.h since it is unused and replace linux/kernel.h with more explicit headers. There are a couple of other headers added weren't covered by kernel.h, like linux/gpio/consumer.h that are added since the module makes use of those APIs as well. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-6-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 3d08a14ae1b8a2..a64b7f1e835511 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -7,21 +7,28 @@ */ #include +#include #include +#include +#include #include #include +#include +#include +#include #include -#include #include +#include #include #include +#include +#include #include #include #include #include #include -#include #include #include #include From 27b7b7aa1dfc87afa3286fe60976c82deab821b4 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:55 -0500 Subject: [PATCH 08/28] iio: adc: ad_sigma_delta: refactor setting read address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor code to set the read address in a separate function. This code is already duplicated twice and we will need to use it a third time in a later commit. Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-7-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a64b7f1e835511..58e60a1dbb05d8 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -108,6 +108,14 @@ 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, u8 *val) { @@ -128,9 +136,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); @@ -289,9 +295,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); } From f42a157dc266020db524f1fdb0d7659f509da159 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:56 -0500 Subject: [PATCH 09/28] iio: adc: ad_sigma_delta: use spi_optimize_message() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use spi_optimize_message() to improve the performance of buffered reads. By setting up the SPI message and pre-optimizing it in the buffer postenable callback, we can reduce overhead during each sample read. A rough estimate shows that this reduced the CPU usage of the interrupt handler thread from 22% to 16% using an EVAL-AD4112ARDZ board on a DE10-Nano (measuring a single channel at the default 6.2 kHz sample rate). Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-8-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 72 ++++++++++++-------------- include/linux/iio/adc/ad_sigma_delta.h | 3 ++ 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 58e60a1dbb05d8..f9f35383b35090 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -514,8 +514,9 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); 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, samples_buf_size; - unsigned int channel; + unsigned int channel, scan_size; u8 *samples_buf; int ret; @@ -559,6 +560,28 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) 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; + + 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); + } + + 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; @@ -586,6 +609,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) err_unlock: spi_bus_unlock(sigma_delta->spi->controller); + spi_unoptimize_message(&sigma_delta->sample_msg); return ret; } @@ -611,7 +635,10 @@ 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) @@ -621,50 +648,19 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) 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); u8 *data = sigma_delta->rx_buf; - unsigned int transfer_size; unsigned int sample_size; unsigned int sample_pos; unsigned int status_pos; unsigned int reg_size; - unsigned int data_reg; + int ret; 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); - 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; - - 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; - - /* 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; - - default: - dev_err_ratelimited(&indio_dev->dev, "Unsupported reg_size: %u\n", reg_size); + ret = spi_sync_locked(sigma_delta->spi, &sigma_delta->sample_msg); + if (ret) goto irq_handled; - } /* * For devices sampling only one channel at diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 11184400bad87d..7fbd9ce2927e4d 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -105,6 +105,8 @@ struct ad_sigma_delta { bool status_appended; /* map slots to channels in order to know what to expect from devices */ unsigned int *slots; + struct spi_message sample_msg; + struct spi_transfer sample_xfer[2]; u8 *samples_buf; /* @@ -116,6 +118,7 @@ struct ad_sigma_delta { */ u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN); u8 rx_buf[16] __aligned(8); + u8 sample_addr; }; static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd, From 7beaef6379e97c2e0e304bb0b7cd25f5346ea759 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 1 Jul 2025 16:37:59 -0500 Subject: [PATCH 10/28] iio: adc: ad_sigma_delta: add SPI offload support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add SPI offload support to the ad_sigma_delta module. When the SPI controller has SPI offload capabilities, the module will now use that for buffered reads instead of the RDY interrupt trigger. Drivers that use the ad_sigma_delta module will have to opt into this by setting supports_spi_offload since each driver will likely need additional changes before SPI offload can be used. This will allow us to gradually enable SPI offload support for each driver. [marcelo.schmitt@analog.com: adapted to kernel 6.12] Signed-off-by: David Lechner Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250701-iio-adc-ad7173-add-spi-offload-support-v3-11-42abb83e3dac@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 163 +++++++++++++++++-------- include/linux/iio/adc/ad_sigma_delta.h | 14 +++ 2 files changed, 126 insertions(+), 51 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index f9f35383b35090..d13919794a7d92 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include +#include #include #include #include @@ -515,8 +517,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); 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, samples_buf_size; - unsigned int channel, scan_size; + unsigned int i, slot, channel; u8 *samples_buf; int ret; @@ -544,26 +545,35 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) sigma_delta->active_slots = slot; sigma_delta->current_slot = 0; - if (iio_device_get_current_mode(indio_dev) != INDIO_BUFFER_HARDWARE && - 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 = roundup_pow_of_two(BITS_TO_BYTES(scan_type->realbits)); + } else { + unsigned int samples_buf_size, scan_size; - 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; + if (sigma_delta->active_slots > 1) { + ret = ad_sigma_delta_append_status(sigma_delta, true); + if (ret) + return ret; + } - 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); + 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; if (sigma_delta->info->has_registers) { @@ -579,6 +589,8 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) &xfer[1], 1); } + sigma_delta->sample_msg.offload = sigma_delta->offload; + ret = spi_optimize_message(sigma_delta->spi, &sigma_delta->sample_msg); if (ret) return ret; @@ -598,12 +610,19 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) if (ret) goto err_unlock; - /* - * Differs from upstream because of the spi engine support and we want to enable - * the irq only after setting AD_SD_MODE_CONTINUOUS (as in the upstream lib) - */ - if (iio_device_get_current_mode(indio_dev) != INDIO_BUFFER_HARDWARE) + 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; @@ -618,8 +637,9 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - if (iio_device_get_current_mode(indio_dev) == INDIO_BUFFER_HARDWARE) { - legacy_spi_engine_offload_enable(sigma_delta->spi, false); + 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); @@ -740,7 +760,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; } @@ -773,17 +794,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; @@ -803,14 +813,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; } @@ -833,12 +862,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); } @@ -901,6 +947,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; @@ -910,3 +970,4 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_init, IIO_AD_SIGMA_DELTA); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER); diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 7fbd9ce2927e4d..b74bf26f44ae77 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -31,6 +31,8 @@ struct ad_sigma_delta; struct device; struct gpio_desc; struct iio_dev; +struct spi_offload; +struct spi_offload_trigger; /** * struct ad_sigma_delta_info - Sigma Delta driver specific callbacks and options @@ -47,6 +49,10 @@ struct iio_dev; * @has_registers: true if the device has writable and readable registers, false * if there is just one read-only sample data shift register. * @has_named_irqs: Set to true if there is more than one IRQ line. + * @supports_spi_offload: Set to true if the driver supports SPI offload. Often + * special considerations are needed for scan_type and other channel + * info, so individual drivers have to set this to let the core + * code know that it can use SPI offload if it is available. * @addr_shift: Shift of the register address in the communications register. * @read_mask: Mask for the communications register having the read bit set. * @status_ch_mask: Mask for the channel number stored in status register. @@ -65,6 +71,7 @@ struct ad_sigma_delta_info { int (*postprocess_sample)(struct ad_sigma_delta *, unsigned int raw_sample); bool has_registers; bool has_named_irqs; + bool supports_spi_offload; unsigned int addr_shift; unsigned int read_mask; unsigned int status_ch_mask; @@ -108,6 +115,8 @@ struct ad_sigma_delta { struct spi_message sample_msg; struct spi_transfer sample_xfer[2]; u8 *samples_buf; + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; /* * DMA (thus cache coherency maintenance) requires the @@ -121,6 +130,11 @@ struct ad_sigma_delta { u8 sample_addr; }; +static inline bool ad_sigma_delta_has_spi_offload(struct ad_sigma_delta *sd) +{ + return sd->offload != NULL; +} + static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd, unsigned int channel) { From 845c55263e3902a5cd1ec727ac2151f0e0f24609 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 3 Jul 2025 16:07:44 -0500 Subject: [PATCH 11/28] iio: adc: ad_sigma_delta: change to buffer predisable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the buffer disable callback from postdisable to predisable. This balances the existing posteanble callback. Using postdisable with posteanble can be problematic, for example, if update_scan_mode fails, it would call postdisable without ever having called posteanble, so the drivers using this would be in an unexpected state when postdisable was called. Fixes: af3008485ea0 ("iio:adc: Add common code for ADI Sigma Delta devices") Signed-off-by: David Lechner Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250703-iio-adc-ad_sigma_delta-buffer-predisable-v1-1-f2ab85138f1f@baylibre.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index d13919794a7d92..92107395e90c08 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -633,7 +633,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) 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); @@ -733,7 +733,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, }; From b7e6f6e2d4eea185c0e2ef34f5aeb52580cbdb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 27 Feb 2025 12:35:27 +0100 Subject: [PATCH 12/28] iio: adc: ad7173: Grab direct mode for calibration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While a calibration is running, better don't make the device do anything else. To enforce that, grab direct mode during calibration. Fixes: 031bdc8aee01 ("iio: adc: ad7173: add calibration support") Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/8319fa2dc881c9899d60db4eba7fe8e984716617.1740655250.git.u.kleine-koenig@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index d45d8a94d3311b..55594f6fde3535 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -350,6 +350,9 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, if (ret) return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + mode = st->channels[chan->channel].syscalib_mode; if (sys_calib) { if (mode == AD7173_SYSCALIB_ZERO_SCALE) @@ -360,6 +363,8 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, chan->address); } + iio_device_release_direct(indio_dev); + return ret ? : len; } From ed725fbb170403fa122c8653124c40bbcb8798d2 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 22 Apr 2025 15:12:27 -0500 Subject: [PATCH 13/28] iio: adc: ad7173: fix compiling without gpiolib Fix compiling the ad7173 driver when CONFIG_GPIOLIB is not set by selecting GPIOLIB to be always enabled and remove the #if. Commit 031bdc8aee01 ("iio: adc: ad7173: add calibration support") placed unrelated code in the middle of the #if IS_ENABLED(CONFIG_GPIOLIB) block which caused the reported compile error. However, later commit 7530ed2aaa3f ("iio: adc: ad7173: add openwire detection support for single conversions") makes use of the gpio regmap even when we aren't providing gpio controller support. So it makes more sense to always enable GPIOLIB rather than trying to make it optional. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202504220824.HVrTVov1-lkp@intel.com/ Fixes: 031bdc8aee01 ("iio: adc: ad7173: add calibration support") Signed-off-by: David Lechner Link: https://patch.msgid.link/20250422-iio-adc-ad7173-fix-compile-without-gpiolib-v1-1-295f2c990754@baylibre.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 5 +++-- drivers/iio/adc/ad7173.c | 15 +-------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index cae86f7837e06a..b9ff9727821b96 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -174,8 +174,9 @@ config AD7173 tristate "Analog Devices AD7173 driver" depends on SPI_MASTER select AD_SIGMA_DELTA - select GPIO_REGMAP if GPIOLIB - select REGMAP_SPI if GPIOLIB + select GPIOLIB + select GPIO_REGMAP + select REGMAP_SPI help Say yes here to build support for Analog Devices AD7173 and similar ADC Currently supported models: diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 55594f6fde3535..31a514d56226b2 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -231,10 +231,8 @@ struct ad7173_state { unsigned long long *config_cnts; struct clk *ext_clk; struct clk_hw int_clk_hw; -#if IS_ENABLED(CONFIG_GPIOLIB) struct regmap *reg_gpiocon_regmap; struct gpio_regmap *gpio_regmap; -#endif }; static unsigned int ad4115_sinc5_data_rates[] = { @@ -289,8 +287,6 @@ static const char *const ad7173_clk_sel[] = { "ext-clk", "xtal" }; -#if IS_ENABLED(CONFIG_GPIOLIB) - static const struct regmap_range ad7173_range_gpio[] = { regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO), }; @@ -544,12 +540,6 @@ static int ad7173_gpio_init(struct ad7173_state *st) return 0; } -#else -static int ad7173_gpio_init(struct ad7173_state *st) -{ - return 0; -} -#endif /* CONFIG_GPIOLIB */ static struct ad7173_state *ad_sigma_delta_to_ad7173(struct ad_sigma_delta *sd) { @@ -1800,10 +1790,7 @@ static int ad7173_probe(struct spi_device *spi) if (ret) return ret; - if (IS_ENABLED(CONFIG_GPIOLIB)) - return ad7173_gpio_init(st); - - return 0; + return ad7173_gpio_init(st); } static const struct of_device_id ad7173_of_match[] = { From 33e2e2bb9a1b72ddf201bdc5f9ff019bae773a13 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 11 Jun 2025 09:40:16 -0500 Subject: [PATCH 14/28] iio: adc: ad7173: check return value of spi_setup() Check the return value of spi_setup() and propagate the error in the ad7173_probe() function. This is unlikely to happen since virtually every SPI controller supports SPI_MODE_3, but still always a good idea to check the return value. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250611-iio-adc-ad7173-check-spi_setup-return-v1-1-4d6f9ef0a2e4@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 31a514d56226b2..8cf8eaaae83301 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -1768,7 +1768,9 @@ static int ad7173_probe(struct spi_device *spi) indio_dev->info = &ad7173_info; spi->mode = SPI_MODE_3; - spi_setup(spi); + ret = spi_setup(spi); + if (ret) + return ret; ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info); if (ret) From 2f247595d5af3866e7a43228d5f8ff0e92cb6392 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 20 Jun 2025 09:30:46 -0500 Subject: [PATCH 15/28] iio: adc: ad7173: simplify clock enable/disable Use devm_clk_get_enabled() instead of devm_clk_get(), clk_prepare_enable(), devm_add_action_or_reset() to simplify the code as it effectively does the same thing. We can also drop ext_clk from struct ad7173_state since it is not used anywhere else. Signed-off-by: David Lechner Link: https://patch.msgid.link/20250620-iio-adc-ad7173-simplify-clock-enable-disable-v1-1-8bc693b190ec@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 8cf8eaaae83301..4736b1c5d8a3bd 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -229,7 +229,6 @@ struct ad7173_state { struct ida cfg_slots_status; unsigned long long config_usage_counter; unsigned long long *config_cnts; - struct clk *ext_clk; struct clk_hw int_clk_hw; struct regmap *reg_gpiocon_regmap; struct gpio_regmap *gpio_regmap; @@ -1346,11 +1345,6 @@ static void ad7173_disable_regulators(void *data) regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); } -static void ad7173_clk_disable_unprepare(void *clk) -{ - clk_disable_unprepare(clk); -} - static unsigned long ad7173_sel_clk(struct ad7173_state *st, unsigned int clk_sel) { @@ -1721,22 +1715,14 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) AD7173_ADC_MODE_CLOCKSEL_INT); ad7173_register_clk_provider(indio_dev); } else { + struct clk *clk; + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, AD7173_ADC_MODE_CLOCKSEL_EXT + ret); - st->ext_clk = devm_clk_get(dev, ad7173_clk_sel[ret]); - if (IS_ERR(st->ext_clk)) - return dev_err_probe(dev, PTR_ERR(st->ext_clk), + clk = devm_clk_get_enabled(dev, ad7173_clk_sel[ret]); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Failed to get external clock\n"); - - ret = clk_prepare_enable(st->ext_clk); - if (ret) - return dev_err_probe(dev, ret, - "Failed to enable external clock\n"); - - ret = devm_add_action_or_reset(dev, ad7173_clk_disable_unprepare, - st->ext_clk); - if (ret) - return ret; } return ad7173_fw_parse_channel_config(indio_dev); From 65d36ec111568aba4560e645b054fa257d0322df Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 3 Jul 2025 14:51:17 -0500 Subject: [PATCH 16/28] iio: adc: ad7173: fix channels index for syscalib_mode Fix the index used to look up the channel when accessing the syscalib_mode attribute. The address field is a 0-based index (same as scan_index) that it used to access the channel in the ad7173_channels array throughout the driver. The channels field, on the other hand, may not match the address field depending on the channel configuration specified in the device tree and could result in an out-of-bounds access. Fixes: 031bdc8aee01 ("iio: adc: ad7173: add calibration support") Signed-off-by: David Lechner Link: https://patch.msgid.link/20250703-iio-adc-ad7173-fix-channels-index-for-syscalib_mode-v1-1-7fdaedb9cac0@baylibre.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 4736b1c5d8a3bd..44d9771b69a75c 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -319,7 +319,7 @@ static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev, { struct ad7173_state *st = iio_priv(indio_dev); - st->channels[chan->channel].syscalib_mode = mode; + st->channels[chan->address].syscalib_mode = mode; return 0; } @@ -329,7 +329,7 @@ static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev, { struct ad7173_state *st = iio_priv(indio_dev); - return st->channels[chan->channel].syscalib_mode; + return st->channels[chan->address].syscalib_mode; } static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, @@ -348,7 +348,7 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev, if (!iio_device_claim_direct(indio_dev)) return -EBUSY; - mode = st->channels[chan->channel].syscalib_mode; + mode = st->channels[chan->address].syscalib_mode; if (sys_calib) { if (mode == AD7173_SYSCALIB_ZERO_SCALE) ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO, From 50f8088e3e815f8f3208282d7cd03fae0e954796 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 6 Jul 2025 13:53:08 -0500 Subject: [PATCH 17/28] iio: adc: ad7173: fix num_slots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the num_slots value for most chips in the ad7173 driver. The correct value is the number of CHANNELx registers on the chip. In commit 4310e15b3140 ("iio: adc: ad7173: don't make copy of ad_sigma_delta_info struct"), we refactored struct ad_sigma_delta_info to be static const data instead of being dynamically populated during driver probe. However, there was an existing bug in commit 76a1e6a42802 ("iio: adc: ad7173: add AD7173 driver") where num_slots was incorrectly set to the number of CONFIGx registers instead of the number of CHANNELx registers. This bug was partially propagated to the refactored code in that the 16-channel chips were only given 8 slots instead of 16 although we did managed to fix the 8-channel chips and one of the 4-channel chips in that commit. However, we botched two of the 4-channel chips and ended up incorrectly giving them 8 slots during the refactoring. This patch fixes that mistake on the 4-channel chips and also corrects the 16-channel chips to have 16 slots. Fixes: 4310e15b3140 ("iio: adc: ad7173: don't make copy of ad_sigma_delta_info struct") Signed-off-by: David Lechner Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250706-iio-adc-ad7173-fix-num_slots-on-most-chips-v3-1-d1f5453198a7@baylibre.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 44d9771b69a75c..5755eeeefb59e2 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -772,10 +772,26 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = { .num_slots = 8, }; +static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .disable_one = ad7173_disable_one, + .set_mode = ad7173_set_mode, + .has_registers = true, + .has_named_irqs = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, + .num_slots = 16, +}; + static const struct ad7173_device_info ad4111_device_info = { .name = "ad4111", .id = AD4111_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 8, .num_channels = 16, .num_configs = 8, @@ -797,7 +813,7 @@ static const struct ad7173_device_info ad4111_device_info = { static const struct ad7173_device_info ad4112_device_info = { .name = "ad4112", .id = AD4112_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 8, .num_channels = 16, .num_configs = 8, @@ -818,7 +834,7 @@ static const struct ad7173_device_info ad4112_device_info = { static const struct ad7173_device_info ad4113_device_info = { .name = "ad4113", .id = AD4113_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 8, .num_channels = 16, .num_configs = 8, @@ -837,7 +853,7 @@ static const struct ad7173_device_info ad4113_device_info = { static const struct ad7173_device_info ad4114_device_info = { .name = "ad4114", .id = AD4114_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 16, .num_channels = 16, .num_configs = 8, @@ -856,7 +872,7 @@ static const struct ad7173_device_info ad4114_device_info = { static const struct ad7173_device_info ad4115_device_info = { .name = "ad4115", .id = AD4115_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 16, .num_channels = 16, .num_configs = 8, @@ -875,7 +891,7 @@ static const struct ad7173_device_info ad4115_device_info = { static const struct ad7173_device_info ad4116_device_info = { .name = "ad4116", .id = AD4116_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in_div = 11, .num_channels = 16, .num_configs = 8, @@ -894,7 +910,7 @@ static const struct ad7173_device_info ad4116_device_info = { static const struct ad7173_device_info ad7172_2_device_info = { .name = "ad7172-2", .id = AD7172_2_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_4_slots, .num_voltage_in = 5, .num_channels = 4, .num_configs = 4, @@ -927,7 +943,7 @@ static const struct ad7173_device_info ad7172_4_device_info = { static const struct ad7173_device_info ad7173_8_device_info = { .name = "ad7173-8", .id = AD7173_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in = 17, .num_channels = 16, .num_configs = 8, @@ -944,7 +960,7 @@ static const struct ad7173_device_info ad7173_8_device_info = { static const struct ad7173_device_info ad7175_2_device_info = { .name = "ad7175-2", .id = AD7175_2_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_4_slots, .num_voltage_in = 5, .num_channels = 4, .num_configs = 4, @@ -961,7 +977,7 @@ static const struct ad7173_device_info ad7175_2_device_info = { static const struct ad7173_device_info ad7175_8_device_info = { .name = "ad7175-8", .id = AD7175_8_ID, - .sd_info = &ad7173_sigma_delta_info_8_slots, + .sd_info = &ad7173_sigma_delta_info_16_slots, .num_voltage_in = 17, .num_channels = 16, .num_configs = 8, From 1581a13c38ffd9b2ef591ac0747a2c6de12b8edb Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 8 Jul 2025 20:38:33 -0500 Subject: [PATCH 18/28] iio: adc: ad7173: fix calibration channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the channel index values passed to ad_sd_calibrate() in ad7173_calibrate_all(). ad7173_calibrate_all() expects these values to be that of the CHANNELx register assigned to the channel, not the datasheet INPUTx number of the channel. The incorrect values were causing register writes to fail for some channels because they set the WEN bit that must always be 0 for register access and set the R/W bit to read instead of write. For other channels, the channel number was just wrong because the CHANNELx registers are generally assigned in reverse order and so almost never match the INPUTx numbers. Fixes: 031bdc8aee01 ("iio: adc: ad7173: add calibration support") Signed-off-by: David Lechner Reviewed-by: Nuno Sá Link: https://patch.msgid.link/20250708-iio-adc-ad7313-fix-calibration-channel-v1-1-e6174e2c7cbf@baylibre.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 5755eeeefb59e2..3a5e4f2407b529 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -392,13 +392,12 @@ static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_d if (indio_dev->channels[i].type != IIO_VOLTAGE) continue; - ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain); + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, i); if (ret < 0) return ret; if (st->info->has_internal_fs_calibration) { - ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, - st->channels[i].ain); + ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, i); if (ret < 0) return ret; } From c6845632d665dc40f175799a25aee2e65a038b1e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 10 Jul 2025 15:43:40 -0500 Subject: [PATCH 19/28] iio: adc: ad7173: fix setting ODR in probe Fix the setting of the ODR register value in the probe function for AD7177. The AD7177 chip has a different ODR value after reset than the other chips (0x7 vs. 0x0) and 0 is a reserved value on that chip. The driver already has this information available in odr_start_value and uses it when checking valid values when writing to the sampling_frequency attribute, but failed to set the correct initial value in the probe function. Fixes: 37ae8381ccda ("iio: adc: ad7173: add support for additional models") Signed-off-by: David Lechner Link: https://patch.msgid.link/20250710-iio-adc-ad7173-fix-setting-odr-in-probe-v1-1-78a100fec998@baylibre.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 3a5e4f2407b529..fbd191630477cf 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -1591,6 +1591,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.bipolar = false; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + chan_st_priv->cfg.odr = st->info->odr_start_value; chan_st_priv->cfg.openwire_comp_chan = -1; st->adc_mode |= AD7173_ADC_MODE_REF_EN; if (st->info->data_reg_only_16bit) @@ -1658,7 +1659,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->channel = ain[0]; chan_st_priv->chan_reg = chan_index; chan_st_priv->cfg.input_buf = st->info->has_input_buf; - chan_st_priv->cfg.odr = 0; + chan_st_priv->cfg.odr = st->info->odr_start_value; chan_st_priv->cfg.openwire_comp_chan = -1; chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); From d987c8b928f4100f2c168ba2d62a792e1743d673 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Tue, 22 Jul 2025 14:20:07 -0500 Subject: [PATCH 20/28] iio: adc: ad7173: prevent scan if too many setups requested Add a check to ad7173_update_scan_mode() to ensure that we didn't exceed the maximum number of unique channel configurations. In the AD7173 family of chips, there are some chips that have 16 CHANNELx registers but only 8 setups (combination of CONFIGx, FILTERx, GAINx and OFFSETx registers). Since commit 92c247216918 ("iio: adc: ad7173: fix num_slots"), it is possible to have more than 8 channels enabled in a scan at the same time, so it is possible to get a bad configuration when more than 8 channels are using unique configurations. This happens because the algorithm to allocate the setup slots only takes into account which slot has been least recently used and doesn't know about the maximum number of slots available. Since the algorithm to allocate the setup slots is quite complex, it is simpler to check after the fact if the current state is valid or not. So this patch adds a check in ad7173_update_scan_mode() after setting up all of the configurations to make sure that the actual setup still matches the requested setup for each enabled channel. If not, we prevent the scan from being enabled and return an error. The setup comparison in ad7173_setup_equal() is refactored to a separate function since we need to call it in two places now. Fixes: 92c247216918 ("iio: adc: ad7173: fix num_slots") Signed-off-by: David Lechner Link: https://patch.msgid.link/20250722-iio-adc-ad7173-fix-setup-use-limits-v2-1-8e96bdb72a9c@baylibre.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 87 ++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index fbd191630477cf..fd6342217c22e0 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -200,7 +200,7 @@ struct ad7173_channel_config { /* * Following fields are used to compare equality. If you * make adaptations in it, you most likely also have to adapt - * ad7173_find_live_config(), too. + * ad7173_is_setup_equal(), too. */ struct_group(config_props, bool bipolar; @@ -562,12 +562,19 @@ static void ad7173_reset_usage_cnts(struct ad7173_state *st) st->config_usage_counter = 0; } -static struct ad7173_channel_config * -ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg) +/** + * ad7173_is_setup_equal - Compare two channel setups + * @cfg1: First channel configuration + * @cfg2: Second channel configuration + * + * Compares all configuration options that affect the registers connected to + * SETUP_SEL, namely CONFIGx, FILTERx, GAINx and OFFSETx. + * + * Returns: true if the setups are identical, false otherwise + */ +static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1, + const struct ad7173_channel_config *cfg2) { - struct ad7173_channel_config *cfg_aux; - int i; - /* * This is just to make sure that the comparison is adapted after * struct ad7173_channel_config was changed. @@ -580,14 +587,22 @@ ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *c u8 ref_sel; })); + return cfg1->bipolar == cfg2->bipolar && + cfg1->input_buf == cfg2->input_buf && + cfg1->odr == cfg2->odr && + cfg1->ref_sel == cfg2->ref_sel; +} + +static struct ad7173_channel_config * +ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg) +{ + struct ad7173_channel_config *cfg_aux; + int i; + for (i = 0; i < st->num_channels; i++) { cfg_aux = &st->channels[i].cfg; - if (cfg_aux->live && - cfg->bipolar == cfg_aux->bipolar && - cfg->input_buf == cfg_aux->input_buf && - cfg->odr == cfg_aux->odr && - cfg->ref_sel == cfg_aux->ref_sel) + if (cfg_aux->live && ad7173_is_setup_equal(cfg, cfg_aux)) return cfg_aux; } return NULL; @@ -1230,7 +1245,7 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct ad7173_state *st = iio_priv(indio_dev); - int i, ret; + int i, j, k, ret; for (i = 0; i < indio_dev->num_channels; i++) { if (test_bit(i, scan_mask)) @@ -1241,6 +1256,54 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev, return ret; } + /* + * On some chips, there are more channels that setups, so if there were + * more unique setups requested than the number of available slots, + * ad7173_set_channel() will have written over some of the slots. We + * can detect this by making sure each assigned cfg_slot matches the + * requested configuration. If it doesn't, we know that the slot was + * overwritten by a different channel. + */ + for_each_set_bit(i, scan_mask, indio_dev->num_channels) { + const struct ad7173_channel_config *cfg1, *cfg2; + + cfg1 = &st->channels[i].cfg; + + for_each_set_bit(j, scan_mask, indio_dev->num_channels) { + cfg2 = &st->channels[j].cfg; + + /* + * Only compare configs that are assigned to the same + * SETUP_SEL slot and don't compare channel to itself. + */ + if (i == j || cfg1->cfg_slot != cfg2->cfg_slot) + continue; + + /* + * If we find two different configs trying to use the + * same SETUP_SEL slot, then we know that the that we + * have too many unique configurations requested for + * the available slots and at least one was overwritten. + */ + if (!ad7173_is_setup_equal(cfg1, cfg2)) { + /* + * At this point, there isn't a way to tell + * which setups are actually programmed in the + * ADC anymore, so we could read them back to + * see, but it is simpler to just turn off all + * of the live flags so that everything gets + * reprogramed on the next attempt read a sample. + */ + for (k = 0; k < st->num_channels; k++) + st->channels[k].cfg.live = false; + + dev_err(&st->sd.spi->dev, + "Too many unique channel configurations requested for scan\n"); + return -EINVAL; + } + } + } + return 0; } From b110286a59cb3a457205653b78a511d3c36f6040 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Thu, 10 Jul 2025 17:22:00 -0500 Subject: [PATCH 21/28] iio: adc: ad7173: add SPI offload support Enable SPI offload support for the AD7173 ADC driver. The scan_type used for SPI offload is assuming that we are using the ad411x_ad717x HDL project [1] which always stores data words in 32-bits. Link: https://analogdevicesinc.github.io/hdl/projects/ad411x_ad717x/index.html [1] Reviewed-by: Andy Shevchenko Signed-off-by: David Lechner Link: https://patch.msgid.link/20250710-iio-adc-ad7173-add-spi-offload-support-v4-1-536857c4e043@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index fd6342217c22e0..1d031cd148fa2f 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -762,6 +762,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_4_slots = { .set_mode = ad7173_set_mode, .has_registers = true, .has_named_irqs = true, + .supports_spi_offload = true, .addr_shift = 0, .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), @@ -778,6 +779,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = { .set_mode = ad7173_set_mode, .has_registers = true, .has_named_irqs = true, + .supports_spi_offload = true, .addr_shift = 0, .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), @@ -794,6 +796,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = { .set_mode = ad7173_set_mode, .has_registers = true, .has_named_irqs = true, + .supports_spi_offload = true, .addr_shift = 0, .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), @@ -1660,6 +1663,11 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) if (st->info->data_reg_only_16bit) chan_arr[chan_index].scan_type = ad4113_scan_type; + if (ad_sigma_delta_has_spi_offload(&st->sd)) { + chan_arr[chan_index].scan_type.storagebits = 32; + chan_arr[chan_index].scan_type.endianness = IIO_CPU; + } + chan_index++; } @@ -1751,6 +1759,12 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) if (st->info->data_reg_only_16bit) chan_arr[chan_index].scan_type = ad4113_scan_type; + /* Assuming SPI offload is ad411x_ad717x HDL project. */ + if (ad_sigma_delta_has_spi_offload(&st->sd)) { + chan_arr[chan_index].scan_type.storagebits = 32; + chan_arr[chan_index].scan_type.endianness = IIO_CPU; + } + chan_index++; } return 0; From f7d2d2d0a4a7a8776490729df59299356b7487b9 Mon Sep 17 00:00:00 2001 From: Waqar Hameed Date: Tue, 5 Aug 2025 11:33:33 +0200 Subject: [PATCH 22/28] iio: Remove error prints for devm_add_action_or_reset() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `devm_add_action_or_reset()` fails, it is due to a failed memory allocation and will thus return `-ENOMEM`. `dev_err_probe()` doesn't do anything when error is `-ENOMEM`. Therefore, remove the useless call to `dev_err_probe()` when `devm_add_action_or_reset()` fails, and just return the value instead. Signed-off-by: Waqar Hameed Reviewed-by: Nuno Sá Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/pndectqm7te.a.out@axis.com Signed-off-by: Jonathan Cameron (cherry picked from commit 65e8202f03224d56896202610a5875060bb775d9) --- drivers/iio/adc/ad7173.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 1d031cd148fa2f..e29f8b0ae064d5 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -1797,8 +1797,7 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) ret = devm_add_action_or_reset(dev, ad7173_disable_regulators, st); if (ret) - return dev_err_probe(dev, ret, - "Failed to add regulators disable action\n"); + return ret; ret = device_property_match_property_string(dev, "clock-names", ad7173_clk_sel, From a9f08565e195bb6129b375713ce0c716d97e3d0e Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 12 Sep 2025 14:09:30 -0500 Subject: [PATCH 23/28] iio: adc: ad7173: rename ad7173_chan_spec_ext_info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename ad7173_calibsys_ext_info[] to ad7173_chan_spec_ext_info[]. This array is not limited to calibration attributes, so the name should be more generic. Reviewed-by: Nuno Sá Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index e29f8b0ae064d5..a0cce22241d629 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -370,7 +370,7 @@ static const struct iio_enum ad7173_syscalib_mode_enum = { .get = ad7173_get_syscalib_mode }; -static const struct iio_chan_spec_ext_info ad7173_calibsys_ext_info[] = { +static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = { { .name = "sys_calibration", .write = ad7173_write_syscalib, @@ -1401,7 +1401,7 @@ static const struct iio_chan_spec ad7173_channel_template = { .storagebits = 32, .endianness = IIO_BE, }, - .ext_info = ad7173_calibsys_ext_info, + .ext_info = ad7173_chan_spec_ext_info, }; static const struct iio_chan_spec ad7173_temp_iio_channel_template = { From 5ae79e4e097ebd733f8a5083e852844ec60275c5 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 12 Sep 2025 14:09:31 -0500 Subject: [PATCH 24/28] iio: adc: ad7173: rename odr field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename odr to sinc5_odr_index in the channel setup structure. In a following commit, we will be adding a separate odr field for when the sinc3 filter is used instead so having sinc5 in the name will help avoid confusion. And _index makes it more clear that this is an index of the sinc5_data_rates array and not the output data rate itself. Reviewed-by: Nuno Sá Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index a0cce22241d629..c812439049bfea 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -205,7 +205,7 @@ struct ad7173_channel_config { struct_group(config_props, bool bipolar; bool input_buf; - u8 odr; + u8 sinc5_odr_index; u8 ref_sel; ); }; @@ -583,13 +583,13 @@ static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1, sizeof(struct { bool bipolar; bool input_buf; - u8 odr; + u8 sinc5_odr_index; u8 ref_sel; })); return cfg1->bipolar == cfg2->bipolar && cfg1->input_buf == cfg2->input_buf && - cfg1->odr == cfg2->odr && + cfg1->sinc5_odr_index == cfg2->sinc5_odr_index && cfg1->ref_sel == cfg2->ref_sel; } @@ -651,7 +651,7 @@ static int ad7173_load_config(struct ad7173_state *st, return ret; return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, - AD7173_FILTER_ODR0_MASK & cfg->odr); + AD7173_FILTER_ODR0_MASK & cfg->sinc5_odr_index); } static int ad7173_config_channel(struct ad7173_state *st, int addr) @@ -1184,7 +1184,7 @@ static int ad7173_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: - reg = st->channels[chan->address].cfg.odr; + reg = st->channels[chan->address].cfg.sinc5_odr_index; *val = st->info->sinc5_data_rates[reg] / MILLI; *val2 = (st->info->sinc5_data_rates[reg] % MILLI) * (MICRO / MILLI); @@ -1231,7 +1231,7 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, break; cfg = &st->channels[chan->address].cfg; - cfg->odr = i; + cfg->sinc5_odr_index = i; cfg->live = false; break; @@ -1657,7 +1657,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.bipolar = false; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; - chan_st_priv->cfg.odr = st->info->odr_start_value; + chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; chan_st_priv->cfg.openwire_comp_chan = -1; st->adc_mode |= AD7173_ADC_MODE_REF_EN; if (st->info->data_reg_only_16bit) @@ -1730,7 +1730,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->channel = ain[0]; chan_st_priv->chan_reg = chan_index; chan_st_priv->cfg.input_buf = st->info->has_input_buf; - chan_st_priv->cfg.odr = st->info->odr_start_value; + chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; chan_st_priv->cfg.openwire_comp_chan = -1; chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); From bb688cd9f77279c579113b1d0e4abb69c5d5ee5a Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 12 Sep 2025 14:09:32 -0500 Subject: [PATCH 25/28] iio: adc: ad7173: support changing filter type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for changing the filter type to the ad7173 driver. This family of chips by default uses a sinc5+sinc1 filter. There are also optional post-filters that can be added in this configuration for various 50/60Hz rejection purposes. The sinc3 filter doesn't have any post-filters and handles the output data rate (ODR) a bit differently. Here, we've opted to use SINC3_MAPx to get the maximum possible sampling frequencies with the SINC3 filter. Adding support consists of adding the filter_type and filter_type_available attributes, making the sampling_frequency attribute aware of the filter type, and programming the filter parameters when we configure the channel of the ADC for reading a sample. Reviewed-by: Nuno Sá Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 186 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 181 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index c812439049bfea..994a56966eea2f 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -8,6 +8,7 @@ * AD7175-8/AD7176-2/AD7177-2 * * Copyright (C) 2015, 2024 Analog Devices, Inc. + * Copyright (C) 2025 BayLibre, SAS */ #include @@ -149,7 +150,12 @@ (pin2) < st->info->num_voltage_in && \ (pin2) >= st->info->num_voltage_in_div) -#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0) +#define AD7173_FILTER_SINC3_MAP BIT(15) +#define AD7173_FILTER_SINC3_MAP_DIV GENMASK(14, 0) +#define AD7173_FILTER_ENHFILTEN BIT(11) +#define AD7173_FILTER_ENHFILT_MASK GENMASK(10, 8) +#define AD7173_FILTER_ORDER BIT(6) +#define AD7173_FILTER_ODR_MASK GENMASK(5, 0) #define AD7173_MAX_CONFIGS 8 #define AD4111_OW_DET_THRSH_MV 300 @@ -190,6 +196,15 @@ struct ad7173_device_info { u8 num_gpios; }; +enum ad7173_filter_type { + AD7173_FILTER_SINC3, + AD7173_FILTER_SINC5_SINC1, + AD7173_FILTER_SINC5_SINC1_PF1, + AD7173_FILTER_SINC5_SINC1_PF2, + AD7173_FILTER_SINC5_SINC1_PF3, + AD7173_FILTER_SINC5_SINC1_PF4, +}; + struct ad7173_channel_config { /* Openwire detection threshold */ unsigned int openwire_thrsh_raw; @@ -205,8 +220,10 @@ struct ad7173_channel_config { struct_group(config_props, bool bipolar; bool input_buf; + u16 sinc3_odr_div; u8 sinc5_odr_index; u8 ref_sel; + enum ad7173_filter_type filter_type; ); }; @@ -267,6 +284,24 @@ static const unsigned int ad7175_sinc5_data_rates[] = { 5000, /* 20 */ }; +/** + * ad7173_sinc3_odr_div_from_odr() - Convert ODR to divider value + * @odr_millihz: ODR (sampling_frequency) in milliHz + * Returns: Divider value for SINC3 filter to pass. + */ +static u16 ad7173_sinc3_odr_div_from_odr(u32 odr_millihz) +{ + /* + * Divider is f_MOD (1 MHz) / 32 / ODR. ODR freq is in milliHz, so + * we need to convert f_MOD to the same units. When SING_CYC=1 or + * multiple channels are enabled (currently always the case), there + * is an additional factor of 3. + */ + u32 div = DIV_ROUND_CLOSEST(MEGA * MILLI, odr_millihz * 32 * 3); + /* Avoid divide by 0 and limit to register field size. */ + return clamp(div, 1U, AD7173_FILTER_SINC3_MAP_DIV); +} + static unsigned int ad4111_current_channel_config[] = { /* Ain sel: pos neg */ 0x1E8, /* 15:IIN0+ 8:IIN0− */ @@ -370,6 +405,47 @@ static const struct iio_enum ad7173_syscalib_mode_enum = { .get = ad7173_get_syscalib_mode }; +static const char * const ad7173_filter_types_str[] = { + [AD7173_FILTER_SINC3] = "sinc3", + [AD7173_FILTER_SINC5_SINC1] = "sinc5+sinc1", + [AD7173_FILTER_SINC5_SINC1_PF1] = "sinc5+sinc1+pf1", + [AD7173_FILTER_SINC5_SINC1_PF2] = "sinc5+sinc1+pf2", + [AD7173_FILTER_SINC5_SINC1_PF3] = "sinc5+sinc1+pf3", + [AD7173_FILTER_SINC5_SINC1_PF4] = "sinc5+sinc1+pf4", +}; + +static int ad7173_set_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int val) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + st->channels[chan->address].cfg.filter_type = val; + st->channels[chan->address].cfg.live = false; + + iio_device_release_direct(indio_dev); + + return 0; +} + +static int ad7173_get_filter_type(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7173_state *st = iio_priv(indio_dev); + + return st->channels[chan->address].cfg.filter_type; +} + +static const struct iio_enum ad7173_filter_type_enum = { + .items = ad7173_filter_types_str, + .num_items = ARRAY_SIZE(ad7173_filter_types_str), + .set = ad7173_set_filter_type, + .get = ad7173_get_filter_type, +}; + static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = { { .name = "sys_calibration", @@ -380,6 +456,16 @@ static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = { &ad7173_syscalib_mode_enum), IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, &ad7173_syscalib_mode_enum), + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad7173_filter_type_enum), + { } +}; + +static const struct iio_chan_spec_ext_info ad7173_temp_chan_spec_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad7173_filter_type_enum), { } }; @@ -583,14 +669,18 @@ static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1, sizeof(struct { bool bipolar; bool input_buf; + u16 sinc3_odr_div; u8 sinc5_odr_index; u8 ref_sel; + enum ad7173_filter_type filter_type; })); return cfg1->bipolar == cfg2->bipolar && cfg1->input_buf == cfg2->input_buf && + cfg1->sinc3_odr_div == cfg2->sinc3_odr_div && cfg1->sinc5_odr_index == cfg2->sinc5_odr_index && - cfg1->ref_sel == cfg2->ref_sel; + cfg1->ref_sel == cfg2->ref_sel && + cfg1->filter_type == cfg2->filter_type; } static struct ad7173_channel_config * @@ -631,6 +721,7 @@ static int ad7173_load_config(struct ad7173_state *st, { unsigned int config; int free_cfg_slot, ret; + u8 post_filter_enable, post_filter_select; free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0, st->info->num_configs - 1, GFP_KERNEL); @@ -650,8 +741,49 @@ static int ad7173_load_config(struct ad7173_state *st, if (ret) return ret; + /* + * When SINC3_MAP flag is enabled, the rest of the register has a + * different meaning. We are using this option to allow the most + * possible sampling frequencies with SINC3 filter. + */ + if (cfg->filter_type == AD7173_FILTER_SINC3) + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 1) | + FIELD_PREP(AD7173_FILTER_SINC3_MAP_DIV, + cfg->sinc3_odr_div)); + + switch (cfg->filter_type) { + case AD7173_FILTER_SINC5_SINC1_PF1: + post_filter_enable = 1; + post_filter_select = 2; + break; + case AD7173_FILTER_SINC5_SINC1_PF2: + post_filter_enable = 1; + post_filter_select = 3; + break; + case AD7173_FILTER_SINC5_SINC1_PF3: + post_filter_enable = 1; + post_filter_select = 5; + break; + case AD7173_FILTER_SINC5_SINC1_PF4: + post_filter_enable = 1; + post_filter_select = 6; + break; + default: + post_filter_enable = 0; + post_filter_select = 0; + break; + } + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, - AD7173_FILTER_ODR0_MASK & cfg->sinc5_odr_index); + FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) | + FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, + post_filter_enable) | + FIELD_PREP(AD7173_FILTER_ENHFILTEN, + post_filter_select) | + FIELD_PREP(AD7173_FILTER_ORDER, 0) | + FIELD_PREP(AD7173_FILTER_ODR_MASK, + cfg->sinc5_odr_index)); } static int ad7173_config_channel(struct ad7173_state *st, int addr) @@ -1184,6 +1316,13 @@ static int ad7173_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: + if (st->channels[chan->address].cfg.filter_type == AD7173_FILTER_SINC3) { + /* Inverse operation of ad7173_sinc3_odr_div_from_odr() */ + *val = MEGA; + *val2 = 3 * 32 * st->channels[chan->address].cfg.sinc3_odr_div; + return IIO_VAL_FRACTIONAL; + } + reg = st->channels[chan->address].cfg.sinc5_odr_index; *val = st->info->sinc5_data_rates[reg] / MILLI; @@ -1223,6 +1362,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, * * This will cause the reading of CH1 to be actually done once every * 200.16ms, an effective rate of 4.99sps. + * + * Both the sinc5 and sinc3 rates are set here so that if the filter + * type is changed, the requested rate will still be set (aside from + * rounding differences). */ case IIO_CHAN_INFO_SAMP_FREQ: freq = val * MILLI + val2 / MILLI; @@ -1232,6 +1375,7 @@ static int ad7173_write_raw(struct iio_dev *indio_dev, cfg = &st->channels[chan->address].cfg; cfg->sinc5_odr_index = i; + cfg->sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(freq); cfg->live = false; break; @@ -1248,17 +1392,40 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct ad7173_state *st = iio_priv(indio_dev); + u16 sinc3_count = 0; + u16 sinc3_div = 0; int i, j, k, ret; for (i = 0; i < indio_dev->num_channels; i++) { - if (test_bit(i, scan_mask)) + const struct ad7173_channel_config *cfg = &st->channels[i].cfg; + + if (test_bit(i, scan_mask)) { + if (cfg->filter_type == AD7173_FILTER_SINC3) { + sinc3_count++; + + if (sinc3_div == 0) { + sinc3_div = cfg->sinc3_odr_div; + } else if (sinc3_div != cfg->sinc3_odr_div) { + dev_err(&st->sd.spi->dev, + "All enabled channels must have the same sampling_frequency for sinc3 filter_type\n"); + return -EINVAL; + } + } + ret = ad7173_set_channel(&st->sd, i); - else + } else { ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0); + } if (ret < 0) return ret; } + if (sinc3_count && sinc3_count < bitmap_weight(scan_mask, indio_dev->num_channels)) { + dev_err(&st->sd.spi->dev, + "All enabled channels must have sinc3 filter_type\n"); + return -EINVAL; + } + /* * On some chips, there are more channels that setups, so if there were * more unique setups requested than the number of available slots, @@ -1417,6 +1584,7 @@ static const struct iio_chan_spec ad7173_temp_iio_channel_template = { .storagebits = 32, .endianness = IIO_BE, }, + .ext_info = ad7173_temp_chan_spec_ext_info, }; static void ad7173_disable_regulators(void *data) @@ -1657,7 +1825,11 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan_st_priv->cfg.bipolar = false; chan_st_priv->cfg.input_buf = st->info->has_input_buf; chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( + st->info->sinc5_data_rates[st->info->odr_start_value] + ); chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; chan_st_priv->cfg.openwire_comp_chan = -1; st->adc_mode |= AD7173_ADC_MODE_REF_EN; if (st->info->data_reg_only_16bit) @@ -1730,7 +1902,11 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) chan->channel = ain[0]; chan_st_priv->chan_reg = chan_index; chan_st_priv->cfg.input_buf = st->info->has_input_buf; + chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr( + st->info->sinc5_data_rates[st->info->odr_start_value] + ); chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value; + chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1; chan_st_priv->cfg.openwire_comp_chan = -1; chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); From b9792c6bae575adbd3e8481cba875004d878f756 Mon Sep 17 00:00:00 2001 From: Giorgi Tchankvetadze Date: Mon, 23 Feb 2026 12:24:00 +0400 Subject: [PATCH 26/28] iio: adc: ad7173: move opening brace to a separate line Place the opening brace of ad7173_calc_openwire_thrsh_raw() on its own line to comply with the kernel coding style for function definitions. Issue found by checkpatch. Signed-off-by: Giorgi Tchankvetadze Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7173.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 994a56966eea2f..070b56ed117519 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -1765,7 +1765,8 @@ static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st, static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st, struct iio_chan_spec *chan, struct ad7173_channel *chan_st_priv, - unsigned int thrsh_mv) { + unsigned int thrsh_mv) +{ unsigned int thrsh_raw; thrsh_raw = From eb206614c881a5e91d431a2c7508659a982d09f8 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Tue, 31 Mar 2026 16:46:17 -0300 Subject: [PATCH 27/28] iio: adc: ad_sigma_delta: Select SPI_OFFLOAD and DMAENGINE New SPI offload support is enabled through SPI_OFFLOAD config symbol. Signed-off-by: Marcelo Schmitt --- drivers/iio/adc/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b9ff9727821b96..84fdb44a3231d6 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -19,7 +19,10 @@ config AB8500_GPADC config AD_SIGMA_DELTA tristate select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE select IIO_TRIGGERED_BUFFER + select SPI_OFFLOAD config AD4000 tristate "Analog Devices AD4000 ADC Driver" From 5ed7a395c52a294e1d997b5e08b73b0fa26fe542 Mon Sep 17 00:00:00 2001 From: Marcelo Schmitt Date: Thu, 16 Apr 2026 09:45:04 -0300 Subject: [PATCH 28/28] iio: adc: ad7173: Fix digital filter configuration Filter enable and filter type selection masks were swapped on data preparation for filter configuration register write. Use the correct masks to set each property of post filter configuration. Fixes: bb688cd9f772 ("iio: adc: ad7173: support changing filter type") Signed-off-by: Marcelo Schmitt --- drivers/iio/adc/ad7173.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index 070b56ed117519..8ba3bade1ee8c1 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -777,9 +777,9 @@ static int ad7173_load_config(struct ad7173_state *st, return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) | - FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, - post_filter_enable) | FIELD_PREP(AD7173_FILTER_ENHFILTEN, + post_filter_enable) | + FIELD_PREP(AD7173_FILTER_ENHFILT_MASK, post_filter_select) | FIELD_PREP(AD7173_FILTER_ORDER, 0) | FIELD_PREP(AD7173_FILTER_ODR_MASK,