* Re: [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-2-33e439e4fb87@analog.com>
@ 2026-05-05 13:23 ` Jonathan Cameron
2026-05-07 9:26 ` Sabau, Radu bogdan
1 sibling, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 13:23 UTC (permalink / raw)
To: Radu Sabau via B4 Relay
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
linux-pwm, linux-gpio, linux-doc
On Thu, 30 Apr 2026 13:16:44 +0300
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add support for the Analog Devices AD4691 family of high-speed,
> low-power multichannel SAR ADCs: AD4691 (16-ch, 500 kSPS),
> AD4692 (16-ch, 1 MSPS), AD4693 (8-ch, 500 kSPS) and
> AD4694 (8-ch, 1 MSPS).
>
> The driver implements a custom regmap layer over raw SPI to handle the
> device's mixed 1/2/3/4-byte register widths and uses the standard IIO
> read_raw/write_raw interface for single-channel reads.
>
> The chip idles in Autonomous Mode so that single-shot read_raw can use
> the internal oscillator without disturbing the hardware configuration.
>
> Three voltage supply domains are managed: avdd (required), vio, and a
> reference supply on either the REF pin (ref-supply, external buffer)
> or the REFIN pin (refin-supply, uses the on-chip reference buffer;
> REFBUF_EN is set accordingly). Hardware reset is performed via
> the reset controller framework; a software reset through SPI_CONFIG_A
> is used as fallback when no hardware reset is available.
>
> Accumulator channel masking for single-shot reads uses ACC_MASK_REG via
> an ADDR_DESCENDING SPI write, which covers both mask bytes in a single
> 16-bit transfer.
>
> Reviewed-by: David Lechner <dlechner@baylibre.com>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
Hi Radu
Just one query that Sashiko raised that made me look
closer at how you are handling different register sizes.
https://sashiko.dev/#/patchset/20260430-ad4692-multichannel-sar-adc-driver-v9-0-33e439e4fb87%40analog.com
There was also a question about whether the sampling frequency control would
be better described as shared by all.
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> new file mode 100644
> index 000000000000..05826b762c7f
> --- /dev/null
> +++ b/drivers/iio/adc/ad4691.c
> +static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
> +{
> + struct spi_device *spi = context;
> + u8 tx[2], rx[4];
> + int ret;
> +
> + /* Set bit 15 to mark the operation as READ. */
> + put_unaligned_be16(0x8000 | reg, tx);
> +
> + switch (reg) {
> + case 0 ... AD4691_OSC_FREQ_REG:
> + case AD4691_SPARE_CONTROL ... AD4691_ACC_SAT_OVR_REG(15):
Sashiko raised a query here.
"Will this result in a truncated 1-byte read for AD4691_ACC_MASK_REG (0x185)?
AD4691_ACC_MASK_REG falls into the range between AD4691_SPARE_CONTROL and
AD4691_ACC_SAT_OVR_REG(15). In ad4691_reg_write(), AD4691_ACC_MASK_REG is
handled explicitly alongside AD4691_STD_SEQ_CONFIG to perform a 16-bit
write, but it seems missing from the 2-byte read block here."
Just to check - the reasoning behind not just treating these as
fixed sized registers and using bulk reads and writes is the statement
about them being invalid if partially written?
The ACK_MASK_REG is documented as two separate 8 bit registers so why
attempt to treat it as a larger one?
> + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 1);
> + if (ret)
> + return ret;
> + *val = rx[0];
> + return 0;
> + case AD4691_STD_SEQ_CONFIG:
> + case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15):
> + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 2);
> + if (ret)
> + return ret;
> + *val = get_unaligned_be16(rx);
> + return 0;
> + case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15):
> + case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15):
> + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 3);
> + if (ret)
> + return ret;
> + *val = get_unaligned_be24(rx);
> + return 0;
> + case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15):
> + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 4);
> + if (ret)
> + return ret;
> + *val = get_unaligned_be32(rx);
> + return 0;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int ad4691_reg_write(void *context, unsigned int reg, unsigned int val)
> +{
> + struct spi_device *spi = context;
> + u8 tx[4];
> +
> + put_unaligned_be16(reg, tx);
> +
> + switch (reg) {
> + case 0 ... AD4691_OSC_FREQ_REG:
> + case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1:
> + case AD4691_ACC_MASK_REG + 1 ... AD4691_GPIO_MODE2_REG:
> + if (val > U8_MAX)
> + return -EINVAL;
> + tx[2] = val;
> + return spi_write_then_read(spi, tx, 3, NULL, 0);
> + case AD4691_ACC_MASK_REG:
> + case AD4691_STD_SEQ_CONFIG:
> + if (val > U16_MAX)
> + return -EINVAL;
> + put_unaligned_be16(val, &tx[2]);
> + return spi_write_then_read(spi, tx, 4, NULL, 0);
> + default:
> + return -EINVAL;
> + }
> +}
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
[not found] ` <LV9PR03MB841441B282275F8F36FD12C1F7312@LV9PR03MB8414.namprd03.prod.outlook.com>
@ 2026-05-05 13:26 ` Jonathan Cameron
2026-05-05 14:58 ` Andy Shevchenko
0 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 13:26 UTC (permalink / raw)
To: Sabau, Radu bogdan
Cc: Andy Shevchenko, Lars-Peter Clausen, Hennerich, Michael,
David Lechner, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
>
> > > + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {
> > > + irq = fwnode_irq_get_byname(dev_fwnode(dev),
> > > + ad4691_gp_names[i]);
> > > + if (irq > 0)
> > > + break;
> >
> > This is problematic in case the above returns EPROBE_DEFER. Can you confirm
> > it
> > may not ever happen? (Note, I don't know the answer.)
> >
>
> You are right, thanks for this!
I'm missing something. Why is that a problem? Driver will return
the error and a dev_err_probe() is used so it won't print anything.
So probe will fail which is exactly what we want.
Jonathan
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
[not found] ` <afhReLCsEdaEOT_H@ashevche-desk.local>
@ 2026-05-05 14:04 ` Jonathan Cameron
2026-05-05 14:07 ` Jonathan Cameron
2026-05-07 11:37 ` Sabau, Radu bogdan
3 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 14:04 UTC (permalink / raw)
To: Radu Sabau via B4 Relay
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
linux-pwm, linux-gpio, linux-doc
On Thu, 30 Apr 2026 13:16:45 +0300
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add buffered capture support using the IIO triggered buffer framework.
>
> CNV Burst Mode: the GP pin identified by interrupt-names in the device
> tree is configured as DATA_READY output. The IRQ handler stops
> conversions and fires the IIO trigger; the trigger handler executes a
> pre-built SPI message that reads all active channels from the AVG_IN
> accumulator registers and then resets accumulator state and restarts
> conversions for the next cycle.
>
> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> reads the previous result and starts the next conversion (pipelined
> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> the pipeline). The trigger handler executes the message in a single
> spi_sync() call and collects the results. An external trigger (e.g.
> iio-trig-hrtimer) is required to drive the trigger at the desired
> sample rate.
>
> Both modes share the same trigger handler and push a complete scan —
> one u16 slot per channel at its scan_index position, followed by a
> timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
>
> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> buffer-level attribute via IIO_DEVICE_ATTR.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
Hi Radu
I haven't chased it through but Sashiko is raising concerns around the
irq enabling / disable tricks this pulling and they may well be correct.
Please take a close look at what happens in the remove path. We may
need some local driver logic to avoid a double disable.
https://sashiko.dev/#/patchset/20260430-ad4692-multichannel-sar-adc-driver-v9-0-33e439e4fb87%40analog.com
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> index 05826b762c7f..c1e3406fef18 100644
> --- a/drivers/iio/adc/ad4691.c
> +++ b/drivers/iio/adc/ad4691.c
> +
> +static irqreturn_t ad4691_irq(int irq, void *private)
> +{
> + struct iio_dev *indio_dev = private;
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + iio_trigger_poll(indio_dev->trig);
> + /*
> + * Keep the DATA_READY IRQ disabled until the trigger handler has
> + * finished reading the scan, to prevent a new assertion mid-transfer.
> + * The PWM continues running uninterrupted; the IRQ is re-enabled in
> + * ad4691_trigger_handler once spi_sync completes.
> + *
> + * IRQF_ONESHOT already masks the hardware line during this threaded
> + * handler, so disable_irq_nosync here ensures the IRQ stays disabled
> + * even after IRQF_ONESHOT unmasks on return.
> + */
> + disable_irq_nosync(st->irq);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t ad4691_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + ad4691_read_scan(indio_dev, pf->timestamp);
> + if (!st->manual_mode)
> + enable_irq(st->irq);
I think this should be in the reenable_trigger callback rather than here.
That's ultimately fired by iio_trigger_notify_done.
Sashiko had quite a bit to say about the problem paths around the buffer
being disabled between the interrupt and the trigger handler. I don't think
using the reenable trigger solves that though :( We might be able
to fix that centrally by always reenabling the trigger before
disconnecting it but that's rather ugly. There is a note for the async
trigger reenabling about a driver possibly needing to be careful
to not reenable if the whole trigger was disabled in the meantime but
this particular race isn't covered.
Terrible though it is we may need to have some extra infrastructure
to avoid the double disable (assuming it is real).
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
[not found] ` <afhReLCsEdaEOT_H@ashevche-desk.local>
2026-05-05 14:04 ` Jonathan Cameron
@ 2026-05-05 14:07 ` Jonathan Cameron
2026-05-07 11:37 ` Sabau, Radu bogdan
3 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 14:07 UTC (permalink / raw)
To: Radu Sabau via B4 Relay
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
linux-pwm, linux-gpio, linux-doc
On Thu, 30 Apr 2026 13:16:45 +0300
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add buffered capture support using the IIO triggered buffer framework.
>
> CNV Burst Mode: the GP pin identified by interrupt-names in the device
> tree is configured as DATA_READY output. The IRQ handler stops
> conversions and fires the IIO trigger; the trigger handler executes a
> pre-built SPI message that reads all active channels from the AVG_IN
> accumulator registers and then resets accumulator state and restarts
> conversions for the next cycle.
>
> Manual Mode: CNV is tied to SPI CS so each transfer simultaneously
> reads the previous result and starts the next conversion (pipelined
> N+1 scheme). At preenable time a pre-built, optimised SPI message of
> N+1 transfers is constructed (N channel reads plus one NOOP to drain
> the pipeline). The trigger handler executes the message in a single
> spi_sync() call and collects the results. An external trigger (e.g.
> iio-trig-hrtimer) is required to drive the trigger at the desired
> sample rate.
>
> Both modes share the same trigger handler and push a complete scan —
> one u16 slot per channel at its scan_index position, followed by a
> timestamp — to the IIO buffer via iio_push_to_buffers_with_ts().
>
> The CNV Burst Mode sampling frequency (PWM period) is exposed as a
> buffer-level attribute via IIO_DEVICE_ATTR.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
Another Sashiko found issue inline.
Also it calls out that you have no validate_trigger which might mean
another trigger is used. Moving the irq enable to the trigger_reenable
should solve that.
> +static ssize_t sampling_frequency_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + return sysfs_emit(buf, "%lu\n", NSEC_PER_SEC / st->cnv_period_ns);
> +}
> +
> +static ssize_t sampling_frequency_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad4691_state *st = iio_priv(indio_dev);
> + int freq, ret;
> +
> + ret = kstrtoint(buf, 10, &freq);
I missed this but as Sashiko points out this could read in a negative
frequency. Given that's clearly silly kstrtouint()
> + if (ret)
> + return ret;
> +
> + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> + if (IIO_DEV_ACQUIRE_FAILED(claim))
> + return -EBUSY;
> +
> +
> + ret = ad4691_set_pwm_freq(st, freq);
> + if (ret)
> + return ret;
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0);
> +
> +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
> + &iio_dev_attr_sampling_frequency,
> + NULL
> +};
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-4-33e439e4fb87@analog.com>
@ 2026-05-05 14:12 ` Jonathan Cameron
2026-05-05 14:28 ` Jonathan Cameron
2026-05-07 11:49 ` Sabau, Radu bogdan
2 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 14:12 UTC (permalink / raw)
To: Radu Sabau via B4 Relay
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
linux-pwm, linux-gpio, linux-doc
On Thu, 30 Apr 2026 13:16:46 +0300
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add SPI offload support to enable DMA-based, CPU-independent data
> acquisition using the SPI Engine offload framework.
>
> When an SPI offload is available (devm_spi_offload_get() succeeds),
> the driver registers a DMA engine IIO buffer and uses dedicated buffer
> setup operations. If no offload is available the existing software
> triggered buffer path is used unchanged.
>
> Both CNV Burst Mode and Manual Mode support offload, but use different
> trigger mechanisms:
>
> CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY
> signal on the GP pin specified by the trigger-source consumer reference
> in the device tree (one cell = GP pin number 0-3). For this mode the
> driver acts as both an SPI offload consumer (DMA RX stream, message
> optimization) and a trigger source provider: it registers the
> GP/DATA_READY output via devm_spi_offload_trigger_register() so the
> offload framework can match the '#trigger-source-cells' phandle and
> automatically fire the SPI Engine DMA transfer at end-of-conversion.
>
> Manual Mode: the SPI Engine is triggered by a periodic trigger at
> the configured sampling frequency. The pre-built SPI message uses
> the pipelined CNV-on-CS protocol: N+1 16-bit transfers are issued
> for N active channels (the first result is discarded as garbage from
> the pipeline flush) and the remaining N results are captured by DMA.
>
> All offload transfers use 16-bit frames (bits_per_word=16, len=2).
> The channel scan_type (storagebits=16, shift=0, IIO_BE) is shared
> between the software triggered-buffer and offload paths; no separate
> scan_type or channel array is needed for the offload case. The
> ad4691_manual_channels[] array introduced in the triggered-buffer
> commit is reused here: it hides the IIO_CHAN_INFO_OVERSAMPLING_RATIO
> attribute, which is not applicable in Manual Mode.
Probably good to call out that oversampling hasn't been introduced
to the driver yet. This confused Sashiko ;)
>
> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
One minor thing inline.
> static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
> {
> struct spi_device *spi = context;
> @@ -712,6 +791,7 @@ static const struct iio_buffer_setup_ops ad4691_manual_buffer_setup_ops = {
> static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> {
> struct ad4691_state *st = iio_priv(indio_dev);
> + unsigned int acc_mask;
> unsigned int k, i;
> int ret;
>
> @@ -758,9 +838,9 @@ static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> if (ret)
> goto err_unoptimize;
>
> - ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> - ~bitmap_read(indio_dev->active_scan_mask, 0,
> - iio_get_masklength(indio_dev)) & GENMASK(15, 0));
> + acc_mask = ~bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)) & GENMASK(15, 0);
Not obvious to me why this change is here. If you want it, push back to the
original patch that introduced this code.
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, acc_mask);
> if (ret)
> goto err_unoptimize;
>
> @@ -803,6 +883,209 @@ static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = {
> .postdisable = &ad4691_cnv_burst_buffer_postdisable,
> };
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-4-33e439e4fb87@analog.com>
2026-05-05 14:12 ` [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support Jonathan Cameron
@ 2026-05-05 14:28 ` Jonathan Cameron
2026-05-06 9:17 ` Sabau, Radu bogdan
2026-05-07 11:49 ` Sabau, Radu bogdan
2 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 14:28 UTC (permalink / raw)
To: Radu Sabau via B4 Relay
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
linux-pwm, linux-gpio, linux-doc
On Thu, 30 Apr 2026 13:16:46 +0300
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add SPI offload support to enable DMA-based, CPU-independent data
> acquisition using the SPI Engine offload framework.
>
> When an SPI offload is available (devm_spi_offload_get() succeeds),
> the driver registers a DMA engine IIO buffer and uses dedicated buffer
> setup operations. If no offload is available the existing software
> triggered buffer path is used unchanged.
>
> Both CNV Burst Mode and Manual Mode support offload, but use different
> trigger mechanisms:
>
> CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY
> signal on the GP pin specified by the trigger-source consumer reference
> in the device tree (one cell = GP pin number 0-3). For this mode the
> driver acts as both an SPI offload consumer (DMA RX stream, message
> optimization) and a trigger source provider: it registers the
> GP/DATA_READY output via devm_spi_offload_trigger_register() so the
> offload framework can match the '#trigger-source-cells' phandle and
> automatically fire the SPI Engine DMA transfer at end-of-conversion.
>
> Manual Mode: the SPI Engine is triggered by a periodic trigger at
> the configured sampling frequency. The pre-built SPI message uses
> the pipelined CNV-on-CS protocol: N+1 16-bit transfers are issued
> for N active channels (the first result is discarded as garbage from
> the pipeline flush) and the remaining N results are captured by DMA.
>
> All offload transfers use 16-bit frames (bits_per_word=16, len=2).
> The channel scan_type (storagebits=16, shift=0, IIO_BE) is shared
> between the software triggered-buffer and offload paths; no separate
> scan_type or channel array is needed for the offload case. The
> ad4691_manual_channels[] array introduced in the triggered-buffer
> commit is reused here: it hides the IIO_CHAN_INFO_OVERSAMPLING_RATIO
> attribute, which is not applicable in Manual Mode.
>
> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
In general have a read through Sashiko reviews once they come in and
if you agree with them reply to your own patches to say what you are
changing.
https://sashiko.dev/#/patchset/20260430-ad4692-multichannel-sar-adc-driver-v9-0-33e439e4fb87%40analog.com
No perfect but another one in here is something I missed completely.
A few of them called out here but please make sure you've addressed them
all or established them to be false (which happens!)
> +
> +struct ad4691_offload_state {
> + struct spi_offload *offload;
> + struct spi_offload_trigger *trigger;
> + u64 trigger_hz;
> + u8 tx_cmd[17][2];
> + u8 tx_reset[4];
These two buffers share cachelines with trigger_hz. That can
be written via sysfs in the middle of a non coherent DMA transfer.
If it is you may loose the written value when a post transfer
flush copies back a stale cacheline (with the new data in tx_cmd
/ tx_reset). These need marking to force them to have their own cache lines.
> };
>
> +
> +static int ad4691_manual_offload_buffer_predisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> +
> + spi_offload_trigger_disable(offload->offload, offload->trigger);
> + spi_unoptimize_message(&st->scan_msg);
> +
> + return ad4691_exit_conversion_mode(st);
> +}
> +
> +static const struct iio_buffer_setup_ops ad4691_manual_offload_buffer_setup_ops = {
> + .postenable = &ad4691_manual_offload_buffer_postenable,
> + .predisable = &ad4691_manual_offload_buffer_predisable,
> +};
> +
> +static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
> + struct spi_offload_trigger_config config = {
> + .type = SPI_OFFLOAD_TRIGGER_DATA_READY,
> + };
> + unsigned int bpw = indio_dev->channels[0].scan_type.realbits;
> + unsigned int acc_mask;
> + unsigned int bit, k;
> + int ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)));
> + if (ret)
> + return ret;
> +
> + acc_mask = ~bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)) & GENMASK(15, 0);
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG, acc_mask);
> + if (ret)
> + return ret;
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + return ret;
> +
> + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
> +
> + /*
> + * Each AVG_IN register read uses two 16-bit transfers:
> + * TX: [reg_hi | 0x80, reg_lo] (address, CS stays asserted)
> + * RX: [data_hi, data_lo] (data, storagebits=16, shift=0)
> + * The state reset is also split into two 16-bit transfers
> + * (address then value) to keep bits_per_word uniform throughout.
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, bit) {
> + put_unaligned_be16(0x8000 | AD4691_AVG_IN(bit), offload->tx_cmd[k]);
Sashiko makes the interesting point that you are setting bpw to 16 at least sometimes
and that means the SPI bus will be dealing in u16s. As such the endian swap seems
odd. Which also makes a mess if the word length isn't 16 bits. I'm assuming it
always is. If that's the case hard code it rather than giving impression this
will work otherwise. The endian swap probably wants to be unconditional rather
than only if little endian host.
There is a related question whether the larger words interact with the channel
endianness marking. I think that should be fine but please check.
> +
> + /* TX: address phase, CS stays asserted into data phase */
> + st->scan_xfers[2 * k].tx_buf = offload->tx_cmd[k];
> + st->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);
> + st->scan_xfers[2 * k].bits_per_word = bpw;
> +
> + /* RX: data phase, CS toggles after to delimit the next register op */
> + st->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);
> + st->scan_xfers[2 * k + 1].bits_per_word = bpw;
> + st->scan_xfers[2 * k + 1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
> + st->scan_xfers[2 * k + 1].cs_change = 1;
> + k++;
> + }
> +
> + /* State reset to re-arm DATA_READY for the next scan. */
> + put_unaligned_be16(AD4691_STATE_RESET_REG, offload->tx_reset);
> + offload->tx_reset[2] = AD4691_STATE_RESET_ALL;
> +
> + st->scan_xfers[2 * k].tx_buf = offload->tx_reset;
> + st->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);
> + st->scan_xfers[2 * k].bits_per_word = bpw;
> +
> + st->scan_xfers[2 * k + 1].tx_buf = &offload->tx_reset[2];
> + st->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);
> + st->scan_xfers[2 * k + 1].bits_per_word = bpw;
> + st->scan_xfers[2 * k + 1].cs_change = 1;
This is odd. Add a comment on why if you do want to leave CS active
after this sequence.
> +
> + spi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, 2 * k + 2);
> + st->scan_msg.offload = offload->offload;
> +
> + ret = spi_optimize_message(spi, &st->scan_msg);
> + if (ret)
> + goto err_exit_conversion;
> +
> + ret = ad4691_sampling_enable(st, true);
There are some questions from Sashiko around whether the offload trigger should be enabled
first to avoid getting the device into an odd state. I haven't read the datasheet
in enough depth to check - so over to you to argue one way or the other!
> + if (ret)
> + goto err_unoptimize;
> +
> + ret = spi_offload_trigger_enable(offload->offload, offload->trigger, &config);
> + if (ret)
> + goto err_sampling_disable;
> +
> + return 0;
> +
> +err_sampling_disable:
> + ad4691_sampling_enable(st, false);
> +err_unoptimize:
> + spi_unoptimize_message(&st->scan_msg);
> +err_exit_conversion:
> + ad4691_exit_conversion_mode(st);
> + return ret;
> +}
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 5/6] iio: adc: ad4691: add oversampling support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-5-33e439e4fb87@analog.com>
@ 2026-05-05 14:32 ` Jonathan Cameron
2026-05-07 11:56 ` Sabau, Radu bogdan
1 sibling, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 14:32 UTC (permalink / raw)
To: Radu Sabau via B4 Relay
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
linux-pwm, linux-gpio, linux-doc
On Thu, 30 Apr 2026 13:16:47 +0300
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add per-channel oversampling ratio (OSR) support for CNV burst mode.
> The accumulator depth register (ACC_DEPTH_IN) is programmed with the
> selected OSR at buffer enable time and before each single-shot read.
>
> Supported OSR values: 1, 2, 4, 8, 16, 32.
>
> Introduce AD4691_MANUAL_CHANNEL() for manual mode channels, which do
> not expose the oversampling ratio attribute since OSR is not applicable
> in that mode. A separate manual_channels array is added to
> struct ad4691_channel_info and selected at probe time; offload paths
> reuse the same arrays with num_channels capping access before the soft
> timestamp entry.
>
> in_voltageN_sampling_frequency represents the effective output rate for
> channel N, defined as osc_freq / osr[N]. The chip has one internal
> oscillator shared by all channels; each channel independently
> accumulates osr[N] oscillator cycles before producing a result.
>
> Writing sampling_frequency computes needed_osc = freq * osr[N] and
> snaps down to the largest oscillator table entry that satisfies both
> osc <= needed_osc and osc % osr[N] == 0, guaranteeing an exact integer
> read-back. The result is stored in target_osc_freq_Hz and written to
> OSC_FREQ_REG at buffer enable and single-shot time, so sampling_frequency
> and oversampling_ratio can be set in any order.
>
> in_voltageN_sampling_frequency_available is computed dynamically from
> the channel's current OSR, listing only oscillator table entries that
> divide evenly by osr[N], expressed as effective rates. The list becomes
> sparser as OSR increases, capping at max_rate / osr[N].
>
> Writing oversampling_ratio stores the new OSR for that channel;
> target_osc_freq_Hz is left unchanged. The effective rate read back via
> in_voltageN_sampling_frequency becomes target_osc_freq_Hz / new_osr
> automatically. The two attributes are orthogonal: sampling_frequency
> controls the oscillator, oversampling_ratio controls the averaging depth.
>
> OSR defaults to 1 (no accumulation) for all channels.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
Just one thing - from Sashiko again.
J
>
> static int ad4691_read_avail(struct iio_dev *indio_dev,
> @@ -540,10 +655,30 @@ static int ad4691_read_avail(struct iio_dev *indio_dev,
> unsigned int start = ad4691_samp_freq_start(st->info);
>
> switch (mask) {
> - case IIO_CHAN_INFO_SAMP_FREQ:
> - *vals = &ad4691_osc_freqs_Hz[start];
> + case IIO_CHAN_INFO_SAMP_FREQ: {
> + unsigned int osr = st->osr[chan->channel];
> + int n = 0;
> +
Sashiko shouts about possibly getting a torn set in here if osr were to be changed
whilst you were computing the array. That's probably worth using locks to protect against.
> + /*
> + * Only oscillator frequencies evenly divisible by the channel's
> + * OSR yield an integer effective rate; expose those as effective
> + * rates (osc / osr) so the user works entirely in output-sample
> + * space.
> + */
> + for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
> + if (ad4691_osc_freqs_Hz[i] % osr != 0)
> + continue;
> + st->samp_freq_avail[n++] = ad4691_osc_freqs_Hz[i] / osr;
> + }
> + *vals = st->samp_freq_avail;
> *type = IIO_VAL_INT;
> - *length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;
> + *length = n;
> + return IIO_AVAIL_LIST;
> + }
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + *vals = ad4691_oversampling_ratios;
> + *type = IIO_VAL_INT;
> + *length = ARRAY_SIZE(ad4691_oversampling_ratios);
> return IIO_AVAIL_LIST;
> default:
> return -EINVAL;
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 6/6] docs: iio: adc: ad4691: add driver documentation
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-6-33e439e4fb87@analog.com>
@ 2026-05-05 14:35 ` Jonathan Cameron
0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 14:35 UTC (permalink / raw)
To: Radu Sabau via B4 Relay
Cc: radu.sabau, Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Uwe Kleine-König, Liam Girdwood, Mark Brown,
Linus Walleij, Bartosz Golaszewski, Philipp Zabel,
Jonathan Corbet, Shuah Khan, linux-iio, devicetree, linux-kernel,
linux-pwm, linux-gpio, linux-doc
On Thu, 30 Apr 2026 13:16:48 +0300
Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org> wrote:
> From: Radu Sabau <radu.sabau@analog.com>
>
> Add RST documentation for the AD4691 family ADC driver covering
> supported devices, IIO channels, operating modes, oversampling,
> reference voltage, LDO supply, reset, GP pins, SPI offload support,
> and buffer data format.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> diff --git a/Documentation/iio/ad4691.rst b/Documentation/iio/ad4691.rst
> new file mode 100644
> index 000000000000..38e2ad28a713
> --- /dev/null
> +++ b/Documentation/iio/ad4691.rst
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 24e4502b8292..819d8b6eb6bb 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1491,6 +1491,7 @@ S: Supported
> W: https://ez.analog.com/linux-software-drivers
> F: Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml
> F: drivers/iio/adc/ad4691.c
> +F: drivers/iio/adc/ad4691.rst
Not there. (Sashiko got this one)
>
> ANALOG DEVICES INC AD4695 DRIVER
> M: Michael Hennerich <michael.hennerich@analog.com>
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
2026-05-05 13:26 ` [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support Jonathan Cameron
@ 2026-05-05 14:58 ` Andy Shevchenko
2026-05-05 16:17 ` Jonathan Cameron
0 siblings, 1 reply; 25+ messages in thread
From: Andy Shevchenko @ 2026-05-05 14:58 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
David Lechner, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Tue, May 05, 2026 at 02:26:40PM +0100, Jonathan Cameron wrote:
...
> > > > + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {
> > > > + irq = fwnode_irq_get_byname(dev_fwnode(dev),
> > > > + ad4691_gp_names[i]);
> > > > + if (irq > 0)
> > > > + break;
> > >
> > > This is problematic in case the above returns EPROBE_DEFER. Can you confirm
> > > it
> > > may not ever happen? (Note, I don't know the answer.)
> >
> > You are right, thanks for this!
> I'm missing something. Why is that a problem? Driver will return
> the error and a dev_err_probe() is used so it won't print anything.
> So probe will fail which is exactly what we want.
If there are two IRQs and the first one is probe deferred and second returns
an error, we return that error instead of the deferral probe.
May be I missed something, but I have no idea how in this case it may return
the first error code in such a case.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
2026-05-05 14:58 ` Andy Shevchenko
@ 2026-05-05 16:17 ` Jonathan Cameron
2026-05-06 7:25 ` Andy Shevchenko
0 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-05 16:17 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
David Lechner, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Tue, 5 May 2026 17:58:21 +0300
Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> On Tue, May 05, 2026 at 02:26:40PM +0100, Jonathan Cameron wrote:
>
> ...
>
> > > > > + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {
> > > > > + irq = fwnode_irq_get_byname(dev_fwnode(dev),
> > > > > + ad4691_gp_names[i]);
> > > > > + if (irq > 0)
> > > > > + break;
> > > >
> > > > This is problematic in case the above returns EPROBE_DEFER. Can you confirm
> > > > it
> > > > may not ever happen? (Note, I don't know the answer.)
> > >
> > > You are right, thanks for this!
> > I'm missing something. Why is that a problem? Driver will return
> > the error and a dev_err_probe() is used so it won't print anything.
> > So probe will fail which is exactly what we want.
>
> If there are two IRQs and the first one is probe deferred and second returns
> an error, we return that error instead of the deferral probe.
>
> May be I missed something, but I have no idea how in this case it may return
> the first error code in such a case.
Ah. Indeed. I completely misread the code. if (irq) would do the job to fix this.
J
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
2026-05-05 16:17 ` Jonathan Cameron
@ 2026-05-06 7:25 ` Andy Shevchenko
2026-05-06 9:01 ` Sabau, Radu bogdan
0 siblings, 1 reply; 25+ messages in thread
From: Andy Shevchenko @ 2026-05-06 7:25 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
David Lechner, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Tue, May 05, 2026 at 05:17:23PM +0100, Jonathan Cameron wrote:
> On Tue, 5 May 2026 17:58:21 +0300
> Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> > On Tue, May 05, 2026 at 02:26:40PM +0100, Jonathan Cameron wrote:
...
> > > > > > + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {
> > > > > > + irq = fwnode_irq_get_byname(dev_fwnode(dev),
> > > > > > + ad4691_gp_names[i]);
> > > > > > + if (irq > 0)
> > > > > > + break;
> > > > >
> > > > > This is problematic in case the above returns EPROBE_DEFER. Can you confirm
> > > > > it
> > > > > may not ever happen? (Note, I don't know the answer.)
> > > >
> > > > You are right, thanks for this!
> > > I'm missing something. Why is that a problem? Driver will return
> > > the error and a dev_err_probe() is used so it won't print anything.
> > > So probe will fail which is exactly what we want.
> >
> > If there are two IRQs and the first one is probe deferred and second returns
> > an error, we return that error instead of the deferral probe.
> >
> > May be I missed something, but I have no idea how in this case it may return
> > the first error code in such a case.
> Ah. Indeed. I completely misread the code. if (irq) would do the job to fix this.
Not really, as we are only concerned about deferred probe.
if (irq > 0 || irq == -EPROBE_DEFER)
break;
will do the job. But again, please double check that the
fwnode_irq_get_byname() is even capable of returning deferral probe
(probably yes as my weak memory tells me).
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
2026-05-06 7:25 ` Andy Shevchenko
@ 2026-05-06 9:01 ` Sabau, Radu bogdan
0 siblings, 0 replies; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-06 9:01 UTC (permalink / raw)
To: Andy Shevchenko, Jonathan Cameron
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
> -----Original Message-----
> From: Andy Shevchenko <andriy.shevchenko@intel.com>
> Sent: Wednesday, May 6, 2026 10:26 AM
...
>
> > > > > > > + for (i = 0; i < ARRAY_SIZE(ad4691_gp_names); i++) {
> > > > > > > + irq = fwnode_irq_get_byname(dev_fwnode(dev),
> > > > > > > + ad4691_gp_names[i]);
> > > > > > > + if (irq > 0)
> > > > > > > + break;
> > > > > >
> > > > > > This is problematic in case the above returns EPROBE_DEFER. Can you
> confirm
> > > > > > it
> > > > > > may not ever happen? (Note, I don't know the answer.)
> > > > >
> > > > > You are right, thanks for this!
> > > > I'm missing something. Why is that a problem? Driver will return
> > > > the error and a dev_err_probe() is used so it won't print anything.
> > > > So probe will fail which is exactly what we want.
> > >
> > > If there are two IRQs and the first one is probe deferred and second returns
> > > an error, we return that error instead of the deferral probe.
> > >
> > > May be I missed something, but I have no idea how in this case it may
> return
> > > the first error code in such a case.
> > Ah. Indeed. I completely misread the code. if (irq) would do the job to fix
> this.
>
> Not really, as we are only concerned about deferred probe.
>
> if (irq > 0 || irq == -EPROBE_DEFER)
> break;
>
> will do the job. But again, please double check that the
> fwnode_irq_get_byname() is even capable of returning deferral probe
> (probably yes as my weak memory tells me).
>
> --
Hi everyone,
I checked and fwnode_irq_get_byname() can indeed return deferral probe
and the fix I had in mind is like the above.
Thank you all for looking into this,
Radu
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
2026-05-05 14:28 ` Jonathan Cameron
@ 2026-05-06 9:17 ` Sabau, Radu bogdan
0 siblings, 0 replies; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-06 9:17 UTC (permalink / raw)
To: Jonathan Cameron, Radu Sabau via B4 Relay
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Tuesday, May 5, 2026 5:28 PM
> To: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> Cc: Sabau, Radu bogdan <Radu.Sabau@analog.com>; Lars-Peter Clausen
> <lars@metafoo.de>; Hennerich, Michael <Michael.Hennerich@analog.com>;
> David Lechner <dlechner@baylibre.com>; Sa, Nuno <Nuno.Sa@analog.com>;
> Andy Shevchenko <andy@kernel.org>; Rob Herring <robh@kernel.org>;
> Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley
> <conor+dt@kernel.org>; Uwe Kleine-König <ukleinek@kernel.org>; Liam
> Girdwood <lgirdwood@gmail.com>; Mark Brown <broonie@kernel.org>; Linus
> Walleij <linusw@kernel.org>; Bartosz Golaszewski <brgl@kernel.org>; Philipp
> Zabel <p.zabel@pengutronix.de>; Jonathan Corbet <corbet@lwn.net>; Shuah
> Khan <skhan@linuxfoundation.org>; linux-iio@vger.kernel.org;
> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-
> pwm@vger.kernel.org; linux-gpio@vger.kernel.org; linux-doc@vger.kernel.org
> Subject: Re: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
>
> [External]
>
> On Thu, 30 Apr 2026 13:16:46 +0300
> Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> wrote:
>
> > From: Radu Sabau <radu.sabau@analog.com>
> >
> > Add SPI offload support to enable DMA-based, CPU-independent data
> > acquisition using the SPI Engine offload framework.
> >
> > When an SPI offload is available (devm_spi_offload_get() succeeds),
> > the driver registers a DMA engine IIO buffer and uses dedicated buffer
> > setup operations. If no offload is available the existing software
> > triggered buffer path is used unchanged.
> >
> > Both CNV Burst Mode and Manual Mode support offload, but use different
> > trigger mechanisms:
> >
> > CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY
> > signal on the GP pin specified by the trigger-source consumer reference
> > in the device tree (one cell = GP pin number 0-3). For this mode the
> > driver acts as both an SPI offload consumer (DMA RX stream, message
> > optimization) and a trigger source provider: it registers the
> > GP/DATA_READY output via devm_spi_offload_trigger_register() so the
> > offload framework can match the '#trigger-source-cells' phandle and
> > automatically fire the SPI Engine DMA transfer at end-of-conversion.
> >
> > Manual Mode: the SPI Engine is triggered by a periodic trigger at
> > the configured sampling frequency. The pre-built SPI message uses
> > the pipelined CNV-on-CS protocol: N+1 16-bit transfers are issued
> > for N active channels (the first result is discarded as garbage from
> > the pipeline flush) and the remaining N results are captured by DMA.
> >
> > All offload transfers use 16-bit frames (bits_per_word=16, len=2).
> > The channel scan_type (storagebits=16, shift=0, IIO_BE) is shared
> > between the software triggered-buffer and offload paths; no separate
> > scan_type or channel array is needed for the offload case. The
> > ad4691_manual_channels[] array introduced in the triggered-buffer
> > commit is reused here: it hides the IIO_CHAN_INFO_OVERSAMPLING_RATIO
> > attribute, which is not applicable in Manual Mode.
> >
> > Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.
> >
> > Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> In general have a read through Sashiko reviews once they come in and
> if you agree with them reply to your own patches to say what you are
> changing.
> https://urldefense.com/v3/__https://sashiko.dev/*/patchset/20260430-
> ad4692-multichannel-sar-adc-driver-v9-0-
> 33e439e4fb87*40analog.com__;IyU!!A3Ni8CS0y2Y!-
> G5xU6m5PVXdQJCRVp_OOtLEM0gRiLsoZigLcBOrGi00oHLlyYgtxQAtx7azQuyZ
> Bkv8I3Cgy2WoEg$
> No perfect but another one in here is something I missed completely.
>
> A few of them called out here but please make sure you've addressed them
> all or established them to be false (which happens!)
>
Will have a look at Sashiko review in order to address everything in the next
version.
I will also reply to each patch and mention the change, argue to why I think the
raised concern could be false and perhaps have a follow-up discussion here.
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-2-33e439e4fb87@analog.com>
2026-05-05 13:23 ` [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family Jonathan Cameron
@ 2026-05-07 9:26 ` Sabau, Radu bogdan
2026-05-07 14:15 ` Jonathan Cameron
1 sibling, 1 reply; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-07 9:26 UTC (permalink / raw)
To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, David Lechner, Sa, Nuno, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
Addressing Sashiko's review for initial driver's patch.
> -----Original Message-----
> From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> Sent: Thursday, April 30, 2026 1:17 PM
...
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 60038ae8dfc4..3685a03aa8dc 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -139,6 +139,17 @@ config AD4170_4
> To compile this driver as a module, choose M here: the module will be
> called ad4170-4.
>
> +config AD4691
> + tristate "Analog Devices AD4691 Family ADC Driver"
> + depends on SPI
"Should this driver also depend on REGULATOR? In ad4691_regulator_setup(),
it relies on devm_regulator_get_enable_read_voltage() to obtain its reference
voltage. If the kernel is compiled without CONFIG_REGULATOR, this function
is stubbed to return -ENODEV, causing probe to fail unconditionally."
Will add "depends on REGULATOR". Without it,
devm_regulator_get_enable_read_voltage() can be stubbed and
return -ENODEV, making the driver unusable regardless of hardware.
> + select REGMAP
> + help
> + Say yes here to build support for Analog Devices AD4691 Family
...
> +};
> +
> +#define AD4691_CHANNEL(ch)
> \
> + { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
> \
> + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .info_mask_separate_available =
> \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),
> \
"Since ad4691_set_sampling_freq() modifies the AD4691_OSC_FREQ_REG register
which controls the global internal oscillator frequency for the entire ADC,
should IIO_CHAN_INFO_SAMP_FREQ be exposed as info_mask_shared_by_all
instead of info_mask_separate? Changing the frequency for one channel
currently seems to silently alter it for all channels."
Agreed for this commit. With no per-channel OSR in the base driver
all channels share a single oscillator register, so info_mask_shared_by_all
is the correct annotation. Changing accordingly. The OSR commit switches it
back to info_mask_separate because at that point the effective rate is
genuinely per-channel (osc_freq / osr[N]).
> + .channel = ch, \
> + .scan_index = ch, \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + }, \
> + }
> +
...
> +};
> +
> +static int ad4691_reg_read(void *context, unsigned int reg, unsigned int
> *val)
> +{
> + struct spi_device *spi = context;
> + u8 tx[2], rx[4];
> + int ret;
> +
> + /* Set bit 15 to mark the operation as READ. */
> + put_unaligned_be16(0x8000 | reg, tx);
> +
> + switch (reg) {
> + case 0 ... AD4691_OSC_FREQ_REG:
> + case AD4691_SPARE_CONTROL ... AD4691_ACC_SAT_OVR_REG(15):
> + ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 1);
"Will this result in a truncated 1-byte read for AD4691_ACC_MASK_REG (0x185)?
AD4691_ACC_MASK_REG falls into the range between AD4691_SPARE_CONTROL and
AD4691_ACC_SAT_OVR_REG(15). In ad4691_reg_write(), AD4691_ACC_MASK_REG is
handled explicitly alongside AD4691_STD_SEQ_CONFIG to perform a 16-bit
write, but it seems missing from the 2-byte read block here."
Valid bug. The write path already splits the range around ACC_MASK_REG;
the read path was missing the same split. Fixed: ACC_MASK_REG now gets
a 2-byte read alongside STD_SEQ_CONFIG.
> + if (ret)
> + return ret;
...
> +
> +static int ad4691_reset(struct ad4691_state *st)
> +{
> + struct device *dev = regmap_get_device(st->regmap);
> + struct reset_control *rst;
> +
> + rst = devm_reset_control_get_optional_exclusive(dev, NULL);
> + if (IS_ERR(rst))
> + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get
> reset\n");
> +
> + if (rst) {
> + /*
> + * reset_gpio_probe() already drives the pin asserted, so the
> + * device is held in reset before we get here.
> + * devm_reset_control_get_optional_exclusive_deasserted()
> cannot
> + * be used because it deasserts immediately without delay; the
> + * datasheet (Table 5) requires a ≥300 µs reset pulse width
> + * before deassertion.
> + */
> + fsleep(300);
> + return reset_control_deassert(rst);
"Could this sequence break on driver reload?
When the driver is unloaded, the reset line remains in its deasserted state
because there is no cleanup action to re-assert it. Upon reloading, the 300
microsecond sleep will occur while the pin is already deasserted, meaning
the hardware might not receive the required reset pulse.
Should reset_control_assert() be called explicitly prior to the delay?"
Valid. reset_control_put() does not re-assert the line at unload, so
on reload the pin is already deasserted when we enter the 300 µs sleep.
Adding reset_control_assert(rst) before fsleep(300) to guarantee a proper
reset pulse on every probe including reloads.
> + }
> +
...
> +
> + st = iio_priv(indio_dev);
> + st->info = spi_get_device_match_data(spi);
"Is it possible for st->info to be NULL here?
If the driver is manually bound to a device with a non-matching name using
the sysfs bind interface in combination with driver_override,
spi_get_device_match_data() could return NULL, which would cause a NULL
pointer dereference later in the probe sequence."
Agreed, will add the NULL check with -ENODEV immediately after
spi_get_device_match_data().
> +
> + ret = devm_mutex_init(dev, &st->lock);
> + if (ret)
> + return ret;
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
` (2 preceding siblings ...)
2026-05-05 14:07 ` Jonathan Cameron
@ 2026-05-07 11:37 ` Sabau, Radu bogdan
2026-05-07 14:25 ` Jonathan Cameron
3 siblings, 1 reply; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-07 11:37 UTC (permalink / raw)
To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, David Lechner, Sa, Nuno, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
Addressing Sashiko's review for triggered buffer patch.
> -----Original Message-----
> From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> Sent: Thursday, April 30, 2026 1:17 PM
...
> +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + unsigned int prev_i, k, i;
> + bool first;
> + int ret;
> +
> + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
> + memset(st->scan_tx, 0, sizeof(st->scan_tx));
> +
> + spi_message_init(&st->scan_msg);
> +
> + first = true;
> + prev_i = 0;
> + k = 0;
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
> + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> + /*
> + * The pipeline means xfer[0] receives the residual from the
> + * previous sequence, not a valid sample for channel i. Point
> + * it at vals[i] anyway; xfer[1] (or the NOOP when only one
> + * channel is active) will overwrite that slot with the real
> + * result, so no separate dummy buffer is needed.
> + */
> + if (first) {
> + st->scan_xfers[k].rx_buf = &st->vals[i];
> + first = false;
> + } else {
> + st->scan_xfers[k].rx_buf = &st->vals[prev_i];
> + }
"The IIO subsystem expects data pushed to the buffer to be densely packed
according to the active channels in the scan mask.
If only a subset of channels are enabled, does assigning the rx_buf pointer
directly to absolute array indices at &st->vals[i] leave holes in the buffer?
When iio_push_to_buffers_with_ts() is called, this might cause it to read
uninitialized memory instead of the expected samples."
I would say there is no change needed. Writing to &st->vals[scan_index] and
passing the full array to iio_push_to_buffers_with_ts() is the standard IIO kfifo
pattern: the core demultiplexes by reading data[scan_index * storagebits/8]
for each active channel; holes at inactive indices are silently ignored.
The same pattern is used in ad4695, ad_sigma_delta, and others. The
pipeline residual in the first manual-mode transfer is overwritten by the
subsequent transfer before the scan is pushed, as the comment explains.
> + st->scan_xfers[k].len = sizeof(__be16);
> + st->scan_xfers[k].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> + prev_i = i;
> + k++;
> + }
> +
...
> +static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + unsigned int k, i;
> + int ret;
> +
> + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
> + memset(st->scan_tx, 0, sizeof(st->scan_tx));
> +
> + spi_message_init(&st->scan_msg);
> +
> + /*
> + * Each AVG_IN read needs two transfers: a 2-byte address write phase
> + * followed by a 2-byte data read phase. CS toggles between channels
> + * (cs_change=1 on the read phase of all but the last channel).
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, i) {
> + st->scan_tx[k] = cpu_to_be16(0x8000 | AD4691_AVG_IN(i));
> + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[2 * k].len = sizeof(__be16);
> + spi_message_add_tail(&st->scan_xfers[2 * k], &st-
> >scan_msg);
> + st->scan_xfers[2 * k + 1].rx_buf = &st->vals[i];
> + st->scan_xfers[2 * k + 1].len = sizeof(__be16);
> + st->scan_xfers[2 * k + 1].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st-
> >scan_msg);
> + k++;
> + }
> +
> + st->scan_tx[k] = cpu_to_be16(AD4691_STATE_RESET_REG);
> + st->scan_xfers[2 * k].tx_buf = &st->scan_tx[k];
> + st->scan_xfers[2 * k].len = sizeof(__be16);
> + spi_message_add_tail(&st->scan_xfers[2 * k], &st->scan_msg);
> + st->scan_tx[k + 1] = cpu_to_be16(AD4691_STATE_RESET_ALL << 8);
"Does the iio_for_each_active_channel() loop iterate over the soft timestamp
channel if it is enabled?
If the soft timestamp channel is enabled, k would reach 17. Because
st->scan_tx is sized for 18 elements, does writing to st->scan_tx[k + 1] and
st->scan_xfers[2 * k + 1] overflow the bounds of these arrays?"
Valid defensive concern. With all data channels + timestamp enabled,
the loop iterates scan_index=16 (timestamp), pushing k to 17, and the
state-reset then accesses scan_xfers[34] which is out-of-bounds (array is
sized 34). Fixed by adding a guard to break out of the loop when
i >= num_channels - 1 (the timestamp scan_index), consistent with how
both ad4691_channels and ad4693_channels are laid out.
"Also, the AD4691 auto-decrements register addresses on multi-byte writes.
Could writing a 2-byte payload to the 1-byte AD4691_STATE_RESET_REG (0x181)
inadvertently write 0x00 to the adjacent lower register 0x180
(AD4691_OSC_EN_REG), disabling the internal oscillator?"
Real bug. The two back-to-back 2-byte transfers ([0x01,0x81] then
[0x01,0x00]) are sent with CS continuously asserted, so the chip sees a
4-byte write: 0x01 to STATE_RESET_REG and 0x00 to OSC_EN_REG, disabling
the oscillator mid-scan. Fixed by using a single 4-byte transfer
[addr_hi, addr_lo, STATE_RESET_ALL, OSC_EN=1] in a dedicated
u8 scan_tx_reset[4] field. The fourth byte deliberately re-enables
OSC_EN_REG (0x180) via ADDR_DESCENDING as a side-write, keeping the
oscillator enabled. This same buffer is reused by the offload path in
the following commit (the two paths are mutually exclusive at probe).
> + st->scan_xfers[2 * k + 1].tx_buf = &st->scan_tx[k + 1];
> + st->scan_xfers[2 * k + 1].len = sizeof(__be16);
> + st->scan_xfers[2 * k + 1].cs_change = 1;
> + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);
> +
> + ret = spi_optimize_message(st->spi, &st->scan_msg);
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)));
> + if (ret)
> + goto err_unoptimize;
> +
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> + ~bitmap_read(indio_dev->active_scan_mask, 0,
> + iio_get_masklength(indio_dev)) &
> GENMASK(15, 0));
> + if (ret)
> + goto err_unoptimize;
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + goto err_unoptimize;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto err_exit_conv;
> +
> + enable_irq(st->irq);
> + return 0;
"Is there a race condition introduced by enabling the PWM and unmasking the
IRQ here?
If a hardware interrupt fires before the IIO core attaches the trigger's poll
function, iio_trigger_poll() drops the event. Will the IRQ handler then call
disable_irq_nosync() without ever running the consumer thread to re-enable it?"
Valid. preenable is called before the IIO core attaches the trigger
poll function; if a DATA_READY IRQ fires in that window, iio_trigger_poll()
is dropped, disable_irq_nosync() disables the IRQ, and enable_irq() is
never called, leaving the IRQ stuck. Although the delay would need to be
very great for this to happen, I moved sampling_enable(true) and
enable_irq() to a new postenable callback which the IIO core calls only
after the trigger poll function is attached.
> +
> +err_exit_conv:
> + ad4691_exit_conversion_mode(st);
> +err_unoptimize:
> + spi_unoptimize_message(&st->scan_msg);
> + return ret;
> +}
> +
> +static int ad4691_cnv_burst_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + disable_irq(st->irq);
"During buffer teardown, the IIO core detaches the trigger consumer before
invoking the postdisable callback.
If an interrupt fires while the PWM is still running before the call to
ad4691_sampling_enable(), could ad4691_irq() disable the IRQ without a
consumer to re-enable it? Does the subsequent disable_irq() call then increase
the disable depth to 2, permanently disabling the hardware IRQ?"
Valid, fixed as part of the previous concern rework. IRQ disabling and
sampling_enable(false) are now in a new predisable callback (called before
the consumer is detached); postdisable only handles SPI cleanup.
> +
> + ret = ad4691_sampling_enable(st, false);
> + if (ret)
> + return ret;
"If ad4691_sampling_enable() fails, does returning early bypass
spi_unoptimize_message() and leak the DMA mappings allocated by the SPI core
during preenable?"
Valid. If pwm_apply_might_sleep() inside sampling_enable(false) fails,
spi_unoptimize_message() was skipped. Always called now before returning
from postdisable regardless of sampling_enable() result.
> +
> + ret = ad4691_exit_conversion_mode(st);
...
> +static ssize_t sampling_frequency_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct ad4691_state *st = iio_priv(indio_dev);
> + int freq, ret;
> +
> + ret = kstrtoint(buf, 10, &freq);
> + if (ret)
> + return ret;
> +
> + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> + if (IIO_DEV_ACQUIRE_FAILED(claim))
> + return -EBUSY;
> +
> +
> + ret = ad4691_set_pwm_freq(st, freq);
"If a negative value is passed to kstrtoint(), does ad4691_set_pwm_freq()
calculate a cnv_period_ns of 0 due to signed integer arithmetic rules?
Could this lead to a division by zero regression in sampling_frequency_show()
when it calculates NSEC_PER_SEC / st->cnv_period_ns?"
Valid. Switching sampling_frequency_store to kstrtouint + unsigned int freq so
Negative strings are rejected at parse time before reaching set_pwm_freq.
> + if (ret)
> + return ret;
> +
> + return len;
> +}
> +
> +static IIO_DEVICE_ATTR_RW(sampling_frequency, 0);
> +
> +static const struct iio_dev_attr *ad4691_buffer_attrs[] = {
> + &iio_dev_attr_sampling_frequency,
> + NULL
> +};
> +
> +static irqreturn_t ad4691_irq(int irq, void *private)
> +{
> + struct iio_dev *indio_dev = private;
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> + iio_trigger_poll(indio_dev->trig);
> + /*
> + * Keep the DATA_READY IRQ disabled until the trigger handler has
> + * finished reading the scan, to prevent a new assertion mid-transfer.
> + * The PWM continues running uninterrupted; the IRQ is re-enabled in
> + * ad4691_trigger_handler once spi_sync completes.
> + *
> + * IRQF_ONESHOT already masks the hardware line during this
> threaded
> + * handler, so disable_irq_nosync here ensures the IRQ stays disabled
> + * even after IRQF_ONESHOT unmasks on return.
> + */
> + disable_irq_nosync(st->irq);
> +
> + return IRQ_HANDLED;
> +}
"Can a concurrency issue occur here between this threaded IRQ handler and the
trigger consumer thread?
Since iio_trigger_poll() wakes up the consumer thread, could the consumer
thread complete the SPI transfer and call enable_irq() before the provider
thread reaches disable_irq_nosync()? This might trigger an unbalanced enable
for IRQ warning and leave the hardware IRQ permanently disabled."
Valid. Moved disable_irq_nosync() before iio_trigger_poll() so the
IRQ is always disabled before the consumer thread gets a chance to call
enable_irq().
> +
> +static const struct iio_trigger_ops ad4691_trigger_ops = {
> + .validate_device = iio_trigger_validate_own_device,
...
> +
> static const struct iio_info ad4691_info = {
> .read_raw = &ad4691_read_raw,
> .write_raw = &ad4691_write_raw,
> @@ -514,6 +910,18 @@ static const struct iio_info ad4691_info = {
> .debugfs_reg_access = &ad4691_reg_access,
> };
"Without a .validate_trigger callback here, can external triggers be attached?
If an external trigger fires, the consumer thread might execute
enable_irq(st->irq) without a corresponding hardware interrupt calling
disable_irq_nosync() first, leading to an unbalanced IRQ enable warning."
Valid. Added .validate_trigger = iio_validate_own_trigger to
ad4691_info to prevent external triggers from causing unbalanced
enable_irq() calls.
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-4-33e439e4fb87@analog.com>
2026-05-05 14:12 ` [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support Jonathan Cameron
2026-05-05 14:28 ` Jonathan Cameron
@ 2026-05-07 11:49 ` Sabau, Radu bogdan
2026-05-07 15:11 ` Jonathan Cameron
2 siblings, 1 reply; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-07 11:49 UTC (permalink / raw)
To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, David Lechner, Sa, Nuno, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
Addressing Sashiko's review for the offload support patch.
> -----Original Message-----
> From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> Sent: Thursday, April 30, 2026 1:17 PM
...
> All offload transfers use 16-bit frames (bits_per_word=16, len=2).
> The channel scan_type (storagebits=16, shift=0, IIO_BE) is shared
> between the software triggered-buffer and offload paths; no separate
> scan_type or channel array is needed for the offload case. The
> ad4691_manual_channels[] array introduced in the triggered-buffer
> commit is reused here: it hides the IIO_CHAN_INFO_OVERSAMPLING_RATIO
> attribute, which is not applicable in Manual Mode.
"This isn't a bug, but ad4691_manual_channels and the oversampling attribute
don't appear to exist in this driver. Was this copied from another driver?"
Agreed on the commit message as per Jonathan's input as well.
Removed the two sentences referencing ad4691_manual_channels[] and
IIO_CHAN_INFO_OVERSAMPLING_RATIO — both were carried over from a
draft that included OSR support, which was split into the following commit.
The updated message now calls out explicitly that per-mode channel array
distinctions and oversampling support are introduced in the next commit.
>
> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
> ---
> drivers/iio/adc/Kconfig | 2 +
> drivers/iio/adc/ad4691.c | 392
> ++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 389 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index d498f16c0816..fdc6565933c5 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -143,8 +143,10 @@ config AD4691
> tristate "Analog Devices AD4691 Family ADC Driver"
> depends on SPI
> select IIO_BUFFER
> + select IIO_BUFFER_DMAENGINE
> select IIO_TRIGGERED_BUFFER
> select REGMAP
> + select SPI_OFFLOAD
> help
> Say yes here to build support for Analog Devices AD4691 Family
> MuxSAR
> SPI analog to digital converters (ADC).
> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
> index c1e3406fef18..50d81d87d4a0 100644
> --- a/drivers/iio/adc/ad4691.c
> +++ b/drivers/iio/adc/ad4691.c
> @@ -23,10 +23,14 @@
...
> +struct ad4691_offload_state {
> + struct spi_offload *offload;
> + struct spi_offload_trigger *trigger;
> + u64 trigger_hz;
> + u8 tx_cmd[17][2];
> + u8 tx_reset[4];
> };
"Could using these buffers for DMA transfers cause memory corruption on
architectures with non-coherent caches?
They are not marked with __aligned(IIO_DMA_MINALIGN), so they share cachelines
with each other and adjacent struct members like trigger_hz."
Valid. struct ad4691_offload_state is allocated with devm_kzalloc
which does not guarantee cache-line alignment, so tx_cmd and tx_reset
shared cache lines with trigger_hz and the pointer fields. Removed both
from ad4691_offload_state entirely. The existing scan_tx[17] field in
struct ad4691_state is large enough for all 17 AVG_IN addresses and is
already __aligned(IIO_DMA_MINALIGN); reused it for the offload TX
commands (the two paths are mutually exclusive at probe).
>
> struct ad4691_state {
> @@ -252,7 +286,10 @@ struct ad4691_state {
> * transfers in one go.
> */
> struct spi_message scan_msg;
...
> +
> +static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev
> *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> + struct device *dev = regmap_get_device(st->regmap);
> + struct spi_device *spi = to_spi_device(dev);
...
> + /* TX: address phase, CS stays asserted into data phase */
> + st->scan_xfers[2 * k].tx_buf = offload->tx_cmd[k];
> + st->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);
> + st->scan_xfers[2 * k].bits_per_word = bpw;
"When bits_per_word is greater than 8 (like bpw = 16 here), the SPI framework
treats tx_buf as an array of native 16-bit words.
On little-endian architectures, the controller will byte-swap the data before
transmitting it. Will using a u8 array and put_unaligned_be16() result in the
command bytes being reversed on the wire?"
Switched to cpu_to_be16() assigned directly into __be16 scan_tx[],
matching the non-offload path. This makes the intended wire format
self-evident and sidesteps the byte-ordering question entirely.
> +
> + /* RX: data phase, CS toggles after to delimit the next register
> op */
> + st->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);
> + st->scan_xfers[2 * k + 1].bits_per_word = bpw;
> + st->scan_xfers[2 * k + 1].offload_flags =
> SPI_OFFLOAD_XFER_RX_STREAM;
> + st->scan_xfers[2 * k + 1].cs_change = 1;
> + k++;
> + }
> +
> + /* State reset to re-arm DATA_READY for the next scan. */
> + put_unaligned_be16(AD4691_STATE_RESET_REG, offload->tx_reset);
> + offload->tx_reset[2] = AD4691_STATE_RESET_ALL;
> +
> + st->scan_xfers[2 * k].tx_buf = offload->tx_reset;
> + st->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);
> + st->scan_xfers[2 * k].bits_per_word = bpw;
> +
> + st->scan_xfers[2 * k + 1].tx_buf = &offload->tx_reset[2];
> + st->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);
"Will passing &offload->tx_reset[2] directly as tx_buf cause DMA mapping
issues, since it is only 2-byte aligned?"
Addressed by the second concern's fix: no sub-aligned pointer into the middle
of a DMA buffer — the full 4-byte scan_tx_reset[] is passed as tx_buf
from its own cache-line-aligned start address.
> + st->scan_xfers[2 * k + 1].bits_per_word = bpw;
> + st->scan_xfers[2 * k + 1].cs_change = 1;
The ADI SPI Engine always deasserts CS at the end of its programmed
sequence regardless, though I will drop this in order to avoid confusion.
> +
> + spi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, 2 * k
> + 2);
> + st->scan_msg.offload = offload->offload;
> +
> + ret = spi_optimize_message(spi, &st->scan_msg);
> + if (ret)
> + goto err_exit_conversion;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto err_unoptimize;
> +
> + ret = spi_offload_trigger_enable(offload->offload, offload->trigger,
> &config);
"If the PWM starts generating DATA_READY trigger edges in ad4691_sampling_enable
before the SPI offload trigger is enabled, could the offload engine miss the
first trigger edge?
If that first edge is missed, wouldn't the DATA_READY line remain permanently
asserted (because the STATE_RESET command is never sent), deadlocking the
entire capture?"
Valid. Swapped the order: spi_offload_trigger_enable() first, then
ad4691_sampling_enable(), so the offload engine is armed before the first
CNV pulse fires. Without this, if DATA_READY asserts in the window between
sampling_enable() and trigger_enable(), the SPI Engine misses the rising
edge and the capture deadlocks.
> + if (ret)
> + goto err_sampling_disable;
> +
> + return 0;
> +
> +err_sampling_disable:
> + ad4691_sampling_enable(st, false);
> +err_unoptimize:
> + spi_unoptimize_message(&st->scan_msg);
> +err_exit_conversion:
> + ad4691_exit_conversion_mode(st);
> + return ret;
> +}
> +
> +static int ad4691_cnv_burst_offload_buffer_predisable(struct iio_dev
> *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + struct ad4691_offload_state *offload = st->offload;
> + int ret;
> +
> + spi_offload_trigger_disable(offload->offload, offload->trigger);
> +
> + ret = ad4691_sampling_enable(st, false);
> + if (ret)
> + return ret;
> +
> + spi_unoptimize_message(&st->scan_msg);
"If ad4691_sampling_enable() fails and returns early, won't this bypass
spi_unoptimize_message() and permanently leak the DMA mappings and memory
allocated during SPI message optimization?"
Valid. spi_unoptimize_message() is now always called before returning
from predisable, regardless of sampling_enable() result.
> +
> + return ad4691_exit_conversion_mode(st);
> +}
> +
> +static const struct iio_buffer_setup_ops
> ad4691_cnv_burst_offload_buffer_setup_ops = {
> + .postenable = &ad4691_cnv_burst_offload_buffer_postenable,
> + .predisable = &ad4691_cnv_burst_offload_buffer_predisable,
> +};
> +
> static ssize_t sampling_frequency_show(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> @@ -810,6 +1093,9 @@ static ssize_t sampling_frequency_show(struct
> device *dev,
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> struct ad4691_state *st = iio_priv(indio_dev);
>
> + if (st->manual_mode && st->offload)
> + return sysfs_emit(buf, "%llu\n", st->offload->trigger_hz);
Valid. Using READ_ONCE in show and WRITE_ONCE in store for trigger_hz;
on 32-bit architectures a bare u64 read/write is not atomic.
> +
> return sysfs_emit(buf, "%lu\n", NSEC_PER_SEC / st->cnv_period_ns);
> }
>
> @@ -829,6 +1115,19 @@ static ssize_t sampling_frequency_store(struct
> device *dev,
> if (IIO_DEV_ACQUIRE_FAILED(claim))
> return -EBUSY;
>
> + if (st->manual_mode && st->offload) {
> + struct spi_offload_trigger_config config = {
> + .type = SPI_OFFLOAD_TRIGGER_PERIODIC,
> + .periodic = { .frequency_hz = freq },
> + };
"Does this code safely handle zero or negative inputs?
kstrtoint parses freq as a signed int, and the existing validation
(if (!freq) return -EINVAL) is bypassed for the offload path.
A negative value would sign-extend to a massive u64 value when assigned to
frequency_hz."
Addressed per previous patch.
> +
> + ret = spi_offload_trigger_validate(st->offload->trigger,
> &config);
...
> - ret = ad4691_setup_triggered_buffer(indio_dev, st);
> + if (spi_offload) {
> + indio_dev->channels = st->info->offload_info->channels;
> + indio_dev->num_channels = st->info->offload_info-
> >num_channels;
> + ret = ad4691_setup_offload(indio_dev, st, spi_offload);
"The offload path reuses the existing channels array which defines
scan_type.endianness = IIO_BE.
Since the SPI offload DMA RX stream delivers data as native 16-bit words,
which are little-endian on LE platforms, will the IIO subsystem incorrectly
byte-swap the native data before passing it to userspace?"
Agreed. AD4691_CHANNEL no longer sets .endianness, matching the
ad4695 approach and defaulting to IIO_CPU.
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 5/6] iio: adc: ad4691: add oversampling support
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-5-33e439e4fb87@analog.com>
2026-05-05 14:32 ` [PATCH v9 5/6] iio: adc: ad4691: add oversampling support Jonathan Cameron
@ 2026-05-07 11:56 ` Sabau, Radu bogdan
2026-05-07 15:26 ` Jonathan Cameron
1 sibling, 1 reply; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-07 11:56 UTC (permalink / raw)
To: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
Jonathan Cameron, David Lechner, Sa, Nuno, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
Addressing Sashiko's review for the oversampling support patch.
> -----Original Message-----
> From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> Sent: Thursday, April 30, 2026 1:17 PM
...
> +/* CNV burst mode channel — exposes oversampling ratio. */
> #define AD4691_CHANNEL(ch)
> \
> + { \
> + .type = IIO_VOLTAGE, \
> + .indexed = 1, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> \
> +
> BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .info_mask_separate_available =
> \
> +
> BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
"The IIO_CHAN_INFO_SAMP_FREQ attribute is exported with info_mask_separate,
implying per-channel independence. However, writing to sampling_frequency
updates st->target_osc_freq_Hz, a global variable shared by all channels.
Does modifying this global variable on one channel silently alter the
effective sampling rate of all other channels? Should this use
info_mask_shared_by_all to comply with the IIO ABI?"
info_mask_separate is intentional here. The oscillator is shared, but
the effective output rate is genuinely per-channel: osc_freq / osr[N].
Writing in_voltageN_sampling_frequency sets the oscillator (which changes
the read-back for all channels), but reading it back for any channel always
returns osc_freq / osr[N] — a value specific to that channel's OSR. The
oscillator snaps to the largest table entry <= freq * osr[N] that is evenly
divisible by osr[N], so the read-back is always an integer for the channel
that drove the write. The same trade-off is present in ad4695.
info_mask_separate is the correct annotation for this per-channel view.
> + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),
> \
> + .channel = ch, \
> + .scan_index = ch, \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + .endianness = IIO_BE, \
> + }, \
> + }
> +
...
> static int ad4691_read_avail(struct iio_dev *indio_dev,
> @@ -540,10 +655,30 @@ static int ad4691_read_avail(struct iio_dev
> *indio_dev,
> unsigned int start = ad4691_samp_freq_start(st->info);
>
> switch (mask) {
> - case IIO_CHAN_INFO_SAMP_FREQ:
> - *vals = &ad4691_osc_freqs_Hz[start];
> + case IIO_CHAN_INFO_SAMP_FREQ: {
> + unsigned int osr = st->osr[chan->channel];
> + int n = 0;
> +
> + /*
> + * Only oscillator frequencies evenly divisible by the channel's
> + * OSR yield an integer effective rate; expose those as effective
> + * rates (osc / osr) so the user works entirely in output-sample
> + * space.
> + */
> + for (unsigned int i = start; i <
> ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
> + if (ad4691_osc_freqs_Hz[i] % osr != 0)
> + continue;
> + st->samp_freq_avail[n++] = ad4691_osc_freqs_Hz[i] /
> osr;
> + }
> + *vals = st->samp_freq_avail;
"The IIO core calls read_avail() without holding driver-level locks that
serialize sysfs reads. Can concurrent reads of
in_voltageN_sampling_frequency_available for channels with different OSR
values race and overwrite st->samp_freq_avail simultaneously?
This could result in interleaved or corrupted output being formatted by the
IIO core and sent to userspace. Is a per-channel array or synchronization
required here?"
Valid. Sysfs serializes reads to the same sysfs file but not across
different channel _available files. Concurrent reads of
in_voltage0_sampling_frequency_available and
in_voltage1_sampling_frequency_available (with different OSRs) raced on
the shared samp_freq_avail[] scratch array. Made it a 2D array
[16][N_OSC_ENTRIES] so each channel has its own sub-array; concurrent
reads for different channels no longer interfere.
> *type = IIO_VAL_INT;
> - *length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;
> + *length = n;
> + return IIO_AVAIL_LIST;
...
> @@ -634,9 +780,29 @@ static int ad4691_write_raw(struct iio_dev
> *indio_dev,
> struct iio_chan_spec const *chan,
> int val, int val2, long mask)
> {
> + struct ad4691_state *st = iio_priv(indio_dev);
> +
> switch (mask) {
> case IIO_CHAN_INFO_SAMP_FREQ:
> - return ad4691_set_sampling_freq(indio_dev, val);
> + return ad4691_set_sampling_freq(indio_dev, chan, val);
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
> + IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> + if (IIO_DEV_ACQUIRE_FAILED(claim))
> + return -EBUSY;
> +
> + for (unsigned int i = 0; i <
> ARRAY_SIZE(ad4691_oversampling_ratios); i++) {
> + if (ad4691_oversampling_ratios[i] != val)
> + continue;
> + /*
> + * Store the new OSR; target_osc_freq_Hz is
> unchanged.
> + * The effective rate read back via
> in_voltageN_sampling_frequency
> + * becomes target_osc_freq_Hz / new_osr
> automatically.
> + */
> + st->osr[chan->channel] = val;
"Writing oversampling_ratio updates osr but leaves target_osc_freq_Hz
unchanged. Does this break the target_osc_freq_Hz % osr == 0 guarantee
enforced in ad4691_set_sampling_freq()?
Reading sampling_frequency after this could yield a truncated integer. If
that exact integer is written back, it might compute a smaller oscillator
requirement and snap the device to a different, lower oscillator frequency.
Does modifying oversampling_ratio need to recalculate target_osc_freq_Hz
to maintain state consistency?"
Valid. Storing a new OSR without updating target_osc_freq_Hz can
break the target_osc_freq_Hz % osr == 0 invariant. For example with
target_osc_freq_Hz = 250 kHz and new OSR = 32: read-back is
250000 / 32 = 7812 (truncated); writing that value back computes
needed_osc = 249984, snapping the oscillator to the next lower table
entry — a silent frequency change on what the user expected to be a
no-op write-back.
Fixed: the OVERSAMPLING_RATIO write path now calls ad4691_find_osc_freq()
with old_effective * new_osr as the ceiling, where
old_effective = target_osc_freq_Hz / old_osr. This preserves the
divisibility invariant and keeps the oscillator as close as possible to
the previous effective rate.
> + return 0;
> + }
> + return -EINVAL;
> + }
> default:
> return -EINVAL;
> }
> @@ -691,6 +857,10 @@ static int ad4691_enter_conversion_mode(struct
> ad4691_state *st)
> return regmap_update_bits(st->regmap,
> AD4691_DEVICE_SETUP,
> AD4691_MANUAL_MODE,
> AD4691_MANUAL_MODE);
>
> + ret = ad4691_write_osc_freq(st);
> + if (ret)
> + return ret;
> +
> ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
> AD4691_ADC_MODE_MASK,
> AD4691_CNV_BURST_MODE);
> if (ret)
> @@ -844,6 +1014,12 @@ static int
> ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> if (ret)
> goto err_unoptimize;
>
> + iio_for_each_active_channel(indio_dev, i) {
> + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(i),
> st->osr[i]);
> + if (ret)
> + goto err_unoptimize;
> + }
> +
"If the IIO soft timestamp channel is enabled, its scan_index is 16.
iio_for_each_active_channel() iterates over all active channels including
the timestamp channel.
Will this loop process i = 16 and access st->osr[16] out of bounds, since
st->osr is an array of size 16?
Additionally, since AD4691_ACC_DEPTH_IN(16) resolves to 0x196
(AD4691_GPIO_MODE1_REG), will this silently corrupt the GPIO configuration
and potentially break the DATA_READY interrupt?
Furthermore, does the presence of the timestamp channel cause the loop
iteration to advance k to 17, making the state-reset SPI transfers appended
after the loop write to st->scan_xfers[34] and [35]? This would overflow
the scan_xfers array which is sized 34 and could corrupt the surrounding
struct ad4691_state."
Valid for the osr[] out-of-bounds and GPIO corruption concerns. With
the soft timestamp enabled (scan_index = 16), the loop would access
st->osr[16] out-of-bounds and write to AD4691_ACC_DEPTH_IN(16) = 0x196,
which is AD4691_GPIO_MODE1_REG, silently corrupting the GPIO configuration
and potentially breaking the DATA_READY interrupt.
Added the same guard used by the scan_xfers loops in the triggered-buffer
commit: if (i >= indio_dev->num_channels - 1) break.
The scan_xfers k-overflow concern is already handled by that existing guard
in a separate loop — it is not affected by the ACC_DEPTH_IN loop added
here.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family
2026-05-07 9:26 ` Sabau, Radu bogdan
@ 2026-05-07 14:15 ` Jonathan Cameron
2026-05-08 4:44 ` Andy Shevchenko
0 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-07 14:15 UTC (permalink / raw)
To: Sabau, Radu bogdan
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Thu, 7 May 2026 09:26:00 +0000
"Sabau, Radu bogdan" <Radu.Sabau@analog.com> wrote:
> Addressing Sashiko's review for initial driver's patch.
>
> > -----Original Message-----
> > From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> > Sent: Thursday, April 30, 2026 1:17 PM
>
> ...
>
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index 60038ae8dfc4..3685a03aa8dc 100644
> > --- a/drivers/iio/adc/Kconfig
> > +++ b/drivers/iio/adc/Kconfig
> > @@ -139,6 +139,17 @@ config AD4170_4
> > To compile this driver as a module, choose M here: the module will be
> > called ad4170-4.
> >
> > +config AD4691
> > + tristate "Analog Devices AD4691 Family ADC Driver"
> > + depends on SPI
>
> "Should this driver also depend on REGULATOR? In ad4691_regulator_setup(),
> it relies on devm_regulator_get_enable_read_voltage() to obtain its reference
> voltage. If the kernel is compiled without CONFIG_REGULATOR, this function
> is stubbed to return -ENODEV, causing probe to fail unconditionally."
>
> Will add "depends on REGULATOR". Without it,
> devm_regulator_get_enable_read_voltage() can be stubbed and
> return -ENODEV, making the driver unusable regardless of hardware.
If you really want to also add || COMPILE_TEST because
I want maximum build coverage and for that I don't mind if the driver
can actually probe or not.
>
> > + select REGMAP
> > + help
> > + Say yes here to build support for Analog Devices AD4691 Family
>
> ...
>
> > +};
> > +
> > +#define AD4691_CHANNEL(ch)
> > \
> > + { \
> > + .type = IIO_VOLTAGE, \
> > + .indexed = 1, \
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
> > \
> > + | BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> > + .info_mask_separate_available =
> > \
> > + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> > + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),
> > \
>
> "Since ad4691_set_sampling_freq() modifies the AD4691_OSC_FREQ_REG register
> which controls the global internal oscillator frequency for the entire ADC,
> should IIO_CHAN_INFO_SAMP_FREQ be exposed as info_mask_shared_by_all
> instead of info_mask_separate? Changing the frequency for one channel
> currently seems to silently alter it for all channels."
>
> Agreed for this commit. With no per-channel OSR in the base driver
> all channels share a single oscillator register, so info_mask_shared_by_all
> is the correct annotation. Changing accordingly. The OSR commit switches it
> back to info_mask_separate because at that point the effective rate is
> genuinely per-channel (osc_freq / osr[N]).
Don't. That would mean an ABI change mid way through the series which we
don't want. Just add a note to the patch description.
...
>
> > +
> > +static int ad4691_reset(struct ad4691_state *st)
> > +{
> > + struct device *dev = regmap_get_device(st->regmap);
> > + struct reset_control *rst;
> > +
> > + rst = devm_reset_control_get_optional_exclusive(dev, NULL);
> > + if (IS_ERR(rst))
> > + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get
> > reset\n");
> > +
> > + if (rst) {
> > + /*
> > + * reset_gpio_probe() already drives the pin asserted, so the
> > + * device is held in reset before we get here.
> > + * devm_reset_control_get_optional_exclusive_deasserted()
> > cannot
> > + * be used because it deasserts immediately without delay; the
> > + * datasheet (Table 5) requires a ≥300 µs reset pulse width
> > + * before deassertion.
> > + */
> > + fsleep(300);
> > + return reset_control_deassert(rst);
>
> "Could this sequence break on driver reload?
> When the driver is unloaded, the reset line remains in its deasserted state
> because there is no cleanup action to re-assert it. Upon reloading, the 300
> microsecond sleep will occur while the pin is already deasserted, meaning
> the hardware might not receive the required reset pulse.
> Should reset_control_assert() be called explicitly prior to the delay?"
>
> Valid. reset_control_put() does not re-assert the line at unload, so
> on reload the pin is already deasserted when we enter the 300 µs sleep.
> Adding reset_control_assert(rst) before fsleep(300) to guarantee a proper
> reset pulse on every probe including reloads.
I'm not that fussed about driver reload bugs like this one but why
not fix it I guess. Lots of cases of this in upstream code though.
>
> > + }
> > +
>
> ...
>
> > +
> > + st = iio_priv(indio_dev);
> > + st->info = spi_get_device_match_data(spi);
>
> "Is it possible for st->info to be NULL here?
> If the driver is manually bound to a device with a non-matching name using
> the sysfs bind interface in combination with driver_override,
> spi_get_device_match_data() could return NULL, which would cause a NULL
> pointer dereference later in the probe sequence."
>
> Agreed, will add the NULL check with -ENODEV immediately after
> spi_get_device_match_data().
Andy, you seeing this one? Looks like we are putting these checks back in again.
Whilst anyone forcing a bind like this is onto a looser anyway we shouldn't
crash due to a null dereference.
>
> > +
> > + ret = devm_mutex_init(dev, &st->lock);
> > + if (ret)
> > + return ret;
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
2026-05-07 11:37 ` Sabau, Radu bogdan
@ 2026-05-07 14:25 ` Jonathan Cameron
2026-05-08 11:08 ` Sabau, Radu bogdan
0 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-07 14:25 UTC (permalink / raw)
To: Sabau, Radu bogdan
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Thu, 7 May 2026 11:37:25 +0000
"Sabau, Radu bogdan" <Radu.Sabau@analog.com> wrote:
> Addressing Sashiko's review for triggered buffer patch.
>
> > -----Original Message-----
> > From: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
> > Sent: Thursday, April 30, 2026 1:17 PM
>
> ...
>
> > +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> > +{
> > + struct ad4691_state *st = iio_priv(indio_dev);
> > + unsigned int prev_i, k, i;
> > + bool first;
> > + int ret;
> > +
> > + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
> > + memset(st->scan_tx, 0, sizeof(st->scan_tx));
> > +
> > + spi_message_init(&st->scan_msg);
> > +
> > + first = true;
> > + prev_i = 0;
> > + k = 0;
> > + iio_for_each_active_channel(indio_dev, i) {
> > + st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
> > + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> > + /*
> > + * The pipeline means xfer[0] receives the residual from the
> > + * previous sequence, not a valid sample for channel i. Point
> > + * it at vals[i] anyway; xfer[1] (or the NOOP when only one
> > + * channel is active) will overwrite that slot with the real
> > + * result, so no separate dummy buffer is needed.
> > + */
> > + if (first) {
> > + st->scan_xfers[k].rx_buf = &st->vals[i];
> > + first = false;
> > + } else {
> > + st->scan_xfers[k].rx_buf = &st->vals[prev_i];
> > + }
>
>
> "The IIO subsystem expects data pushed to the buffer to be densely packed
> according to the active channels in the scan mask.
> If only a subset of channels are enabled, does assigning the rx_buf pointer
> directly to absolute array indices at &st->vals[i] leave holes in the buffer?
> When iio_push_to_buffers_with_ts() is called, this might cause it to read
> uninitialized memory instead of the expected samples."
>
> I would say there is no change needed. Writing to &st->vals[scan_index] and
> passing the full array to iio_push_to_buffers_with_ts() is the standard IIO kfifo
> pattern: the core demultiplexes by reading data[scan_index * storagebits/8]
> for each active channel; holes at inactive indices are silently ignored.
> The same pattern is used in ad4695, ad_sigma_delta, and others. The
> pipeline residual in the first manual-mode transfer is overwritten by the
> subsequent transfer before the scan is pushed, as the comment explains.
This looks wrong to me.
What holes? If available_scan_masks is set we will do a bunch of
demux work - but then this code would see the mask picked from that
list. If it's not then typically we won't (subject to multiple consumers
forcing it - but that still won't close up holes here).
If the active_scan_mask == the one requested, there is no demux at all
and I think that's the case here - the code pushes the data passed in
directly to the kfifo.
Perhaps given an illustration of what the layout of resulting data
is if only even numbered channels are enabled.
>
> > + st->scan_xfers[k].len = sizeof(__be16);
> > + st->scan_xfers[k].cs_change = 1;
> > + spi_message_add_tail(&st->scan_xfers[k], &st->scan_msg);
> > + prev_i = i;
> > + k++;
> > + }
> > +
>
>
> > + st->scan_xfers[2 * k + 1].len = sizeof(__be16);
> > + st->scan_xfers[2 * k + 1].cs_change = 1;
> > + spi_message_add_tail(&st->scan_xfers[2 * k + 1], &st->scan_msg);
> > +
> > + ret = spi_optimize_message(st->spi, &st->scan_msg);
> > + if (ret)
> > + return ret;
> > +
> > + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> > + bitmap_read(indio_dev->active_scan_mask, 0,
> > + iio_get_masklength(indio_dev)));
> > + if (ret)
> > + goto err_unoptimize;
> > +
> > + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> > + ~bitmap_read(indio_dev->active_scan_mask, 0,
> > + iio_get_masklength(indio_dev)) &
> > GENMASK(15, 0));
> > + if (ret)
> > + goto err_unoptimize;
> > +
> > + ret = ad4691_enter_conversion_mode(st);
> > + if (ret)
> > + goto err_unoptimize;
> > +
> > + ret = ad4691_sampling_enable(st, true);
> > + if (ret)
> > + goto err_exit_conv;
> > +
> > + enable_irq(st->irq);
> > + return 0;
>
> "Is there a race condition introduced by enabling the PWM and unmasking the
> IRQ here?
> If a hardware interrupt fires before the IIO core attaches the trigger's poll
> function, iio_trigger_poll() drops the event. Will the IRQ handler then call
> disable_irq_nosync() without ever running the consumer thread to re-enable it?"
>
> Valid. preenable is called before the IIO core attaches the trigger
> poll function; if a DATA_READY IRQ fires in that window, iio_trigger_poll()
> is dropped, disable_irq_nosync() disables the IRQ, and enable_irq() is
> never called, leaving the IRQ stuck. Although the delay would need to be
> very great for this to happen, I moved sampling_enable(true) and
> enable_irq() to a new postenable callback which the IIO core calls only
> after the trigger poll function is attached.
Make sure to add a comment on why that is there.
Otherwise makes sense.
>
Rest look fine to me.
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
2026-05-07 11:49 ` Sabau, Radu bogdan
@ 2026-05-07 15:11 ` Jonathan Cameron
2026-05-08 11:11 ` Sabau, Radu bogdan
0 siblings, 1 reply; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-07 15:11 UTC (permalink / raw)
To: Sabau, Radu bogdan
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
> > + /* TX: address phase, CS stays asserted into data phase */
> > + st->scan_xfers[2 * k].tx_buf = offload->tx_cmd[k];
> > + st->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);
> > + st->scan_xfers[2 * k].bits_per_word = bpw;
>
> "When bits_per_word is greater than 8 (like bpw = 16 here), the SPI framework
> treats tx_buf as an array of native 16-bit words.
> On little-endian architectures, the controller will byte-swap the data before
> transmitting it. Will using a u8 array and put_unaligned_be16() result in the
> command bytes being reversed on the wire?"
>
> Switched to cpu_to_be16() assigned directly into __be16 scan_tx[],
> matching the non-offload path. This makes the intended wire format
> self-evident and sidesteps the byte-ordering question entirely.
This confuses me a bit because the SPI controller should work with
native endian and from that generate the expected big endian on the wire.
So on a little endian host byte order in address space is LH but it will
write top bit of H first thus the ADC channel address needs to be in the
second byte.
On a big endian host despite the ordering in memory being HL, the top
bit of H is still written first thus in needs to be in the first byte.
If you using cpu_to_be16() to assign a 16 bit value swapping only on little endian
and start with the cmd in L on little endian you'll end up with LH swapped to
HL and on big endian HL but the little endian SPI controller should then swap
it again sending what it thinks is the high byte first (L) whereas the big endian
system will send H.
Upshot. I think the field should be native endian. If a byte swap is needed
it should be unconditional and not rely on endianness of the host.
>
> > +
> > + /* RX: data phase, CS toggles after to delimit the next register
> > op */
> > + st->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);
> > + st->scan_xfers[2 * k + 1].bits_per_word = bpw;
> > + st->scan_xfers[2 * k + 1].offload_flags =
> > SPI_OFFLOAD_XFER_RX_STREAM;
> > + st->scan_xfers[2 * k + 1].cs_change = 1;
> > + k++;
> > + }
> > +
> > + /* State reset to re-arm DATA_READY for the next scan. */
> > + put_unaligned_be16(AD4691_STATE_RESET_REG, offload->tx_reset);
> > + offload->tx_reset[2] = AD4691_STATE_RESET_ALL;
> > +
> > + st->scan_xfers[2 * k].tx_buf = offload->tx_reset;
> > + st->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);
> > + st->scan_xfers[2 * k].bits_per_word = bpw;
> > +
> > + st->scan_xfers[2 * k + 1].tx_buf = &offload->tx_reset[2];
> > + st->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);
>
> "Will passing &offload->tx_reset[2] directly as tx_buf cause DMA mapping
> issues, since it is only 2-byte aligned?"
>
> Addressed by the second concern's fix: no sub-aligned pointer into the middle
> of a DMA buffer — the full 4-byte scan_tx_reset[] is passed as tx_buf
> from its own cache-line-aligned start address.
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 5/6] iio: adc: ad4691: add oversampling support
2026-05-07 11:56 ` Sabau, Radu bogdan
@ 2026-05-07 15:26 ` Jonathan Cameron
0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2026-05-07 15:26 UTC (permalink / raw)
To: Sabau, Radu bogdan
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Thu, 7 May 2026 11:56:53 +0000
"Sabau, Radu bogdan" <Radu.Sabau@analog.com> wrote:
> Addressing Sashiko's review for the oversampling support patch.
>
One thing inline. I think Sashiko got it wrong..
> > @@ -691,6 +857,10 @@ static int ad4691_enter_conversion_mode(struct
> > ad4691_state *st)
> > return regmap_update_bits(st->regmap,
> > AD4691_DEVICE_SETUP,
> > AD4691_MANUAL_MODE,
> > AD4691_MANUAL_MODE);
> >
> > + ret = ad4691_write_osc_freq(st);
> > + if (ret)
> > + return ret;
> > +
> > ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
> > AD4691_ADC_MODE_MASK,
> > AD4691_CNV_BURST_MODE);
> > if (ret)
> > @@ -844,6 +1014,12 @@ static int
> > ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
> > if (ret)
> > goto err_unoptimize;
> >
> > + iio_for_each_active_channel(indio_dev, i) {
> > + ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(i),
> > st->osr[i]);
> > + if (ret)
> > + goto err_unoptimize;
> > + }
> > +
>
> "If the IIO soft timestamp channel is enabled, its scan_index is 16.
> iio_for_each_active_channel() iterates over all active channels including
> the timestamp channel.
I saw this one and think it's wrong.
It is sort of true and sort of not. active_scan_mask never has the timestamp
channel set. There is special handling for that channel.
https://elixir.bootlin.com/linux/v7.0.1/source/drivers/iio/industrialio-buffer.c#L613
IIRC the bitmap is technically one bit too large given that quirk
(I've not checked that today so I'm relying on memory of when this last came up).
Jonathan
> Will this loop process i = 16 and access st->osr[16] out of bounds, since
> st->osr is an array of size 16?
> Additionally, since AD4691_ACC_DEPTH_IN(16) resolves to 0x196
> (AD4691_GPIO_MODE1_REG), will this silently corrupt the GPIO configuration
> and potentially break the DATA_READY interrupt?
> Furthermore, does the presence of the timestamp channel cause the loop
> iteration to advance k to 17, making the state-reset SPI transfers appended
> after the loop write to st->scan_xfers[34] and [35]? This would overflow
> the scan_xfers array which is sized 34 and could corrupt the surrounding
> struct ad4691_state."
>
> Valid for the osr[] out-of-bounds and GPIO corruption concerns. With
> the soft timestamp enabled (scan_index = 16), the loop would access
> st->osr[16] out-of-bounds and write to AD4691_ACC_DEPTH_IN(16) = 0x196,
> which is AD4691_GPIO_MODE1_REG, silently corrupting the GPIO configuration
> and potentially breaking the DATA_READY interrupt.
>
> Added the same guard used by the scan_xfers loops in the triggered-buffer
> commit: if (i >= indio_dev->num_channels - 1) break.
>
> The scan_xfers k-overflow concern is already handled by that existing guard
> in a separate loop — it is not affected by the ACC_DEPTH_IN loop added
> here.
>
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family
2026-05-07 14:15 ` Jonathan Cameron
@ 2026-05-08 4:44 ` Andy Shevchenko
2026-05-08 9:53 ` Andy Shevchenko
0 siblings, 1 reply; 25+ messages in thread
From: Andy Shevchenko @ 2026-05-08 4:44 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
David Lechner, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Thu, May 07, 2026 at 03:15:49PM +0100, Jonathan Cameron wrote:
> On Thu, 7 May 2026 09:26:00 +0000
> "Sabau, Radu bogdan" <Radu.Sabau@analog.com> wrote:
...
> > > + st->info = spi_get_device_match_data(spi);
> >
> > "Is it possible for st->info to be NULL here?
> > If the driver is manually bound to a device with a non-matching name using
> > the sysfs bind interface in combination with driver_override,
> > spi_get_device_match_data() could return NULL, which would cause a NULL
> > pointer dereference later in the probe sequence."
> >
> > Agreed, will add the NULL check with -ENODEV immediately after
> > spi_get_device_match_data().
>
> Andy, you seeing this one? Looks like we are putting these checks back in again.
> Whilst anyone forcing a bind like this is onto a looser anyway we shouldn't
> crash due to a null dereference.
We should find a way how to disable that combination from the start. The driver
makes no sense to be instantiated from user space. Actually most of the drivers
nowadays should not be bound to the devices without driver data.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family
2026-05-08 4:44 ` Andy Shevchenko
@ 2026-05-08 9:53 ` Andy Shevchenko
0 siblings, 0 replies; 25+ messages in thread
From: Andy Shevchenko @ 2026-05-08 9:53 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Sabau, Radu bogdan, Lars-Peter Clausen, Hennerich, Michael,
David Lechner, Sa, Nuno, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
On Fri, May 08, 2026 at 07:44:39AM +0300, Andy Shevchenko wrote:
> On Thu, May 07, 2026 at 03:15:49PM +0100, Jonathan Cameron wrote:
> > On Thu, 7 May 2026 09:26:00 +0000
> > "Sabau, Radu bogdan" <Radu.Sabau@analog.com> wrote:
...
> > > > + st->info = spi_get_device_match_data(spi);
> > >
> > > "Is it possible for st->info to be NULL here?
> > > If the driver is manually bound to a device with a non-matching name using
> > > the sysfs bind interface in combination with driver_override,
> > > spi_get_device_match_data() could return NULL, which would cause a NULL
> > > pointer dereference later in the probe sequence."
> > >
> > > Agreed, will add the NULL check with -ENODEV immediately after
> > > spi_get_device_match_data().
> >
> > Andy, you seeing this one? Looks like we are putting these checks back in again.
> > Whilst anyone forcing a bind like this is onto a looser anyway we shouldn't
> > crash due to a null dereference.
>
> We should find a way how to disable that combination from the start. The driver
> makes no sense to be instantiated from user space. Actually most of the drivers
> nowadays should not be bound to the devices without driver data.
The 20260508095224.1275645-1-andriy.shevchenko@linux.intel.com has been just sent.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support
2026-05-07 14:25 ` Jonathan Cameron
@ 2026-05-08 11:08 ` Sabau, Radu bogdan
0 siblings, 0 replies; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-08 11:08 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Thursday, May 7, 2026 5:26 PM
...
> > > +static int ad4691_manual_buffer_preenable(struct iio_dev *indio_dev)
> > > +{
> > > + struct ad4691_state *st = iio_priv(indio_dev);
> > > + unsigned int prev_i, k, i;
> > > + bool first;
> > > + int ret;
> > > +
> > > + memset(st->scan_xfers, 0, sizeof(st->scan_xfers));
> > > + memset(st->scan_tx, 0, sizeof(st->scan_tx));
> > > +
> > > + spi_message_init(&st->scan_msg);
> > > +
> > > + first = true;
> > > + prev_i = 0;
> > > + k = 0;
> > > + iio_for_each_active_channel(indio_dev, i) {
> > > + st->scan_tx[k] = cpu_to_be16(AD4691_ADC_CHAN(i));
> > > + st->scan_xfers[k].tx_buf = &st->scan_tx[k];
> > > + /*
> > > + * The pipeline means xfer[0] receives the residual from the
> > > + * previous sequence, not a valid sample for channel i. Point
> > > + * it at vals[i] anyway; xfer[1] (or the NOOP when only one
> > > + * channel is active) will overwrite that slot with the real
> > > + * result, so no separate dummy buffer is needed.
> > > + */
> > > + if (first) {
> > > + st->scan_xfers[k].rx_buf = &st->vals[i];
> > > + first = false;
> > > + } else {
> > > + st->scan_xfers[k].rx_buf = &st->vals[prev_i];
> > > + }
> >
> >
> > "The IIO subsystem expects data pushed to the buffer to be densely packed
> > according to the active channels in the scan mask.
> > If only a subset of channels are enabled, does assigning the rx_buf pointer
> > directly to absolute array indices at &st->vals[i] leave holes in the buffer?
> > When iio_push_to_buffers_with_ts() is called, this might cause it to read
> > uninitialized memory instead of the expected samples."
> >
> > I would say there is no change needed. Writing to &st->vals[scan_index] and
> > passing the full array to iio_push_to_buffers_with_ts() is the standard IIO
> kfifo
> > pattern: the core demultiplexes by reading data[scan_index * storagebits/8]
> > for each active channel; holes at inactive indices are silently ignored.
> > The same pattern is used in ad4695, ad_sigma_delta, and others. The
> > pipeline residual in the first manual-mode transfer is overwritten by the
> > subsequent transfer before the scan is pushed, as the comment explains.
>
> This looks wrong to me.
>
> What holes? If available_scan_masks is set we will do a bunch of
> demux work - but then this code would see the mask picked from that
> list. If it's not then typically we won't (subject to multiple consumers
> forcing it - but that still won't close up holes here).
>
> If the active_scan_mask == the one requested, there is no demux at all
> and I think that's the case here - the code pushes the data passed in
> directly to the kfifo.
>
> Perhaps given an illustration of what the layout of resulting data
> is if only even numbered channels are enabled.
>
Correct. scan_bytes is a dense count (channels 0, 2, 4 active → 3 × 2 = 6
bytes). With the old sparse layout (rx_buf = &vals[scan_index]):
vals[0] = ch0 result (correct)
vals[1] = hole (incorrect) <- userspace reads as ch2
vals[2] = ch2 result (correct) <-userspace reads as ch4
Fixed by using the slot counter k as the rx_buf index rather than the channel
index i, giving a densely packed vals[]. In manual mode xfer[k] delivers the
previous channel's result (pipelined), so subsequent transfers write into
vals[k-1]; vals[0] serves as a throwaway slot for the first transfer's garbage.
^ permalink raw reply [flat|nested] 25+ messages in thread
* RE: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
2026-05-07 15:11 ` Jonathan Cameron
@ 2026-05-08 11:11 ` Sabau, Radu bogdan
0 siblings, 0 replies; 25+ messages in thread
From: Sabau, Radu bogdan @ 2026-05-08 11:11 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Uwe Kleine-König, Liam Girdwood, Mark Brown, Linus Walleij,
Bartosz Golaszewski, Philipp Zabel, Jonathan Corbet, Shuah Khan,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Thursday, May 7, 2026 6:11 PM
> To: Sabau, Radu bogdan <Radu.Sabau@analog.com>
> Cc: Lars-Peter Clausen <lars@metafoo.de>; Hennerich, Michael
> <Michael.Hennerich@analog.com>; David Lechner <dlechner@baylibre.com>;
> Sa, Nuno <Nuno.Sa@analog.com>; Andy Shevchenko <andy@kernel.org>;
> Rob Herring <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>;
> Conor Dooley <conor+dt@kernel.org>; Uwe Kleine-König
> <ukleinek@kernel.org>; Liam Girdwood <lgirdwood@gmail.com>; Mark Brown
> <broonie@kernel.org>; Linus Walleij <linusw@kernel.org>; Bartosz
> Golaszewski <brgl@kernel.org>; Philipp Zabel <p.zabel@pengutronix.de>;
> Jonathan Corbet <corbet@lwn.net>; Shuah Khan
> <skhan@linuxfoundation.org>; linux-iio@vger.kernel.org;
> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; linux-
> pwm@vger.kernel.org; linux-gpio@vger.kernel.org; linux-doc@vger.kernel.org
> Subject: Re: [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support
>
>
> > > + /* TX: address phase, CS stays asserted into data phase */
> > > + st->scan_xfers[2 * k].tx_buf = offload->tx_cmd[k];
> > > + st->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);
> > > + st->scan_xfers[2 * k].bits_per_word = bpw;
> >
> > "When bits_per_word is greater than 8 (like bpw = 16 here), the SPI
> framework
> > treats tx_buf as an array of native 16-bit words.
> > On little-endian architectures, the controller will byte-swap the data before
> > transmitting it. Will using a u8 array and put_unaligned_be16() result in the
> > command bytes being reversed on the wire?"
> >
> > Switched to cpu_to_be16() assigned directly into __be16 scan_tx[],
> > matching the non-offload path. This makes the intended wire format
> > self-evident and sidesteps the byte-ordering question entirely.
>
> This confuses me a bit because the SPI controller should work with
> native endian and from that generate the expected big endian on the wire.
>
> So on a little endian host byte order in address space is LH but it will
> write top bit of H first thus the ADC channel address needs to be in the
> second byte.
> On a big endian host despite the ordering in memory being HL, the top
> bit of H is still written first thus in needs to be in the first byte.
>
>
> If you using cpu_to_be16() to assign a 16 bit value swapping only on little
> endian
> and start with the cmd in L on little endian you'll end up with LH swapped to
> HL and on big endian HL but the little endian SPI controller should then swap
> it again sending what it thinks is the high byte first (L) whereas the big endian
> system will send H.
>
> Upshot. I think the field should be native endian. If a byte swap is needed
> it should be unconditional and not rely on endianness of the host.
>
Correct. With bits_per_word=16 the SPI controller reads tx_buf as a native
16-bit word. cpu_to_be16() stores BE bytes, but on an LE host those bytes are
read back as a different native integer, sending the wrong byte first.
scan_tx is declared as u16. Both offload paths store the exact wire value as a
plain native u16 — no endianness macro. Native storage is self-consistent:
store X, read back X, SPI shifts X out MSB-first → correct wire bytes on any
host. Trace for AVG_IN(0) = 0x8201, expected wire [0x82, 0x01]:
cpu_to_be16(0x8201), LE host → native 0x0182 → wire [0x01, 0x82] (incorrect)
(u16)0x8201, LE host → native 0x8201 → wire [0x82, 0x01] (correct)
(u16)0x8201, BE host → native 0x8201 → wire [0x82, 0x01] (correct)
Manual offload: TX and RX are full-duplex (same clock cycles, shared xfer):
st->scan_tx[k] = (u16)(AD4691_ADC_CHAN(bit) << 8);
/* e.g. ch=0: 0x8000 → wire [0x80, 0x00] */
CNV burst offload: TX and RX are separate xfers, both use bits_per_word=bpw:
st->scan_tx[k] = 0x8000 | AD4691_AVG_IN(bit);
/* e.g. AVG_IN(0): 0x8201 → wire [0x82, 0x01] */
The state-reset transfer uses a u8 buffer with bits_per_word=8 (default);
put_unaligned_be16() writes bytes in memory order, which SPI sends as-is.
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2026-05-08 11:12 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260430-ad4692-multichannel-sar-adc-driver-v9-0-33e439e4fb87@analog.com>
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-4-33e439e4fb87@analog.com>
2026-05-05 14:12 ` [PATCH v9 4/6] iio: adc: ad4691: add SPI offload support Jonathan Cameron
2026-05-05 14:28 ` Jonathan Cameron
2026-05-06 9:17 ` Sabau, Radu bogdan
2026-05-07 11:49 ` Sabau, Radu bogdan
2026-05-07 15:11 ` Jonathan Cameron
2026-05-08 11:11 ` Sabau, Radu bogdan
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-6-33e439e4fb87@analog.com>
2026-05-05 14:35 ` [PATCH v9 6/6] docs: iio: adc: ad4691: add driver documentation Jonathan Cameron
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-2-33e439e4fb87@analog.com>
2026-05-05 13:23 ` [PATCH v9 2/6] iio: adc: ad4691: add initial driver for AD4691 family Jonathan Cameron
2026-05-07 9:26 ` Sabau, Radu bogdan
2026-05-07 14:15 ` Jonathan Cameron
2026-05-08 4:44 ` Andy Shevchenko
2026-05-08 9:53 ` Andy Shevchenko
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-3-33e439e4fb87@analog.com>
[not found] ` <afhReLCsEdaEOT_H@ashevche-desk.local>
[not found] ` <LV9PR03MB841441B282275F8F36FD12C1F7312@LV9PR03MB8414.namprd03.prod.outlook.com>
2026-05-05 13:26 ` [PATCH v9 3/6] iio: adc: ad4691: add triggered buffer support Jonathan Cameron
2026-05-05 14:58 ` Andy Shevchenko
2026-05-05 16:17 ` Jonathan Cameron
2026-05-06 7:25 ` Andy Shevchenko
2026-05-06 9:01 ` Sabau, Radu bogdan
2026-05-05 14:04 ` Jonathan Cameron
2026-05-05 14:07 ` Jonathan Cameron
2026-05-07 11:37 ` Sabau, Radu bogdan
2026-05-07 14:25 ` Jonathan Cameron
2026-05-08 11:08 ` Sabau, Radu bogdan
[not found] ` <20260430-ad4692-multichannel-sar-adc-driver-v9-5-33e439e4fb87@analog.com>
2026-05-05 14:32 ` [PATCH v9 5/6] iio: adc: ad4691: add oversampling support Jonathan Cameron
2026-05-07 11:56 ` Sabau, Radu bogdan
2026-05-07 15:26 ` Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox