From: Jonathan Cameron <jic23@kernel.org>
To: Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>
Cc: radu.sabau@analog.com, "Lars-Peter Clausen" <lars@metafoo.de>,
"Michael Hennerich" <Michael.Hennerich@analog.com>,
"David Lechner" <dlechner@baylibre.com>,
"Nuno Sá" <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>,
linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-gpio@vger.kernel.org
Subject: Re: [PATCH v4 4/4] iio: adc: ad4691: add SPI offload support
Date: Sat, 21 Mar 2026 15:28:50 +0000 [thread overview]
Message-ID: <20260321152850.3f335d7c@jic23-huawei> (raw)
In-Reply-To: <20260320-ad4692-multichannel-sar-adc-driver-v4-4-052c1050507a@analog.com>
On Fri, 20 Mar 2026 13:03:58 +0200
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 4-byte 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 32-bit frames (bits_per_word=32, len=4) for
> DMA word alignment. This patch promotes the channel scan_type from
> storagebits=16 (triggered-buffer path) to storagebits=32 to match the
> DMA word size; the triggered-buffer paths are updated to the same layout
> for consistency.
That's quite a large cost for the kfifo sizing, particularly if timestamps
are enabled. I think I'd prefer we kept the exiting paths using 16 bit data
storage for each channel.
> CNV Burst Mode channel data arrives in the lower 16
> bits of the 32-bit word (shift=0); Manual Mode data arrives in the upper
> 16 bits (shift=16), matching the 4-byte SPI transfer layout
> [data_hi, data_lo, 0, 0]. A separate ad4691_manual_channels[] array
> encodes the shift=16 scan type for manual mode.
That's odd - but fair enough if that's what the IP ends up doing.
>
> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.
>
> Signed-off-by: Radu Sabau <radu.sabau@analog.com>
Various other minor comments inline.
> -#define AD4691_CHANNEL(ch) \
> +/*
> + * 16-bit ADC data is stored in 32-bit slots to match the SPI offload DMA
> + * word size (32 bits per transfer). The shift reflects the data position
As mentioned elsewhere, I don't think we care about matching the offload
layout. Lots of existing drivers don't and if we can we want to minimize
wasted space whilst still keep the data naturally aligned to make accesses easy.
> + * within the 32-bit word:
> + * CNV_BURST: RX = [dummy, dummy, data_hi, data_lo] -> shift = 0
> + * MANUAL: RX = [data_hi, data_lo, dummy, dummy] -> shift = 16
> + * The triggered-buffer paths store data in the same position for consistency.
> + * Do not "fix" storagebits to 16.
> + */
> +#define AD4691_CHANNEL(ch, _shift) \
> { \
> .type = IIO_VOLTAGE, \
> .indexed = 1, \
> @@ -118,40 +140,72 @@ struct ad4691_chip_info {
> .scan_type = { \
> .sign = 'u', \
> .realbits = 16, \
> - .storagebits = 16, \
> - .shift = 0, \
> + .storagebits = 32, \
> + .shift = _shift, \
> }, \
> }
> @@ -227,9 +285,9 @@ struct ad4691_state {
> */
> struct mutex lock;
> /*
> - * Per-buffer-enabl ree lifetimesources:
> - * Manual Mode - a pre-built SPI message that clocks out N+1
> - * transfers in one go.
> + * Per-buffer-enable lifetime resources (triggered-buffer paths):
> + * Manual Mode - a pre-built SPI message that clocks out N+1
> + * transfers in one go.
> * CNV Burst Mode - a pre-built SPI message that clocks out 2*N
> * transfers in one go.
> */
> @@ -238,9 +296,19 @@ struct ad4691_state {
> struct spi_transfer *scan_xfers;
> __be16 *scan_tx;
> __be16 *scan_rx;
> - /* Scan buffer: one slot per channel (u16) plus timestamp */
> + /* SPI offload DMA path resources */
> + struct spi_offload *offload;
> + /* SPI offload trigger - periodic (MANUAL) or DATA_READY (CNV_BURST) */
> + struct spi_offload_trigger *offload_trigger;
> + u64 offload_trigger_hz;
> + struct spi_message offload_msg;
> + /* Max 16 channel xfers + 1 state-reset or NOOP */
> + struct spi_transfer offload_xfer[17];
> + u32 offload_tx_cmd[17];
Andy already commented on this being large. Allocating separately
probably makes sense.
> + u32 offload_tx_reset;
> + /* Scan buffer: one slot per channel (u32) plus timestamp */
> struct {
> - u16 vals[16];
> + u32 vals[16];
> s64 ts __aligned(8);
> } scan __aligned(IIO_DMA_MINALIGN);
> };
> +static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev)
> +{
> + struct ad4691_state *st = iio_priv(indio_dev);
> + 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 n_active = hweight_long(*indio_dev->active_scan_mask);
> + unsigned int bit, k;
> + int ret;
> +
> + ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
> + (u16)~(*indio_dev->active_scan_mask));
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
> + *indio_dev->active_scan_mask);
> + if (ret)
> + return ret;
> +
> + iio_for_each_active_channel(indio_dev, bit) {
> + ret = regmap_write(st->regmap, AD4691_ACC_COUNT_LIMIT(bit),
> + AD4691_ACC_COUNT_VAL);
> + if (ret)
> + return ret;
> + }
> +
> + ret = ad4691_enter_conversion_mode(st);
> + if (ret)
> + return ret;
> +
> + memset(st->offload_xfer, 0, sizeof(st->offload_xfer));
> +
> + /*
> + * N transfers to read N AVG_IN registers plus one state-reset
> + * transfer (no RX) to re-arm DATA_READY.
> + * TX: [reg_hi | 0x80, reg_lo, 0x00, 0x00]
> + * RX: [0x00, 0x00, data_hi, data_lo] (shift=0)
> + */
> + k = 0;
> + iio_for_each_active_channel(indio_dev, bit) {
> + unsigned int reg = AD4691_AVG_IN(bit);
> +
> + st->offload_tx_cmd[k] =
> + cpu_to_be32(((reg >> 8 | 0x80) << 24) |
> + ((reg & 0xFF) << 16));
This is an odd looking construct. Maybe it's worth casting offload_tx_cmd[k] to
a u8 * and just filling the two bytes in directly.
> + st->offload_xfer[k].tx_buf = &st->offload_tx_cmd[k];
> + st->offload_xfer[k].len = sizeof(u32);
> + st->offload_xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;
> + st->offload_xfer[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
> + if (k < n_active - 1)
> + st->offload_xfer[k].cs_change = 1;
> + k++;
> + }
> +
> + /* State reset to re-arm DATA_READY for the next scan. */
> + st->offload_tx_reset =
> + cpu_to_be32(((AD4691_STATE_RESET_REG >> 8) << 24) |
> + ((AD4691_STATE_RESET_REG & 0xFF) << 16) |
> + (AD4691_STATE_RESET_ALL << 8));
Similar to above. Feels like we should be manipulating this as a u8[4]
> + st->offload_xfer[k].tx_buf = &st->offload_tx_reset;
> + st->offload_xfer[k].len = sizeof(u32);
> + st->offload_xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;
> + k++;
> +
> + spi_message_init_with_transfers(&st->offload_msg, st->offload_xfer, k);
> + st->offload_msg.offload = st->offload;
> +
> + ret = spi_optimize_message(spi, &st->offload_msg);
> + if (ret)
> + goto err_exit_conversion;
> +
> + ret = ad4691_sampling_enable(st, true);
> + if (ret)
> + goto err_unoptimize;
> +
> + ret = spi_offload_trigger_enable(st->offload, st->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->offload_msg);
> +err_exit_conversion:
> + ad4691_exit_conversion_mode(st);
> + return ret;
> +}
next prev parent reply other threads:[~2026-03-21 15:29 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-20 11:03 [PATCH v4 0/4] iio: adc: ad4691: add driver for AD4691 multichannel SAR ADC family Radu Sabau via B4 Relay
2026-03-20 11:03 ` [PATCH v4 1/4] dt-bindings: iio: adc: add AD4691 family Radu Sabau via B4 Relay
2026-03-20 12:26 ` Rob Herring (Arm)
2026-03-21 14:35 ` Jonathan Cameron
2026-03-21 16:11 ` David Lechner
2026-03-20 11:03 ` [PATCH v4 2/4] iio: adc: ad4691: add initial driver for " Radu Sabau via B4 Relay
2026-03-20 15:16 ` Andy Shevchenko
2026-03-21 14:48 ` Jonathan Cameron
2026-03-23 11:44 ` Nuno Sá
2026-03-20 11:03 ` [PATCH v4 3/4] iio: adc: ad4691: add triggered buffer support Radu Sabau via B4 Relay
2026-03-20 16:14 ` Uwe Kleine-König
2026-03-21 15:16 ` Jonathan Cameron
2026-03-23 9:03 ` Sabau, Radu bogdan
2026-03-24 12:22 ` Nuno Sá
2026-03-25 12:47 ` Sabau, Radu bogdan
2026-03-25 13:12 ` Nuno Sá
2026-03-20 11:03 ` [PATCH v4 4/4] iio: adc: ad4691: add SPI offload support Radu Sabau via B4 Relay
2026-03-20 17:07 ` Andy Shevchenko
2026-03-21 15:28 ` Jonathan Cameron [this message]
2026-03-22 3:05 ` kernel test robot
2026-03-23 13:06 ` kernel test robot
2026-03-25 11:32 ` kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260321152850.3f335d7c@jic23-huawei \
--to=jic23@kernel.org \
--cc=Michael.Hennerich@analog.com \
--cc=andy@kernel.org \
--cc=brgl@kernel.org \
--cc=broonie@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=devnull+radu.sabau.analog.com@kernel.org \
--cc=dlechner@baylibre.com \
--cc=krzk+dt@kernel.org \
--cc=lars@metafoo.de \
--cc=lgirdwood@gmail.com \
--cc=linusw@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=nuno.sa@analog.com \
--cc=p.zabel@pengutronix.de \
--cc=radu.sabau@analog.com \
--cc=robh@kernel.org \
--cc=ukleinek@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox