From nobody Thu Apr 9 23:26:30 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C9A5E39D6FD; Thu, 5 Mar 2026 12:23:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772713415; cv=none; b=pixJDcT5syPQ/9mGrGQNmpejREt1gu0VCI+PHCzED4XB3E2iSnh2k8gUP3vyQVxNnV/s+LOolln/DYbCzTmpm5rkurfIOT1CzFoJSdSGucJMcdrvJm6LBpXh1TJHvQR46CTx2Wbobsow/aRvf7K1HXRv6nhSR1roHHq4ivVa6gU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772713415; c=relaxed/simple; bh=XMyV1p3aB7Qf2mFuPa6CrsdsH84IZNGoq026sEdAMXQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZsShrtsFjoQ6gIsxO5gY7PvckgJVElCCf+YO7/qE3mt6MZ3oArKZHSqJA7STIG6XTRoC/UrZHJC2IV9p2jfwG7DepF4VJQ5JfOKg7SDhBTVLD1Y3InWn5QeUwD+0T5zGXO4rQDavFRSa21Di0facjNysbt+GTkTTmh1iZfEEuj0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iEeUuqDu; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="iEeUuqDu" Received: by smtp.kernel.org (Postfix) with ESMTPS id 84F52C4AF13; Thu, 5 Mar 2026 12:23:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772713415; bh=XMyV1p3aB7Qf2mFuPa6CrsdsH84IZNGoq026sEdAMXQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=iEeUuqDu3h+0eFXGuOOjtON1SiuUuCz7s0bkPtt4Ja1A2R86Kt3i5i8XePNjiLtFK kMnS0j+zdQhomHrZLlt1Wwz0xq96cojgFAL44dHXrEeqj/pwKLE61NKdLEpjbKGtHv dbJB+zkSzfP0x2N9aTFlGeOD/ompMSKG3kg+hbdUZ27Y7oq4MR4LYLNo7OryPVb4w6 xHKQj/dRmui2mKVAwBS6dGmqFQDRpsal0+GzdJthwslWiDgOBiyhAIU3/DiSQ5C6fq knKH3iFn7iytCraEUt3tidpVoH0Kxscenpp9esr5hdwEO+mMvspFKbpbF1M7bbWKzd +00fJxZfBBq5g== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 760DEF3092C; Thu, 5 Mar 2026 12:23:35 +0000 (UTC) From: Radu Sabau via B4 Relay Date: Thu, 05 Mar 2026 14:23:30 +0200 Subject: [PATCH 4/4] iio: adc: ad4691: add SPI offload support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260305-ad4692-multichannel-sar-adc-driver-v1-4-336229a8dcc7@analog.com> References: <20260305-ad4692-multichannel-sar-adc-driver-v1-0-336229a8dcc7@analog.com> In-Reply-To: <20260305-ad4692-multichannel-sar-adc-driver-v1-0-336229a8dcc7@analog.com> To: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Liam Girdwood , Mark Brown , Linus Walleij , Bartosz Golaszewski 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, Radu Sabau X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772713413; l=25027; i=radu.sabau@analog.com; s=20260220; h=from:subject:message-id; bh=fsJYfVJAeFuv2N8CEoY4pRwap+ES24z5DGo+5a2M3YI=; b=8v8JFRQz/pAwK4RQsJ0ld0jvDSjFQt97MafVAPQJ0NKgxq2MJg3oZ1bNMiELvBITgMHsbfbXO cXH/3RhjvUHCKdsDscmVXTPZ+UQjwEApA0n8J2JNowRPlz2MfCNg3wU X-Developer-Key: i=radu.sabau@analog.com; a=ed25519; pk=lDPQHgn9jTdt0vo58Na9lLxLaE2mb330if71Cn+EvFU= X-Endpoint-Received: by B4 Relay for radu.sabau@analog.com/20260220 with auth_id=642 X-Original-From: Radu Sabau Reply-To: radu.sabau@analog.com From: Radu Sabau Add SPI offload support to the AD4691 family driver to enable DMA-based RX stream acquisition. When an SPI offload is available, the driver switches to a pre-built SPI message with 32-bit transfers (4-byte frames aligned to DMA width) and registers a periodic offload trigger for autonomous, CPU-independent sampling. The offload path implements its own buffer setup ops (ad4691_offload_buffer_postenable/predisable) that enable the offload trigger and wire the DMAengine buffer, while the existing software triggered buffer path is retained as a fallback for non-offload configurations. Offload channel specs use a 32-bit storage/repeat with a 16-bit shift to extract ADC data from the MSBytes of each DMA word, matching the wire format in Manual Mode where SDO outputs ADC data directly without a command echo. Kconfig gains a dependency on IIO_BUFFER_DMAENGINE. Signed-off-by: Radu Sabau --- drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ad4691.c | 541 +++++++++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 531 insertions(+), 11 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d498f16c0816..93f090e9a562 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -144,6 +144,7 @@ config AD4691 depends on SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select IIO_BUFFER_DMAENGINE select REGMAP help Say yes here to build support for Analog Devices AD4691 Family MuxSAR diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c index ab48f336e46c..7ec0a2555a4b 100644 --- a/drivers/iio/adc/ad4691.c +++ b/drivers/iio/adc/ad4691.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -21,11 +22,15 @@ #include #include #include +#include +#include #include #include #include =20 #include +#include +#include #include =20 #include @@ -49,6 +54,7 @@ */ #define AD4691_MANUAL_MODE_STD_FREQ(x, y) ((y) / (36 * ((x) + 1))) #define AD4691_BITS_PER_XFER 24 +#define AD4691_OFFLOAD_BITS_PER_WORD 32 #define AD4691_CNV_DUTY_CYCLE_NS 380 #define AD4691_MAX_CONV_PERIOD_US 800 =20 @@ -253,6 +259,43 @@ static const struct iio_chan_spec ad4693_manual_channe= ls[] =3D { AD4691_CHANNEL(7, 7, 16, 24, 8) }; =20 +/* + * Manual mode offload channels. + * + * Transfer format: 4-byte SPI frame to match 32-bit DMA width. + * In Manual Mode there is no command echo - SDO outputs ADC data directly. + * 16-bit ADC data in 2 MSBytes, shift=3D16 for extraction. + */ +static const struct iio_chan_spec ad4691_manual_offload_channels[] =3D { + AD4691_CHANNEL(0, 0, 16, 32, 16), + AD4691_CHANNEL(1, 1, 16, 32, 16), + AD4691_CHANNEL(2, 2, 16, 32, 16), + AD4691_CHANNEL(3, 3, 16, 32, 16), + AD4691_CHANNEL(4, 4, 16, 32, 16), + AD4691_CHANNEL(5, 5, 16, 32, 16), + AD4691_CHANNEL(6, 6, 16, 32, 16), + AD4691_CHANNEL(7, 7, 16, 32, 16), + AD4691_CHANNEL(8, 8, 16, 32, 16), + AD4691_CHANNEL(9, 9, 16, 32, 16), + AD4691_CHANNEL(10, 10, 16, 32, 16), + AD4691_CHANNEL(11, 11, 16, 32, 16), + AD4691_CHANNEL(12, 12, 16, 32, 16), + AD4691_CHANNEL(13, 13, 16, 32, 16), + AD4691_CHANNEL(14, 14, 16, 32, 16), + AD4691_CHANNEL(15, 15, 16, 32, 16) +}; + +static const struct iio_chan_spec ad4693_manual_offload_channels[] =3D { + AD4691_CHANNEL(0, 0, 16, 32, 16), + AD4691_CHANNEL(1, 1, 16, 32, 16), + AD4691_CHANNEL(2, 2, 16, 32, 16), + AD4691_CHANNEL(3, 3, 16, 32, 16), + AD4691_CHANNEL(4, 4, 16, 32, 16), + AD4691_CHANNEL(5, 5, 16, 32, 16), + AD4691_CHANNEL(6, 6, 16, 32, 16), + AD4691_CHANNEL(7, 7, 16, 32, 16) +}; + static const struct ad4691_chip_info ad4691_chips[] =3D { [AD4691_ID_AD4691] =3D { .channels =3D ad4691_channels, @@ -310,6 +353,17 @@ struct ad4691_state { struct hrtimer sampling_timer; ktime_t sampling_period; =20 + struct spi_offload *offload; + struct spi_offload_trigger *offload_trigger; + struct spi_offload_trigger *offload_trigger_periodic; + u64 offload_trigger_hz; + struct spi_message offload_msg; + /* Max 16 channels * 2 transfers (cmd + data) + 1 state reset + 1 conv st= art */ + struct spi_transfer offload_xfer[34]; + /* TX commands for manual and accumulator modes */ + u32 offload_tx_cmd[17]; + u32 offload_tx_reset; + u32 offload_tx_conv_stop; /* DMA (thus cache coherency maintenance) may require the * transfer buffers to live in their own cache lines. * Make the buffer large enough for one 24 bit sample and one 64 bit @@ -324,6 +378,65 @@ struct ad4691_state { } scan __aligned(IIO_DMA_MINALIGN); }; =20 +static const struct spi_offload_config ad4691_offload_config =3D { + .capability_flags =3D SPI_OFFLOAD_CAP_TRIGGER | + SPI_OFFLOAD_CAP_RX_STREAM_DMA, +}; + +static bool ad4691_offload_trigger_match(struct spi_offload_trigger *trigg= er, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + if (type !=3D SPI_OFFLOAD_TRIGGER_DATA_READY) + return false; + + /* + * Requires 2 args: + * args[0] is the trigger event (BUSY or DATA_READY). + * args[1] is the GPIO pin number (only GP0 supported). + */ + if (nargs !=3D 2) + return false; + + if (args[0] !=3D AD4691_TRIGGER_EVENT_BUSY && + args[0] !=3D AD4691_TRIGGER_EVENT_DATA_READY) + return false; + + if (args[1] !=3D AD4691_TRIGGER_PIN_GP0) + return false; + + return true; +} + +static int ad4691_offload_trigger_request(struct spi_offload_trigger *trig= ger, + enum spi_offload_trigger_type type, + u64 *args, u32 nargs) +{ + /* + * GP0 is configured as DATA_READY or BUSY in ad4691_config() + * based on the ADC mode. No additional configuration needed here. + */ + if (nargs !=3D 2) + return -EINVAL; + + return 0; +} + +static int ad4691_offload_trigger_validate(struct spi_offload_trigger *tri= gger, + struct spi_offload_trigger_config *config) +{ + if (config->type !=3D SPI_OFFLOAD_TRIGGER_DATA_READY) + return -EINVAL; + + return 0; +} + +static const struct spi_offload_trigger_ops ad4691_offload_trigger_ops =3D= { + .match =3D ad4691_offload_trigger_match, + .request =3D ad4691_offload_trigger_request, + .validate =3D ad4691_offload_trigger_validate, +}; + static void ad4691_disable_regulators(void *data) { struct ad4691_state *st =3D data; @@ -560,6 +673,9 @@ static int ad4691_get_sampling_freq(struct ad4691_state= *st) =20 switch (st->adc_mode) { case AD4691_MANUAL_MODE: + /* Offload uses periodic trigger, non-offload uses hrtimer */ + if (st->offload) + return st->offload_trigger_hz; return DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, ktime_to_ns(st->sampling_period)); case AD4691_CNV_CLOCK_MODE: @@ -600,6 +716,7 @@ static int __ad4691_set_sampling_freq(struct ad4691_sta= te *st, int freq) * ad4691_cnv_burst_period_ns - Compute the CNV_BURST_MODE PWM period. * @st: Driver state. * @n_active: Number of active channels. + * @is_offload: True for the SPI offload path, false for the triggered buf= fer path. * * The period must cover the full conversion time tOSC*(n_active+1) plus * the SPI transfer time for reading the accumulator results and issuing @@ -608,7 +725,7 @@ static int __ad4691_set_sampling_freq(struct ad4691_sta= te *st, int freq) * Return: Period in nanoseconds. */ static u64 ad4691_cnv_burst_period_ns(struct ad4691_state *st, - int n_active) + int n_active, bool is_offload) { unsigned int osc_idx =3D AD4691_OSC_1MHZ; u64 osc_freq, conv_time_ns, spi_bits, spi_time_ns; @@ -620,7 +737,20 @@ static u64 ad4691_cnv_burst_period_ns(struct ad4691_st= ate *st, osc_freq =3D ad4691_int_osc_val[osc_idx]; conv_time_ns =3D div64_u64((u64)(n_active + 1) * NSEC_PER_SEC, osc_freq); =20 - spi_bits =3D (u64)n_active * 32 + 24; + if (is_offload) { + /* + * Offload SPI sequence per trigger: n_active AVG_IN reads + * (4 B each) + STATE_RESET (4 B). + */ + spi_bits =3D (u64)(n_active + 1) * 32; + } else { + /* + * Non-offload sequence per trigger: n_active AVG_IN reads + * (4 B: 2 cmd + 2 data each) + STATE_RESET (3 B). + */ + spi_bits =3D (u64)n_active * 32 + 24; + } + spi_time_ns =3D div64_u64(spi_bits * NSEC_PER_SEC, st->spi->max_speed_hz); =20 /* 50% margin on SPI time absorbs OS scheduling jitter. */ @@ -662,7 +792,9 @@ static int ad4691_pwm_get(struct spi_device *spi, struc= t ad4691_state *st) * transfer time. Use worst-case channel count here; the period * is refined at buffer enable time when the active count is known. */ - u64 period_ns =3D ad4691_cnv_burst_period_ns(st, st->chip->num_channels); + u64 period_ns =3D ad4691_cnv_burst_period_ns(st, + st->chip->num_channels, + false); int pwm_freq =3D (int)max(1ULL, div64_u64(NSEC_PER_SEC, period_ns)); =20 return __ad4691_set_sampling_freq(st, pwm_freq); @@ -683,6 +815,26 @@ static int ad4691_set_sampling_freq(struct iio_dev *in= dio_dev, unsigned int freq mutex_lock(&st->lock); switch (st->adc_mode) { case AD4691_MANUAL_MODE: + /* For offload mode, validate and store frequency for periodic trigger */ + if (st->offload) { + struct spi_offload_trigger_config config =3D { + .type =3D SPI_OFFLOAD_TRIGGER_PERIODIC, + .periodic =3D { + .frequency_hz =3D freq, + }, + }; + + ret =3D spi_offload_trigger_validate(st->offload_trigger_periodic, + &config); + if (ret) + goto exit; + + st->offload_trigger_hz =3D config.periodic.frequency_hz; + ret =3D 0; + goto exit; + } + + /* Non-offload: update hrtimer sampling period */ if (!freq || freq > st->chip->max_rate) { ret =3D -EINVAL; goto exit; @@ -719,7 +871,9 @@ static int ad4691_set_sampling_freq(struct iio_dev *ind= io_dev, unsigned int freq * count. The exact period is refined at buffer enable time when * the active channel count is known. */ - period_ns =3D ad4691_cnv_burst_period_ns(st, st->chip->num_channels); + period_ns =3D ad4691_cnv_burst_period_ns(st, + st->chip->num_channels, + false); pwm_freq =3D (int)max(1ULL, div64_u64(NSEC_PER_SEC, period_ns)); ret =3D __ad4691_set_sampling_freq(st, pwm_freq); =20 @@ -839,6 +993,13 @@ static int ad4691_read_raw(struct iio_dev *indio_dev, =20 /* * Wait for conversion to complete using a timed delay. + * An interrupt-driven approach is not used for single-shot + * reads: the DATA_READY IRQ is registered only in the + * triggered buffer path, and offload configurations route + * DATA_READY to the SPI engine, not to a CPU interrupt. + * Using usleep_range keeps the driver simple and correct + * across all configurations. + * * CNV_CLOCK_MODE conversion time is bounded by * AD4691_MAX_CONV_PERIOD_US. All other modes are driven by * the internal oscillator; two oscillator periods cover a @@ -1072,6 +1233,294 @@ static const struct iio_buffer_setup_ops ad4691_buf= fer_setup_ops =3D { .postdisable =3D &ad4691_buffer_postdisable, }; =20 +static int ad4691_offload_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st =3D iio_priv(indio_dev); + struct spi_offload_trigger_config config =3D { }; + struct spi_offload_trigger *trigger; + struct spi_transfer *xfer =3D st->offload_xfer; + int ret, num_xfers =3D 0; + int active_chans[16]; + unsigned int bit; + int n_active =3D 0; + int i; + + memset(xfer, 0, sizeof(st->offload_xfer)); + + /* Collect active channels in scan order */ + iio_for_each_active_channel(indio_dev, bit) + active_chans[n_active++] =3D bit; + + /* + * MANUAL_MODE uses a periodic (PWM) trigger and reads directly from the = ADC. + * All other modes use the DATA_READY trigger and read from accumulators. + */ + if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + config.type =3D SPI_OFFLOAD_TRIGGER_PERIODIC; + config.periodic.frequency_hz =3D st->offload_trigger_hz; + trigger =3D st->offload_trigger_periodic; + if (!trigger) + return -EINVAL; + } else { + if (st->adc_mode !=3D AD4691_AUTONOMOUS_MODE) + ret =3D regmap_write(st->regmap, AD4691_GPIO_MODE1_REG, + AD4691_DATA_READY); + else + ret =3D regmap_write(st->regmap, AD4691_GPIO_MODE1_REG, + AD4691_ADC_BUSY); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4691_STATE_RESET_REG, + AD4691_STATE_RESET_ALL); + if (ret) + return ret; + + /* Configure accumulator masks - 0 =3D enabled, 1 =3D masked */ + ret =3D regmap_write(st->regmap, AD4691_ACC_MASK1_REG, + ~(*indio_dev->active_scan_mask) & 0xFF); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4691_ACC_MASK2_REG, + ~(*indio_dev->active_scan_mask >> 8) & 0xFF); + if (ret) + return ret; + + /* Configure sequencer with active channels */ + ret =3D regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, + *indio_dev->active_scan_mask); + if (ret) + return ret; + + /* Configure accumulator count limit for each active channel */ + iio_for_each_active_channel(indio_dev, bit) { + ret =3D regmap_write(st->regmap, AD4691_ACC_COUNT_LIMIT(bit), + AD4691_ACC_COUNT_VAL); + if (ret) + return ret; + } + + config.type =3D SPI_OFFLOAD_TRIGGER_DATA_READY; + trigger =3D st->offload_trigger; + } + + switch (st->adc_mode) { + case AD4691_CNV_CLOCK_MODE: + case AD4691_CNV_BURST_MODE: + case AD4691_AUTONOMOUS_MODE: + case AD4691_SPI_BURST_MODE: + /* + * AUTONOMOUS mode: must stop conversion before reading. + * Sequence: CONV_STOP -> read accumulators -> STATE_RESET -> CONV_START + */ + if (st->adc_mode =3D=3D AD4691_AUTONOMOUS_MODE) { + /* + * With bits_per_word=3D32, SPI engine reads native u32 + * and transmits MSB first. No byte-swap needed. + */ + st->offload_tx_conv_stop =3D (AD4691_OSC_EN_REG >> 8) << 24 | + (AD4691_OSC_EN_REG & 0xFF) << 16 | + 0x00 << 8; /* CONV_START =3D 0 to stop */ + xfer[num_xfers].tx_buf =3D &st->offload_tx_conv_stop; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].cs_change =3D 1; + num_xfers++; + } + + /* + * Single transfer per channel: 2-byte cmd + 2-byte data =3D 4 bytes + * (one 32-bit SPI Engine DMA word). + * + * AVG_IN registers are used instead of ACC_IN. See the + * AD4691_OFFLOAD_CHANNEL macro for a detailed explanation. + * + * RX word layout (big-endian): [cmd_hi, cmd_lo, d_hi, d_lo] + */ + for (i =3D 0; i < n_active; i++) { + unsigned int reg; + int ch =3D active_chans[i]; + + reg =3D AD4691_AVG_IN(ch); + st->offload_tx_cmd[ch] =3D + ((reg >> 8) | 0x80) << 24 | + (reg & 0xFF) << 16; + xfer[num_xfers].tx_buf =3D &st->offload_tx_cmd[ch]; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].offload_flags =3D SPI_OFFLOAD_XFER_RX_STREAM; + xfer[num_xfers].cs_change =3D 1; + num_xfers++; + } + + /* + * State reset: clear accumulator so DATA_READY can fire again. + * + * With bits_per_word=3D32, SPI engine reads native u32 + * and transmits MSB first. No byte-swap needed. + * + * The device uses address-descending mode when streaming, so + * the 4th byte is written to the OSC_EN register. In AUTONOMOUS + * and SPI_BURST modes we want OSC_EN re-asserted, therefore set + * the 4th byte to 0x01 for those modes. + */ + if (st->adc_mode =3D=3D AD4691_AUTONOMOUS_MODE || + st->adc_mode =3D=3D AD4691_SPI_BURST_MODE) { + st->offload_tx_reset =3D ((AD4691_STATE_RESET_REG >> 8) << 24) | + ((AD4691_STATE_RESET_REG & 0xFF) << 16) | + (0x01 << 8) | 0x01; + } else { + st->offload_tx_reset =3D ((AD4691_STATE_RESET_REG >> 8) << 24) | + ((AD4691_STATE_RESET_REG & 0xFF) << 16) | + (0x01 << 8); + } + + xfer[num_xfers].tx_buf =3D &st->offload_tx_reset; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].cs_change =3D 0; + num_xfers++; + + break; + + case AD4691_MANUAL_MODE: + /* + * Manual mode with CNV tied to CS: Each CS toggle triggers a + * conversion AND reads the previous conversion result (pipeline). + */ + for (i =3D 0; i < n_active; i++) { + st->offload_tx_cmd[num_xfers] =3D AD4691_ADC_CHAN(active_chans[i]) << 2= 4; + xfer[num_xfers].tx_buf =3D &st->offload_tx_cmd[num_xfers]; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].cs_change =3D 1; + xfer[num_xfers].cs_change_delay.value =3D 1000; + xfer[num_xfers].cs_change_delay.unit =3D SPI_DELAY_UNIT_NSECS; + /* First transfer RX is garbage - don't capture it */ + if (num_xfers) + xfer[num_xfers].offload_flags =3D SPI_OFFLOAD_XFER_RX_STREAM; + num_xfers++; + } + + /* Final NOOP to flush pipeline and get last channel's data */ + st->offload_tx_cmd[num_xfers] =3D AD4691_NOOP << 24; + xfer[num_xfers].tx_buf =3D &st->offload_tx_cmd[num_xfers]; + xfer[num_xfers].len =3D 4; + xfer[num_xfers].bits_per_word =3D 32; + xfer[num_xfers].speed_hz =3D st->spi->max_speed_hz; + xfer[num_xfers].cs_change =3D 0; + xfer[num_xfers].offload_flags =3D SPI_OFFLOAD_XFER_RX_STREAM; + num_xfers++; + break; + + default: + return -EINVAL; + } + + if (num_xfers =3D=3D 0) + return -EINVAL; + + /* + * For MANUAL_MODE, validate that the trigger frequency is low enough + * for all SPI transfers to complete. Each transfer is 32 bits. + * Add 50% margin for CS setup/hold and other overhead. + */ + if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + u64 min_period_ns; + u64 trigger_period_ns; + + /* Time for all transfers in nanoseconds, with 50% overhead margin */ + min_period_ns =3D div64_u64((u64)num_xfers * AD4691_OFFLOAD_BITS_PER_WOR= D * + NSEC_PER_SEC * 3, + st->spi->max_speed_hz * 2); + + trigger_period_ns =3D div64_u64(NSEC_PER_SEC, st->offload_trigger_hz); + + if (trigger_period_ns < min_period_ns) + return -EINVAL; + } + + spi_message_init_with_transfers(&st->offload_msg, xfer, num_xfers); + st->offload_msg.offload =3D st->offload; + + ret =3D spi_optimize_message(st->spi, &st->offload_msg); + if (ret) + return ret; + + /* + * Start conversions before enabling the trigger for all non-MANUAL modes. + * If the trigger is enabled first, the SPI engine blocks waiting for + * DATA_READY, and any subsequent SPI write times out. + * + * MANUAL_MODE: CNV is tied to CS; conversion starts with each transfer. + * CNV_BURST_MODE: cnv_period updated above; PWM starts conversions. + * AUTONOMOUS_MODE: OSC_EN=3D1 written here; DATA_READY fires when + * accumulation completes and triggers the SPI engine offload sequence. + */ + if (st->adc_mode !=3D AD4691_MANUAL_MODE) { + if (st->adc_mode =3D=3D AD4691_CNV_BURST_MODE) + st->cnv_period =3D + ad4691_cnv_burst_period_ns(st, n_active, true); + ret =3D ad4691_sampling_enable(st, true); + if (ret) + goto err_unoptimize_message; + } + + ret =3D spi_offload_trigger_enable(st->offload, trigger, &config); + if (ret) + goto err_sampling_disable; + + return 0; + +err_sampling_disable: + ad4691_sampling_enable(st, false); +err_unoptimize_message: + spi_unoptimize_message(&st->offload_msg); + return ret; +} + +static int ad4691_offload_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad4691_state *st =3D iio_priv(indio_dev); + struct spi_offload_trigger *trigger; + int ret =3D 0, tmp; + + trigger =3D (st->adc_mode =3D=3D AD4691_MANUAL_MODE) ? + st->offload_trigger_periodic : st->offload_trigger; + + spi_offload_trigger_disable(st->offload, trigger); + spi_unoptimize_message(&st->offload_msg); + + /* Stop conversions and reset sequencer state (not needed for MANUAL_MODE= ) */ + if (st->adc_mode !=3D AD4691_MANUAL_MODE) { + tmp =3D ad4691_sampling_enable(st, false); + if (!ret) + ret =3D tmp; + + tmp =3D regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG, + AD4691_SEQ_ALL_CHANNELS_OFF); + if (!ret) + ret =3D tmp; + + tmp =3D regmap_write(st->regmap, AD4691_STATE_RESET_REG, + AD4691_STATE_RESET_ALL); + if (!ret) + ret =3D tmp; + } + + return ret; +} + +static const struct iio_buffer_setup_ops ad4691_offload_buffer_setup_ops = =3D { + .postenable =3D &ad4691_offload_buffer_postenable, + .predisable =3D &ad4691_offload_buffer_predisable, +}; + static irqreturn_t ad4691_irq(int irq, void *private) { struct iio_dev *indio_dev =3D private; @@ -1353,10 +1802,17 @@ static void ad4691_setup_channels(struct iio_dev *i= ndio_dev, struct ad4691_state *st) { if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { - if (st->chip->num_channels =3D=3D 8) - indio_dev->channels =3D ad4693_manual_channels; - else - indio_dev->channels =3D ad4691_manual_channels; + if (st->offload) { + if (st->chip->num_channels =3D=3D 8) + indio_dev->channels =3D ad4693_manual_offload_channels; + else + indio_dev->channels =3D ad4691_manual_offload_channels; + } else { + if (st->chip->num_channels =3D=3D 8) + indio_dev->channels =3D ad4693_manual_channels; + else + indio_dev->channels =3D ad4691_manual_channels; + } } else { indio_dev->channels =3D st->chip->channels; } @@ -1364,6 +1820,54 @@ static void ad4691_setup_channels(struct iio_dev *in= dio_dev, indio_dev->num_channels =3D st->chip->num_channels; } =20 +static int ad4691_setup_offload(struct iio_dev *indio_dev, + struct ad4691_state *st) +{ + struct device *dev =3D &st->spi->dev; + struct dma_chan *rx_dma; + int ret; + + if (st->adc_mode =3D=3D AD4691_MANUAL_MODE) { + st->offload_trigger_periodic =3D devm_spi_offload_trigger_get(dev, + st->offload, SPI_OFFLOAD_TRIGGER_PERIODIC); + if (IS_ERR(st->offload_trigger_periodic)) + return dev_err_probe(dev, + PTR_ERR(st->offload_trigger_periodic), + "failed to get periodic offload trigger\n"); + + st->offload_trigger_hz =3D AD4691_MANUAL_MODE_STD_FREQ(st->chip->num_cha= nnels, + st->spi->max_speed_hz); + } else { + struct spi_offload_trigger_info trigger_info =3D { + .fwnode =3D dev_fwnode(dev), + .ops =3D &ad4691_offload_trigger_ops, + .priv =3D st, + }; + + ret =3D devm_spi_offload_trigger_register(dev, &trigger_info); + if (ret) + return dev_err_probe(dev, ret, + "failed to register offload trigger\n"); + + st->offload_trigger =3D devm_spi_offload_trigger_get(dev, + st->offload, SPI_OFFLOAD_TRIGGER_DATA_READY); + if (IS_ERR(st->offload_trigger)) + return dev_err_probe(dev, PTR_ERR(st->offload_trigger), + "failed to get offload trigger\n"); + } + + rx_dma =3D devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload); + if (IS_ERR(rx_dma)) + return dev_err_probe(dev, PTR_ERR(rx_dma), + "failed to get offload RX DMA\n"); + + indio_dev->modes =3D INDIO_DIRECT_MODE | INDIO_BUFFER_HARDWARE; + indio_dev->setup_ops =3D &ad4691_offload_buffer_setup_ops; + + return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, + rx_dma, IIO_BUFFER_DIRECTION_IN); +} + static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev, struct ad4691_state *st) { @@ -1455,6 +1959,14 @@ static int ad4691_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(st->regmap), "Failed to initialize regmap\n"); =20 + st->offload =3D devm_spi_offload_get(dev, spi, &ad4691_offload_config); + if (IS_ERR(st->offload)) { + if (PTR_ERR(st->offload) !=3D -ENODEV) + return dev_err_probe(dev, PTR_ERR(st->offload), + "failed to get SPI offload\n"); + st->offload =3D NULL; + } + st->chip =3D spi_get_device_match_data(spi); if (!st->chip) { st->chip =3D (void *)spi_get_device_id(spi)->driver_data; @@ -1481,9 +1993,15 @@ static int ad4691_probe(struct spi_device *spi) =20 ad4691_setup_channels(indio_dev, st); =20 - ret =3D ad4691_setup_triggered_buffer(indio_dev, st); - if (ret) - return ret; + if (st->offload) { + ret =3D ad4691_setup_offload(indio_dev, st); + if (ret) + return ret; + } else { + ret =3D ad4691_setup_triggered_buffer(indio_dev, st); + if (ret) + return ret; + } =20 return devm_iio_device_register(dev, indio_dev); } @@ -1510,3 +2028,4 @@ module_spi_driver(ad4691_driver); MODULE_AUTHOR("Radu Sabau "); MODULE_DESCRIPTION("Analog Devices AD4691 Family ADC Driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IIO_DMA_BUFFER"); --=20 2.43.0