Skip to contentSkip to conversation

iio: adc: ad4000: Add SPI offload support

PATCH Superseded Marcelo Schmitt 3 participants 12 messages Mar 26, 2025 – Mar 27, 2025

Subsystems (4)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore

Marcelo Schmitt

author maintainer
#0 lore raw

v3 cover iio: adc: ad4000: Add SPI offload support

This patch series extends the ad4000 driver to support SPI offloading. In addition to that, ad4000 IIO documentation is expanded to:
- list PulSAR parts supported by the ad4000 driver.
- describe some characteristics of AD4000 IIO device.
- describe changes when SPI offload is being used.

Change log v2 -> v3
- Dropped patch 1 of v2 series and reverted buffer endianness and SPI transfer bits_per_word configurations to the way they were in v1.
- Removed dummy transfer in ad4000_prepare_offload_turbo_message().
- Unified offload message preparation.
- Removed spi->cs_hold.value to use cs_inactive to set CNV/CS high time.
- Adjusted max sample rate for AD7983 and AD7984.
- Dropped AD4000_TCONV_NS and made all time related constants a time_spec field.
- Removed _offl param from _CHANNELS() macros and added a comment about that.
- Commented IIO_BE check in ad4000_prepare_3wire_mode_message().
- Now using predisable to balance with postenable.
- Put spi/offload/consumer.h include right after spi/spi.h.
- Removed unrelated brackets in info_mask_separate_available assignment
- Added blank line after if.

Change log v1 -> v2
- Fixed passing inappropriate pointer instead of ret to dev_err_probe()
- [new patch] Set transfer bits_per_word to have data in CPU endianness
- Set iio_dev num_channels close to where channels is set.
- Complement offload message comment about first sample being invalid
- Document why the first buffer sample is invalid when offloading
- Added blank line before a 'simple return'.

Link to v2: https://lore.kernel.org/linux-iio/[email protected]/
Link to v1: https://lore.kernel.org/linux-iio/[email protected]/

v3 was tested and worked with the following setups
- ADAQ4003 on CoraZ7 with SPI offload support;
- AD7687 on CoraZ7 with SPI offload support;
- AD7687 on rpi (no SPI offload support).
The Linux image ran on cora was built from IIO testing branch. The Linux image ran on rpi was built from rpi tree rpi-6.14.y branch with some adaptations to cope with old iio_device_claim_direct_mode() and addition of the following patches
iio: adc: ad4000: Stop using iio_device_claim_direct_scoped()
spi: add basic support for SPI offloading
spi: offload: add support for hardware triggers
dt-bindings: trigger-source: add generic PWM trigger source
spi: offload-trigger: add PWM trigger driver
spi: add offload TX/RX streaming APIs
iio: buffer-dmaengine: split requesting DMA channel from allocating buffer
iio: buffer-dmaengine: add devm_iio_dmaengine_buffer_setup_with_handle()

Marcelo Schmitt (4):
iio: adc: ad4000: Add support for SPI offload
Documentation: iio: ad4000: Add new supported parts
Documentation: iio: ad4000: Add IIO Device characteristics section
Documentation: iio: ad4000: Describe offload support

 Documentation/iio/ad4000.rst |  86 +++++++-
 drivers/iio/adc/Kconfig      |   7 +-
 drivers/iio/adc/ad4000.c     | 405 ++++++++++++++++++++++++++++++++---
 3 files changed, 463 insertions(+), 35 deletions(-)

base-commit: af94f401e26f686f7391ce79b38a6129417c22dc
prerequisite-patch-id: 3d517eef53a799adba5922815fe684b913e36773

Signature · 1 line
2.47.2
author maintainer
#1 lore raw

PATCH v3 1/4 iio: adc: ad4000: Add support for SPI offload

FPGA HDL projects can include a PWM generator in addition to SPI-Engine. The PWM IP is used to trigger SPI-Engine offload modules that in turn set SPI-Engine to execute transfers to poll data from the ADC. That allows data to be read at the maximum sample rates. Also, it is possible to set a specific sample rate by setting the proper PWM duty cycle and related state parameters, thus allowing an adjustable ADC sample rate when a PWM (offload trigger) is used in combination with SPI-Engine.

Add support for SPI offload.

---
Ideally, we should do something to provide tquiet1 (if using offload) or tconv (is not using turbo mode) delay but, when testing with AD7687 and ADAQ4003 on cora with offload support, I saw no issue in running the transfers without the CS/CNV high delay. Without the preparatory/dummy transfer, offload messages became equal so I dropped one of them. Also, despite SPI-Engine not supporting it, I set cs_inactive which semantic seems to match the CS high delay we want for these transfers.

 drivers/iio/adc/Kconfig  |   7 +-
 drivers/iio/adc/ad4000.c | 405 +++++++++++++++++++++++++++++++++++----
 2 files changed, 378 insertions(+), 34 deletions(-)
drivers/iio/adc/Kconfig +6 -1
index b7ae6e0ae0df..1cfa3a32f3a7 100644
@@ -25,10 +25,15 @@ config AD4000
 	tristate "Analog Devices AD4000 ADC Driver"
 	depends on SPI
 	select IIO_BUFFER
+	select IIO_BUFFER_DMAENGINE
 	select IIO_TRIGGERED_BUFFER
+	select SPI_OFFLOAD
 	help
 	  Say yes here to build support for Analog Devices AD4000 high speed
-	  SPI analog to digital converters (ADC).
+	  SPI analog to digital converters (ADC). If intended to use with
+	  SPI offloading support, it is recommended to enable
+	  CONFIG_SPI_AXI_SPI_ENGINE, CONFIG_PWM_AXI_PWMGEN, and
+	  CONFIG_SPI_OFFLOAD_TRIGGER_PWM.
 
 	  To compile this driver as a module, choose M here: the module will be
 	  called ad4000.
drivers/iio/adc/ad4000.c +372 -33

858 lines not on this page · raw message

Signature · 1 line
2.47.2
reviewer
#2 lore raw

Re: [PATCH v3 1/4] iio: adc: ad4000: Add support for SPI offload

Quoted 12 lines · Marcelo Schmitt

FPGA HDL projects can include a PWM generator in addition to SPI-Engine. The PWM IP is used to trigger SPI-Engine offload modules that in turn set SPI-Engine to execute transfers to poll data from the ADC. That allows data to be read at the maximum sample rates. Also, it is possible to set a specific sample rate by setting the proper PWM duty cycle and related state parameters, thus allowing an adjustable ADC sample rate when a PWM (offload trigger) is used in combination with SPI-Engine.

Add support for SPI offload.

---

...

drivers/iio/adc/ad4000.c line 19

Quoted 9 lines
drivers/iio/adc/ad4000.c +1 -0
index 4fe8dee48da9..9fc56853265e 100644
@@ -16,11 +16,13 @@
 #include <linux/gpio/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
+#include <linux/spi/offload/consumer.h>

Alphabetical order?

Quoted 9 lines
drivers/iio/adc/ad4000.c +1 -0
 #include <linux/units.h>
 #include <linux/util_macros.h>
 #include <linux/iio/iio.h>
 
 #include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
 #include <linux/iio/triggered_buffer.h>
 #include <linux/iio/trigger_consumer.h>
 

...

Quoted 3 lines

+/*
+ * When SPI offload is configured, transfers are executed withouth CPU

s/withouth/without/

Quoted 4 lines

+ * intervention so no soft timestamp can be recorded when transfers run.
+ * Because of that, the macros that set timestamp channel are only used when
+ * transfers are not offloaded.
+ */

...

drivers/iio/adc/ad4000.c static int ad4000_probe(struct spi_device *spi) · line 1087

Quoted 9 lines
drivers/iio/adc/ad4000.c +4 -1
@@ -784,7 +1081,10 @@ static int ad4000_probe(struct spi_device *spi)
 	switch (st->sdi_pin) {
 	case AD4000_SDI_MOSI:
 		indio_dev->info = &ad4000_reg_access_info;
-		indio_dev->channels = chip->reg_access_chan_spec;
+
+		/* Set CNV/CS high time for when turbo mode is used */
+		spi->cs_inactive.value = st->time_spec->t_quiet1_ns;
+		spi->cs_inactive.unit = SPI_DELAY_UNIT_NSECS;

This code path later calls ad4000_prepare_3wire_mode_message() which sets:

	xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns;

Which contradicts/overrides this.

Quoted 36 lines
drivers/iio/adc/ad4000.c +18 -2
 		/*
 		 * In "3-wire mode", the ADC SDI line must be kept high when
@@ -796,9 +1096,26 @@ static int ad4000_probe(struct spi_device *spi)
 		if (ret < 0)
 			return ret;
 
+		if (st->using_offload) {
+			indio_dev->channels = &chip->reg_access_offload_chan_spec;
+			indio_dev->num_channels = 1;
+			ret = ad4000_prepare_offload_message(st, indio_dev->channels);
+			if (ret)
+				return dev_err_probe(dev, ret,
+						     "Failed to optimize SPI msg\n");
+		} else {
+			indio_dev->channels = chip->reg_access_chan_spec;
+			indio_dev->num_channels = ARRAY_SIZE(chip->reg_access_chan_spec);
+		}
+
+		/*
+		 * Call ad4000_prepare_3wire_mode_message() so single-shot read
+		 * SPI messages are always initialized.
+		 */
 		ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
 		if (ret)
-			return ret;
+			return dev_err_probe(dev, ret,
+					     "Failed to optimize SPI msg\n");
 
 		ret = ad4000_config(st);
 		if (ret < 0)
@@ -806,19 +1123,47 @@ static int ad4000_probe(struct spi_device *spi)
 
 		break;
 	case AD4000_SDI_VIO:
-		indio_dev->info = &ad4000_info;

drivers/iio/adc/ad4000.c static int ad4000_probe(struct spi_device *spi) · line 1134

Quoted 10 lines
drivers/iio/adc/ad4000.c +9 -1
-		indio_dev->channels = chip->chan_spec;
+		if (st->using_offload) {
+			indio_dev->info = &ad4000_offload_info;
+			indio_dev->channels = &chip->offload_chan_spec;
+			indio_dev->num_channels = 1;
+
+			/* Set CNV/CS high time for when turbo mode is not used */
+			if (!st->cnv_gpio) {
+				spi->cs_inactive.value = st->time_spec->t_conv_ns;
+				spi->cs_inactive.unit = SPI_DELAY_UNIT_NSECS;

I'm still not sold on this. We know it has no effect with AXI SPI Engine and it is writing over something that usually comes from DT. It is misleading.

And the non-offload case already does:

	xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns;

which actually does work with the AXI SPI Engine. So why not be consistent and do it the same way for the offload case?

It also seems safe to omit this altogether in the offload case and assume that the max sample rate will also ensure that the miniumum time for CS deasserted is respected.

Quoted 22 lines
drivers/iio/adc/ad4000.c +17 -1
+				ret = spi_setup(spi);
+				if (ret < 0)
+					return ret;
+			}
+
+			ret = ad4000_prepare_offload_message(st, indio_dev->channels);
+			if (ret)
+				return dev_err_probe(dev, ret,
+						     "Failed to optimize SPI msg\n");
+		} else {
+			indio_dev->info = &ad4000_info;
+			indio_dev->channels = chip->chan_spec;
+			indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec);
+		}
+
 		ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
 		if (ret)
-			return ret;
+			return dev_err_probe(dev, ret,
+					     "Failed to optimize SPI msg\n");
 
 		break;
#3 lore raw

Re: [PATCH v3 1/4] iio: adc: ad4000: Add support for SPI offload

Hi David, thank you for your review.
Hope I don't sound harsh on my reply.
Comments inline.

Thanks

Quoted 14 lines · David Lechner
Quoted 12 lines · Marcelo Schmitt

FPGA HDL projects can include a PWM generator in addition to SPI-Engine. The PWM IP is used to trigger SPI-Engine offload modules that in turn set SPI-Engine to execute transfers to poll data from the ADC. That allows data to be read at the maximum sample rates. Also, it is possible to set a specific sample rate by setting the proper PWM duty cycle and related state parameters, thus allowing an adjustable ADC sample rate when a PWM (offload trigger) is used in combination with SPI-Engine.

Add support for SPI offload.

---

...

Quoted 5 lines
Quoted 3 lines

#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
+#include <linux/spi/offload/consumer.h>

Alphabetical order?

Ah drat, yes. Hope I've put that in the correct order now.

Quoted 10 lines
Quoted 10 lines

#include <linux/units.h>
#include <linux/util_macros.h>
#include <linux/iio/iio.h>

#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>

Also changing to
...
#include <linux/units.h>
#include <linux/util_macros.h>

#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/buffer-dmaengine.h>
...

Quoted 8 lines

...

Quoted 3 lines

+/*
+ * When SPI offload is configured, transfers are executed withouth CPU

s/withouth/without/

Ack

Quoted 4 lines
Quoted 4 lines

+ * intervention so no soft timestamp can be recorded when transfers run.
+ * Because of that, the macros that set timestamp channel are only used when
+ * transfers are not offloaded.
+ */

...

Quoted 12 lines

drivers/iio/adc/ad4000.c static int ad4000_probe(struct spi_device *spi) · line 1087

Quoted 9 lines
drivers/iio/adc/ad4000.c +4 -1
@@ -784,7 +1081,10 @@ static int ad4000_probe(struct spi_device *spi)
 	switch (st->sdi_pin) {
 	case AD4000_SDI_MOSI:
 		indio_dev->info = &ad4000_reg_access_info;
-		indio_dev->channels = chip->reg_access_chan_spec;
+
+		/* Set CNV/CS high time for when turbo mode is used */
+		spi->cs_inactive.value = st->time_spec->t_quiet1_ns;
+		spi->cs_inactive.unit = SPI_DELAY_UNIT_NSECS;

This code path later calls ad4000_prepare_3wire_mode_message() which sets:

Quoted 3 lines
	xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns;

Which contradicts/overrides this.

Oof, good point. Though, it's probably not a problem if single-shot transfers use a longer delay, right? Would that also override cs_inactive or be of any trouble for SPI controllers?

...

Quoted 27 lines
Quoted 27 lines
drivers/iio/adc/ad4000.c +18 -1
+		if (st->using_offload) {
+			indio_dev->channels = &chip->reg_access_offload_chan_spec;
+			indio_dev->num_channels = 1;
+			ret = ad4000_prepare_offload_message(st, indio_dev->channels);
+			if (ret)
+				return dev_err_probe(dev, ret,
+						     "Failed to optimize SPI msg\n");
+		} else {
+			indio_dev->channels = chip->reg_access_chan_spec;
+			indio_dev->num_channels = ARRAY_SIZE(chip->reg_access_chan_spec);
+		}
+
+		/*
+		 * Call ad4000_prepare_3wire_mode_message() so single-shot read
+		 * SPI messages are always initialized.
+		 */
 		ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
 		if (ret)
-			return ret;
+			return dev_err_probe(dev, ret,
+					     "Failed to optimize SPI msg\n");
 
 		ret = ad4000_config(st);
 		if (ret < 0)
@@ -806,19 +1123,47 @@ static int ad4000_probe(struct spi_device *spi)
 
Quoted 16 lines

drivers/iio/adc/ad4000.c static int ad4000_probe(struct spi_device *spi) · line 1134

Quoted 13 lines
drivers/iio/adc/ad4000.c +9 -2
 		break;
 	case AD4000_SDI_VIO:
-		indio_dev->info = &ad4000_info;
-		indio_dev->channels = chip->chan_spec;
+		if (st->using_offload) {
+			indio_dev->info = &ad4000_offload_info;
+			indio_dev->channels = &chip->offload_chan_spec;
+			indio_dev->num_channels = 1;
+
+			/* Set CNV/CS high time for when turbo mode is not used */
+			if (!st->cnv_gpio) {
+				spi->cs_inactive.value = st->time_spec->t_conv_ns;
+				spi->cs_inactive.unit = SPI_DELAY_UNIT_NSECS;

I'm still not sold on this. We know it has no effect with AXI SPI Engine and it is writing over something that usually comes from DT. It is misleading.

I thought it was okay to set cs_inactive and call spi_setup() from the field doc in include/linux/spi/spi.h.

set_cs_timing() method is for SPI controllers that supports configuring CS timing.

This hook allows SPI client drivers to request SPI controllers to configure specific CS timing through spi_set_cs_timing() after spi_setup().

Would it be better to set spi-cs-inactive-delay-ns in ADC dt node? Or it still doesn't look like a proper use of cs_inactive?

Quoted 7 lines

And the non-offload case already does:

	xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns;

which actually does work with the AXI SPI Engine. So why not be consistent and do it the same way for the offload case?

One of the points in using bits_per_word in spi transfers was to reach high frequency sample rate, right? I think it makes sense to use them for SPI offload transfers. But we were also trying to set a proper CNV/CS dealy so that ADC conversion could complete properly before starting requesting the data. That also sound reasonable to me. But, spi_transfer struct doesn't provide a good way of setting a CS inactive delay if only one transfer is executed. If we use cs_change_delay, we would then be running two transfers, no? Plus, the ADC would be doing two conversions (one after CS deasert of previous message and one after CS deassert at the end of the first transfer) while we only read one of them.

The offload message preparation would look like what we had in v2:

static int ad4000_prepare_offload_turbo_message(struct ad4000_state *st,
const struct iio_chan_spec *chan)

{
	struct spi_transfer *xfers = st->offload_xfers;

	/* Dummy transfer to guarantee enough CS high time. */
	xfers[0].cs_change = 1;
	xfers[0].cs_change_delay.value = st->time_spec->t_quiet1_ns;
	xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;

	xfers[1].bits_per_word = chan->scan_type.realbits;
	xfers[1].len = chan->scan_type.realbits > 16 ? 4 : 2;
	xfers[1].delay.value = st->time_spec->t_quiet2_ns;
	xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
	xfers[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;

	spi_message_init_with_transfers(&st->offload_msg, xfers, 2);
	st->offload_msg.offload = st->offload;

	return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->offload_msg);
}

Are we worried about a few clock cycles in between transfers but not worried about running an entire dummy transfer?

Plus, I've tested the single-transfer offload message version with ADAQ4003 on CoraZ7 and verified the results were correct.
FWIW, I put a copy of the dts I used for the tests at the end of this email.

Quoted 4 lines

It also seems safe to omit this altogether in the offload case and assume that the max sample rate will also ensure that the miniumum time for CS deasserted is respected.

If we can assume that, then I think that's another reason why we don't need a dummy transfer to set CS high delay.

Quoted 22 lines
Quoted 22 lines
drivers/iio/adc/ad4000.c +17 -1
+				ret = spi_setup(spi);
+				if (ret < 0)
+					return ret;
+			}
+
+			ret = ad4000_prepare_offload_message(st, indio_dev->channels);
+			if (ret)
+				return dev_err_probe(dev, ret,
+						     "Failed to optimize SPI msg\n");
+		} else {
+			indio_dev->info = &ad4000_info;
+			indio_dev->channels = chip->chan_spec;
+			indio_dev->num_channels = ARRAY_SIZE(chip->chan_spec);
+		}
+
 		ret = ad4000_prepare_3wire_mode_message(st, &indio_dev->channels[0]);
 		if (ret)
-			return ret;
+			return dev_err_probe(dev, ret,
+					     "Failed to optimize SPI msg\n");
 
Quoted 1 line

Review static int ad4000_probe(struct spi_device *spi) · line 1155

Quoted 1 line
(quoted diff) +0 -0
 		break;

---------- zynq-coraz7s-adaq4003.dts { ------------------

// SPDX-License-Identifier: GPL-2.0
/*
* Analog Devices ADAQ4003
*
* hdl_project: <pulsar_adc/coraz7s>
* Link: https://github.com/analogdevicesinc/hdl/tree/main/projects/pulsar_adc
* board_revision: <A>
*
* Copyright (C) 2023 Analog Devices Inc.
*/
/dts-v1/;
#include "zynq-coraz7s.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>

/ {
	adc_vref: regulator-vref {
		compatible = "regulator-fixed";
		regulator-name = "EVAL 5V Vref";
		regulator-min-microvolt = <5000000>;
		regulator-max-microvolt = <5000000>;
		regulator-always-on;
	};

	adc_vdd: regulator-vdd {
		compatible = "regulator-fixed";
		regulator-name = "Eval VDD supply";
		regulator-min-microvolt = <5000000>;
		regulator-max-microvolt = <5000000>;
		regulator-always-on;
	};

	adc_vio: regulator-vio {
		compatible = "regulator-fixed";
		regulator-name = "Eval VIO supply";
		regulator-min-microvolt = <3300000>;
		regulator-max-microvolt = <3300000>;
		regulator-always-on;
	};

	trigger_pwm: adc-pwm-trigger {
		compatible = "pwm-trigger";
		#trigger-source-cells = <0>;
		pwms = <&adc_trigger 0 1000000 0>;
	};
};

&fpga_axi {

	adc_trigger: pwm@0x44b00000 {
		compatible = "adi,axi-pwmgen-2.00.a";
		reg = <0x44b00000 0x1000>;
		label = "adc_conversion_trigger";
		#pwm-cells = <2>;
		clocks = <&spi_clk>;
	};

	spi_engine: spi@44a00000 {
		compatible = "adi,axi-spi-engine-1.00.a";
		reg = <0x44a00000 0x10000>;
		interrupt-parent = <&intc>;
		interrupts = <0 56 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clkc 15 &spi_clk>;
		clock-names = "s_axi_aclk", "spi_clk";

		dmas = <&rx_dma 0>;
		dma-names = "offload0-rx";
		trigger-sources = <&trigger_pwm>;

		#address-cells = <0x1>;
		#size-cells = <0x0>;

		adc@0 {
			compatible = "adi,adaq4003";
			reg = <0>;
			spi-max-frequency = <100000000>;
			vdd-supply = <&adc_vdd>;
			vio-supply = <&adc_vio>;
			ref-supply = <&adc_vref>;
			adi,high-z-input;
			adi,gain-milli = /bits/ 16 <454>;
		};
	};

	rx_dma: rx-dmac@44a30000 {
		compatible = "adi,axi-dmac-1.00.a";
		reg = <0x44a30000 0x1000>;
		#dma-cells = <1>;
		interrupt-parent = <&intc>;
		interrupts = <0 57 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clkc 16>;
	};

	spi_clk: axi-clkgen@0x44a70000 {
		compatible = "adi,axi-clkgen-2.00.a";
		reg = <0x44a70000 0x10000>;
		#clock-cells = <0>;
		clocks = <&clkc 15>, <&clkc 15>;
		clock-names = "s_axi_aclk", "clkin1";
		clock-output-names = "spi_clk";

		assigned-clocks = <&spi_clk>;
		assigned-clock-rates = <200000000>;
	};
};

---------- } zynq-coraz7s-adaq4003.dts ------------------

reviewer
#4 lore raw

Re: [PATCH v3 1/4] iio: adc: ad4000: Add support for SPI offload

On 3/27/25 12:56 PM, Marcelo Schmitt wrote:

...

Quoted 37 lines
Quoted 7 lines
Quoted 7 lines
drivers/iio/adc/ad4000.c +4 -2
 	case AD4000_SDI_VIO:
-		indio_dev->info = &ad4000_info;
-		indio_dev->channels = chip->chan_spec;
+		if (st->using_offload) {
+			indio_dev->info = &ad4000_offload_info;
+			indio_dev->channels = &chip->offload_chan_spec;
+			indio_dev->num_channels = 1;
Quoted 8 lines

drivers/iio/adc/ad4000.c static int ad4000_probe(struct spi_device *spi) · line 1134

Quoted 5 lines
drivers/iio/adc/ad4000.c +5 -0
+
+			/* Set CNV/CS high time for when turbo mode is not used */
+			if (!st->cnv_gpio) {
+				spi->cs_inactive.value = st->time_spec->t_conv_ns;
+				spi->cs_inactive.unit = SPI_DELAY_UNIT_NSECS;

I'm still not sold on this. We know it has no effect with AXI SPI Engine and it is writing over something that usually comes from DT. It is misleading.

I thought it was okay to set cs_inactive and call spi_setup() from the field doc in include/linux/spi/spi.h.

set_cs_timing() method is for SPI controllers that supports configuring CS timing.

This hook allows SPI client drivers to request SPI controllers to configure specific CS timing through spi_set_cs_timing() after spi_setup().

Would it be better to set spi-cs-inactive-delay-ns in ADC dt node? Or it still doesn't look like a proper use of cs_inactive?

Quoted 7 lines

And the non-offload case already does:

	xfers[0].cs_change_delay.value = st->time_spec->t_conv_ns;

which actually does work with the AXI SPI Engine. So why not be consistent and do it the same way for the offload case?

Quoted 7 lines

One of the points in using bits_per_word in spi transfers was to reach high frequency sample rate, right? I think it makes sense to use them for SPI offload transfers. But we were also trying to set a proper CNV/CS dealy so that ADC conversion could complete properly before starting requesting the data. That also sound reasonable to me. But, spi_transfer struct doesn't provide a good way of setting a CS inactive delay if only one transfer is executed. If we use cs_change_delay, we would then be running two transfers, no?

We would still only be doing one xfer per SPI offload trigger. The only difference it would make is that it would ensure that the second trigger could not come too soon after the CS deassert of the previous message.

In other words, the sampling frequency is already supplying this delay between subsequent SPI messages. Setting xfers[0].cs_change_delay just adds insurance that if the next trigger comes too soon, it will be ignored. This could happen, e.g. if someone sets the max SCLK rate to something low enough that the single xfer takes longer than the trigger period at the max sampling frequency.

In most cases though, we won't actually see cs_change_delay having any effect because the trigger to start the next SPI message doesn't come until later.

Quoted 37 lines

Plus, the ADC
would be doing two conversions (one after CS deasert of previous message and one after CS deassert at the end of the first transfer) while we only read one of them.

The offload message preparation would look like what we had in v2:

static int ad4000_prepare_offload_turbo_message(struct ad4000_state *st,
const struct iio_chan_spec *chan)

{
	struct spi_transfer *xfers = st->offload_xfers;

	/* Dummy transfer to guarantee enough CS high time. */
	xfers[0].cs_change = 1;
	xfers[0].cs_change_delay.value = st->time_spec->t_quiet1_ns;
	xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;

	xfers[1].bits_per_word = chan->scan_type.realbits;
	xfers[1].len = chan->scan_type.realbits > 16 ? 4 : 2;
	xfers[1].delay.value = st->time_spec->t_quiet2_ns;
	xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
	xfers[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;

	spi_message_init_with_transfers(&st->offload_msg, xfers, 2);
	st->offload_msg.offload = st->offload;

	return devm_spi_optimize_message(&st->spi->dev, st->spi, &st->offload_msg);
}

Are we worried about a few clock cycles in between transfers but not worried about running an entire dummy transfer?

Plus, I've tested the single-transfer offload message version with ADAQ4003 on CoraZ7 and verified the results were correct.
FWIW, I put a copy of the dts I used for the tests at the end of this email.

Quoted 1 line
Quoted 7 lines
Quoted 3 lines

It also seems safe to omit this altogether in the offload case and assume that the max sample rate will also ensure that the miniumum time for CS deasserted is respected.

If we can assume that, then I think that's another reason why we don't need a dummy transfer to set CS high delay.

I agree we don't need two xfers, especially in turbo mode.

author maintainer
#5 lore raw

PATCH v3 2/4 Documentation: iio: ad4000: Add new supported parts

In reply to Marcelo Schmitt

Commit <c3948d090080> ("iio: adc: ad4000: Add support for PulSAR devices"), extended the ad4000 driver supports many single-channel PulSAR devices.

Update IIO ad4000 documentation with the extra list of supported devices.

---
 Documentation/iio/ad4000.rst | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)
Documentation/iio/ad4000.rst +16 -1
index de8fd3ae6e62..5578a9cfd9d5 100644
@@ -4,7 +4,7 @@
 AD4000 driver
 =============
 
-Device driver for Analog Devices Inc. AD4000 series of ADCs.
+Device driver for Analog Devices Inc. AD4000 series of ADCs and similar devices.
 
 Supported devices
 =================
@@ -25,6 +25,21 @@ Supported devices
 * `AD4022 <https://www.analog.com/AD4022>`_
 * `ADAQ4001 <https://www.analog.com/ADAQ4001>`_
 * `ADAQ4003 <https://www.analog.com/ADAQ4003>`_
+* `AD7685 <https://www.analog.com/AD7685>`_
+* `AD7686 <https://www.analog.com/AD7686>`_
+* `AD7687 <https://www.analog.com/AD7687>`_
+* `AD7688 <https://www.analog.com/AD7688>`_
+* `AD7690 <https://www.analog.com/AD7690>`_
+* `AD7691 <https://www.analog.com/AD7691>`_
+* `AD7693 <https://www.analog.com/AD7693>`_
+* `AD7942 <https://www.analog.com/AD7942>`_
+* `AD7946 <https://www.analog.com/AD7946>`_
+* `AD7980 <https://www.analog.com/AD7980>`_
+* `AD7982 <https://www.analog.com/AD7982>`_
+* `AD7983 <https://www.analog.com/AD7983>`_
+* `AD7984 <https://www.analog.com/AD7984>`_
+* `AD7988-1 <https://www.analog.com/AD7988-1>`_
+* `AD7988-5 <https://www.analog.com/AD7988-5>`_
 
 Wiring connections
 ------------------
Signature · 1 line
2.47.2
reviewer Reviewed-by on v3 2/4
#6 lore raw

Re: [PATCH v3 2/4] Documentation: iio: ad4000: Add new supported parts

Quoted 7 lines · Marcelo Schmitt

Commit <c3948d090080> ("iio: adc: ad4000: Add support for PulSAR devices"), extended the ad4000 driver supports many single-channel PulSAR devices.

Update IIO ad4000 documentation with the extra list of supported devices.

---

Don't forget to pick up tags. I already gave my:

#7 lore raw

Re: [PATCH v3 2/4] Documentation: iio: ad4000: Add new supported parts

Quoted 9 lines · David Lechner
Quoted 7 lines · Marcelo Schmitt

Commit <c3948d090080> ("iio: adc: ad4000: Add support for PulSAR devices"), extended the ad4000 driver supports many single-channel PulSAR devices.

Update IIO ad4000 documentation with the extra list of supported devices.

---

Don't forget to pick up tags. I already gave my:

Oops, sorry. My bad.
I've picked them now.

Quoted 5 lines

Thanks,
Marcelo

author maintainer
#8 lore raw

PATCH v3 3/4 Documentation: iio: ad4000: Add IIO Device characteristics section

In reply to Marcelo Schmitt

Complement ad4000 IIO driver documentation with considerations about ``_scale_available`` attribute and table of typical channel attributes.

---
 Documentation/iio/ad4000.rst | 47 ++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)
Documentation/iio/ad4000.rst +47 -0
index 5578a9cfd9d5..468d30dc9214 100644
@@ -144,3 +144,50 @@ Set ``adi,sdi-pin`` to ``"cs"`` to select this mode.
                     ^                    |             |
                     +--------------------| SCLK        |
                                          +-------------+
+
+IIO Device characteristics
+==========================
+
+The AD4000 series driver supports differential and pseudo-differential ADCs.
+
+The span compression feature available in AD4000 series devices can be
+enabled/disabled by changing the ``_scale_available`` attribute of the voltage
+channel. Note that span compression configuration requires writing to AD4000
+configuration register, which is only possible when the ADC is wired in 3-wire
+turbo mode, and the SPI controller is ``SPI_MOSI_IDLE_HIGH`` capable. If those
+conditions are not met, no ``_scale_available`` attribute is provided.
+
+Besides that, differential and pseudo-differential voltage channels present
+slightly different sysfs interfaces.
+
+Pseudo-differential ADCs
+------------------------
+
+Typical voltage channel attributes of a pseudo-differential AD4000 series device:
+
++-------------------------------------------+------------------------------------------+
+| Voltage Channel Attributes                | Description                              |
++===========================================+==========================================+
+| ``in_voltage0_raw``                       | Raw ADC output code.                     |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0_offset``                    | Offset to convert raw value to mV.       |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0_scale``                     | Scale factor to convert raw value to mV. |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0_scale_available``           | Toggles input span compression           |
++-------------------------------------------+------------------------------------------+
+
+Differential ADCs
+-----------------
+
+Typical voltage channel attributes of a differential AD4000 series device:
+
++-------------------------------------------+------------------------------------------+
+| Voltage Channel Attributes                | Description                              |
++===========================================+==========================================+
+| ``in_voltage0-voltage1_raw``              | Raw ADC output code.                     |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0-voltage1_scale``            | Scale factor to convert raw value to mV. |
++-------------------------------------------+------------------------------------------+
+| ``in_voltage0-voltage1_scale_available``  | Toggles input span compression           |
++-------------------------------------------+------------------------------------------+
Signature · 1 line
2.47.2
author maintainer
#10 lore raw

PATCH v3 4/4 Documentation: iio: ad4000: Describe offload support

In reply to Marcelo Schmitt

When SPI offloading is supported, the IIO device provides different sysfs interfaces to allow using the adjusting the sample rate. Document SPI offload support for AD4000 and similar devices.

---
 Documentation/iio/ad4000.rst | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
Documentation/iio/ad4000.rst +22 -0
index 468d30dc9214..e490f9604b94 100644
@@ -191,3 +191,25 @@ Typical voltage channel attributes of a differential AD4000 series device:
 +-------------------------------------------+------------------------------------------+
 | ``in_voltage0-voltage1_scale_available``  | Toggles input span compression           |
 +-------------------------------------------+------------------------------------------+
+
+SPI offload support
+-------------------
+
+To be able to achieve the maximum sample rate, the driver can be used with the
+`AXI SPI Engine`_ to provide SPI offload support.
+
+.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/pulsar_adc/index.html
+
+When set for SPI offload support, the IIO device will provide different
+interfaces.
+
+* Either ``in_voltage0_sampling_frequency`` or
+  ``in_voltage0-voltage1_sampling_frequency`` file is provided to allow setting
+  the sample rate.
+* IIO trigger device is not provided (no ``trigger`` directory).
+* ``timestamp`` channel is not provided.
+
+Also, because the ADC output has a one sample latency (delay) when the device is
+wired in "3-wire" mode and only one transfer per sample is done when using SPI
+offloading, the first data sample in the buffer is not valid because it contains
+the output of an earlier conversion result.
Signature · 1 line
2.47.2
reviewer Reviewed-by on v3 4/4
#11 lore raw

Re: [PATCH v3 4/4] Documentation: iio: ad4000: Describe offload support

Quoted 6 lines · Marcelo Schmitt

When SPI offloading is supported, the IIO device provides different sysfs interfaces to allow using the adjusting the sample rate. Document SPI offload support for AD4000 and similar devices.

---