[{"id":3673464,"web_url":"http://patchwork.ozlabs.org/comment/3673464/","msgid":"<1d0d41c8-7867-4459-a91a-a2c6774b1885@baylibre.com>","list_archive_url":null,"date":"2026-04-04T15:34:29","subject":"Re: [PATCH v6 4/4] iio: adc: ad4691: add SPI offload support","submitter":{"id":87228,"url":"http://patchwork.ozlabs.org/api/people/87228/","name":"David Lechner","email":"dlechner@baylibre.com"},"content":"On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:\n> From: Radu Sabau <radu.sabau@analog.com>\n> \n> Add SPI offload support to enable DMA-based, CPU-independent data\n> acquisition using the SPI Engine offload framework.\n> \n> When an SPI offload is available (devm_spi_offload_get() succeeds),\n> the driver registers a DMA engine IIO buffer and uses dedicated buffer\n> setup operations. If no offload is available the existing software\n> triggered buffer path is used unchanged.\n> \n> Both CNV Burst Mode and Manual Mode support offload, but use different\n> trigger mechanisms:\n> \n> CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY\n> signal on the GP pin specified by the trigger-source consumer reference\n> in the device tree (one cell = GP pin number 0-3). For this mode the\n> driver acts as both an SPI offload consumer (DMA RX stream, message\n> optimization) and a trigger source provider: it registers the\n> GP/DATA_READY output via devm_spi_offload_trigger_register() so the\n> offload framework can match the '#trigger-source-cells' phandle and\n> automatically fire the SPI Engine DMA transfer at end-of-conversion.\n> \n> Manual Mode: the SPI Engine is triggered by a periodic trigger at\n> the configured sampling frequency. The pre-built SPI message uses\n> the pipelined CNV-on-CS protocol: N+1 4-byte transfers are issued\n> for N active channels (the first result is discarded as garbage from\n> the pipeline flush) and the remaining N results are captured by DMA.\n> \n> All offload transfers use 32-bit frames (bits_per_word=32, len=4) for\n> DMA word alignment. This patch promotes the channel scan_type from\n> storagebits=16 (triggered-buffer path) to storagebits=32 to match the\n> DMA word size; the triggered-buffer paths are updated to the same layout\n> for consistency. CNV Burst Mode channel data arrives in the lower 16\n> bits of the 32-bit word (shift=0); Manual Mode data arrives in the upper\n> 16 bits (shift=16), matching the 4-byte SPI transfer layout\n> [data_hi, data_lo, 0, 0]. A separate ad4691_manual_channels[] array\n> encodes the shift=16 scan type for manual mode.\n> \n> Add driver documentation under Documentation/iio/ad4691.rst covering\n> operating modes, oversampling, reference voltage, SPI offload paths,\n> and buffer data layout; register in MAINTAINERS and index.rst\n\nDocumentation should be separate patch. It covers more than just SPI\noffload.\n\n> \n> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.\n> \n> Signed-off-by: Radu Sabau <radu.sabau@analog.com>\n> ---\n>  Documentation/iio/ad4691.rst | 259 ++++++++++++++++++++++++++\n>  Documentation/iio/index.rst  |   1 +\n>  MAINTAINERS                  |   1 +\n>  drivers/iio/adc/Kconfig      |   1 +\n>  drivers/iio/adc/ad4691.c     | 422 ++++++++++++++++++++++++++++++++++++++++++-\n>  5 files changed, 676 insertions(+), 8 deletions(-)\n> \n> diff --git a/Documentation/iio/ad4691.rst b/Documentation/iio/ad4691.rst\n> new file mode 100644\n> index 000000000000..36f0c841605a\n> --- /dev/null\n> +++ b/Documentation/iio/ad4691.rst\n> @@ -0,0 +1,259 @@\n> +.. SPDX-License-Identifier: GPL-2.0-only\n> +\n> +=============\n> +AD4691 driver\n> +=============\n> +\n> +ADC driver for Analog Devices Inc. AD4691 family of multichannel SAR ADCs.\n> +The module name is ``ad4691``.\n> +\n> +\n> +Supported devices\n> +=================\n> +\n> +The following chips are supported by this driver:\n> +\n> +* `AD4691 <https://www.analog.com/en/products/ad4691.html>`_ — 16-channel, 500 kSPS\n> +* `AD4692 <https://www.analog.com/en/products/ad4692.html>`_ — 16-channel, 1 MSPS\n> +* `AD4693 <https://www.analog.com/en/products/ad4693.html>`_ — 8-channel, 500 kSPS\n> +* `AD4694 <https://www.analog.com/en/products/ad4694.html>`_ — 8-channel, 1 MSPS\n> +\n> +\n> +IIO channels\n> +============\n> +\n> +Each physical ADC input maps to one IIO voltage channel. The AD4691 and AD4692\n> +expose 16 channels (``voltage0`` through ``voltage15``); the AD4693 and AD4694\n> +expose 8 channels (``voltage0`` through ``voltage7``).\n> +\n> +All channels share a common scale (``in_voltage_scale``), derived from the\n> +reference voltage. Each channel independently exposes:\n> +\n> +* ``in_voltageN_raw`` — single-shot ADC result\n> +* ``in_voltageN_sampling_frequency`` — internal oscillator frequency used for\n\nAs mentioned in another patch, sampling_frquency != osciallator frequency when\noversampling ratio != 1. So this needs to be changed to reflect that.\n\n> +  single-shot reads and CNV Burst Mode buffered captures\n> +* ``in_voltageN_sampling_frequency_available`` — list of valid oscillator\n> +  frequencies\n> +* ``in_voltageN_oversampling_ratio`` — per-channel hardware accumulation depth\n> +* ``in_voltageN_oversampling_ratio_available`` — list of valid ratios\n> +\n> +\n> +Operating modes\n> +===============\n> +\n> +The driver supports two operating modes, auto-detected from the device tree at\n> +probe time. Both modes transition to and from an internal Autonomous Mode idle\n> +state when the IIO buffer is enabled and disabled.\n> +\n> +Manual Mode\n> +-----------\n> +\n> +Selected when no ``pwms`` property is present in the device tree. The CNV pin\n> +is tied to the SPI chip-select: every CS assertion both triggers a new\n> +conversion and returns the result of the previous one (pipelined N+1 scheme).\n> +\n> +To read N channels the driver issues N+1 SPI transfers in a single optimised\n> +message:\n> +\n> +* Transfers 0 to N-1 each carry ``AD4691_ADC_CHAN(n)`` in the TX byte to\n> +  select the next channel; the RX byte of transfer ``k+1`` contains the result\n> +  of the channel selected in transfer ``k``.\n> +* Transfer N is a NOOP (0x00) to flush the last conversion result out of the\n> +  pipeline.\n> +\n> +The external IIO trigger (``pollfunc_store_time``) drives the trigger handler,\n\nI'm not sure \"external\" is the best word to describe this. I would say a \"user-\ndefined IIO triger (e.g. hrtimer trigger)\".\n\n> +which executes the pre-built SPI message and pushes the scan to the buffer.\n> +\n> +CNV Burst Mode\n> +--------------\n> +\n> +Selected when a ``pwms`` property is present in the device tree. The PWM drives\n> +the CNV pin independently of SPI at the configured conversion rate, and a GP\n> +pin (identified by ``interrupt-names``) asserts DATA_READY at end-of-burst to\n> +signal that the AVG_IN result registers are ready to be read.\n> +\n> +The IRQ handler stops the PWM, fires the IIO trigger, and the trigger handler\n\nIf we stop the PWM after an IRQ, then we don't get a consistent sample rate.\nIdeally, we would leave the PWM running and just pick a rate slow enough that\nthere is plenty of time to read the data. Otherwise, this mode doesn't seem\nparticularly useful.\n\n> +reads all active ``AVG_IN(n)`` registers in a single optimised SPI message and\n> +pushes the scan to the buffer.\n> +\n> +The buffer sampling frequency (i.e. the PWM rate) is controlled by the\n> +``sampling_frequency`` attribute on the IIO buffer. Valid values span from the\n> +chip's minimum oscillator rate up to its maximum conversion rate\n> +(500 kSPS for AD4691/AD4693, 1 MSPS for AD4692/AD4694).\n\nValid, but not usable without SPI offload.\n\n> +\n> +Autonomous Mode (idle / single-shot)\n> +-------------------------------------\n> +\n> +The chip idles in Autonomous Mode whenever the IIO buffer is disabled. In this\n> +state, ``read_raw`` requests (``in_voltageN_raw``) use the internal oscillator\n> +to perform a single conversion on the requested channel and read back the\n> +result from the ``AVG_IN(N)`` register. The oscillator is started and stopped\n> +for each read to save power.\n> +\n> +\n> +Oversampling\n> +============\n> +\n> +Each channel has an independent hardware accumulator (ACC_DEPTH_IN) that\n> +averages a configurable number of successive conversions before DATA_READY\n> +asserts. The result is always returned as a 16-bit mean from the ``AVG_IN``\n> +register, so the IIO ``realbits`` and ``storagebits`` are unaffected by the\n> +oversampling ratio.\n> +\n> +Valid ratios are 1, 2, 4, 8, 16 and 32. The default is 1 (no averaging).\n> +\n> +.. code-block:: bash\n> +\n> +    # Set oversampling ratio to 16 on channel 0\n> +    echo 16 > /sys/bus/iio/devices/iio:device0/in_voltage0_oversampling_ratio\n> +\n> +When OSR > 1 the effective conversion rate for ``read_raw`` is reduced\n> +accordingly, since the driver waits for 2 × OSR oscillator periods before\n> +reading the result.\n> +\n> +\n> +Reference voltage\n> +=================\n> +\n> +The driver supports two reference configurations, mutually exclusive:\n> +\n> +* **External reference** (``ref-supply``): a voltage between 2.4 V and 5.25 V\n> +  supplied externally. The internal reference buffer is disabled.\n> +* **Buffered internal reference** (``refin-supply``): An internal reference\n> +  buffer is used. The driver enables ``REFBUF_EN`` in the REF_CTRL register\n> +  when this supply is used.\n> +\n> +Exactly one of ``ref-supply`` or ``refin-supply`` must be present in the\n> +device tree.\n> +\n> +The reference voltage determines the full-scale range:\n> +\n> +.. code-block::\n> +\n> +    full-scale = Vref / 2^16  (per LSB)\n> +\n> +\n> +LDO supply\n> +==========\n> +\n> +The chip contains an internal LDO that powers part of the analog front-end.\n> +The LDO input can be driven externally via the ``ldo-in-supply`` regulator. If\n> +that supply is absent, the driver enables the internal LDO path (``LDO_EN``\n> +bit in DEVICE_SETUP).\n> +\n> +\n> +Reset\n> +=====\n> +\n> +The driver supports two reset mechanisms:\n> +\n> +* **Hardware reset** (``reset-gpios`` in device tree): the GPIO is already\n> +  asserted at driver probe by the reset controller framework. The driver waits\n> +  for the required 300 µs reset pulse width and then deasserts.\n> +* **Software reset** (fallback when ``reset-gpios`` is absent): the driver\n> +  writes the software-reset pattern to the SPI_CONFIG_A register.\n> +\n> +\n> +GP pins and interrupts\n> +======================\n> +\n> +The chip exposes up to four general-purpose (GP) pins that can be configured as\n> +interrupt outputs. In CNV Burst Mode (non-offload), one GP pin must be wired to\n\nOr trigger sources.\n\n> +an interrupt-capable SoC input and declared in the device tree using the\n> +``interrupts`` and ``interrupt-names`` properties.\n> +\n> +The ``interrupt-names`` value identifies which GP pin is used (``\"gp0\"``\n> +through ``\"gp3\"``). The driver configures that pin as a DATA_READY output in\n> +the GPIO_MODE register.\n> +\n> +Example device tree fragment::\n> +\n> +    adc@0 {\n> +        compatible = \"adi,ad4692\";\n> +        ...\n> +        interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;\n> +        interrupt-parent = <&gpio0>;\n> +        interrupt-names = \"gp0\";\n> +    };\n> +\n> +\n> +SPI offload support\n> +===================\n> +\n> +When a SPI offload engine (e.g. the AXI SPI Engine) is present, the driver\n> +uses DMA-backed transfers for CPU-independent, high-throughput data capture.\n> +SPI offload is detected automatically at probe via ``devm_spi_offload_get()``;\n> +if no offload hardware is available the driver falls back to the software\n> +triggered-buffer path.\n> +\n> +Two SPI offload sub-modes exist, corresponding to the two operating modes:\n> +\n> +CNV Burst offload\n> +-----------------\n> +\n> +Used when a ``pwms`` property is present and SPI offload is available.\n> +\n> +The PWM drives CNV at the configured rate. On DATA_READY the SPI offload\n> +engine automatically executes a pre-built message that reads all active\n> +``AVG_IN`` registers and streams the data directly to an IIO DMA buffer with\n> +no CPU involvement. A final state-reset transfer re-arms DATA_READY for the\n> +next burst.\n> +\n> +The GP pin used as DATA_READY trigger is supplied by the trigger-source\n> +consumer (via ``#trigger-source-cells``) at buffer enable time; no\n> +``interrupt-names`` entry is required in this path.\n> +\n> +The buffer sampling frequency is controlled by the ``sampling_frequency``\n> +attribute on the IIO buffer (same as the non-offload CNV Burst path).\n> +\n> +Manual offload\n> +--------------\n> +\n> +Used when no ``pwms`` property is present and SPI offload is available.\n> +\n> +A periodic SPI offload trigger controls the conversion rate. On each trigger\n> +period, the SPI engine executes an N+1 transfer message (same pipelined scheme\n\nHow does this work with oversampling?\n\n> +as software Manual Mode) and streams the data directly to the IIO DMA buffer.\n> +\n> +The ``sampling_frequency`` attribute on the IIO buffer controls the trigger\n> +rate (in Hz). The default is the chip's maximum conversion rate.\n> +\n> +\n> +Buffer data format\n> +==================\n> +\n> +The IIO buffer data format (``in_voltageN_type``) depends on the active path:\n> +\n> ++-------------------------+-------------+-------------+-------+\n> +| Path                    | storagebits | realbits    | shift |\n> ++=========================+=============+=============+=======+\n> +| Triggered buffer        | 16          | 16          | 0     |\n> ++-------------------------+-------------+-------------+-------+\n> +| CNV Burst offload (DMA) | 32          | 16          | 0     |\n> ++-------------------------+-------------+-------------+-------+\n> +| Manual offload (DMA)    | 32          | 16          | 16    |\n> ++-------------------------+-------------+-------------+-------+\n> +\n> +In the triggered-buffer path the driver unpacks the 16-bit result in software\n> +before pushing to the buffer, so ``storagebits`` is 16.\n> +\n> +In the DMA offload paths the DMA engine writes 32-bit words directly into the\n> +IIO DMA buffer:\n> +\n> +* **CNV Burst offload**: the SPI engine reads AVG_IN registers with a 2-byte\n> +  address phase followed by a 2-byte data phase; the 16-bit result lands in\n> +  the lower half of the 32-bit word (``shift=0``).\n> +* **Manual offload**: each 32-bit SPI word carries the channel byte in the\n> +  first byte; the 16-bit result is returned in the upper half of the 32-bit\n\nI would expect the \"first\" byte to be in the \"upper half\" of the 32-bits as\nwell. This layout could be explained better.\n\nAlso, since extra data has to be read in this mode, does this affect the max\nconversion rate?\n\n> +  word (``shift=16``).\n> +\n> +The ``in_voltageN_type`` sysfs attribute reflects the active scan type.\n> +\n> +\n> +Unimplemented features\n> +======================\n> +\n> +* GPIO controller functionality of the GP pins\n> +* Clamp status and overrange events\n> +* Raw accumulator (ACC_IN) and accumulator status registers\n> +* ADC_BUSY and overrun status interrupts","headers":{"Return-Path":"\n <linux-pwm+bounces-8475-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-pwm@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=I+7nU80C;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.232.135.74; helo=sto.lore.kernel.org;\n envelope-from=linux-pwm+bounces-8475-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.b=\"I+7nU80C\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=74.125.82.48","smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=baylibre.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=baylibre.com"],"Received":["from sto.lore.kernel.org (sto.lore.kernel.org [172.232.135.74])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fp05H4Dvlz1yG2\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 05 Apr 2026 02:34:43 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id 619AC3005982\n\tfor <incoming@patchwork.ozlabs.org>; Sat,  4 Apr 2026 15:34:40 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 003A631960A;\n\tSat,  4 Apr 2026 15:34:37 +0000 (UTC)","from mail-dl1-f48.google.com (mail-dl1-f48.google.com\n [74.125.82.48])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 3B87E31714B\n\tfor <linux-pwm@vger.kernel.org>; Sat,  4 Apr 2026 15:34:33 +0000 (UTC)","by mail-dl1-f48.google.com with SMTP id\n a92af1059eb24-1279eced0b9so3106489c88.0\n        for <linux-pwm@vger.kernel.org>; Sat, 04 Apr 2026 08:34:33 -0700 (PDT)","from ?IPV6:2600:8803:e7e4:500:e14e:bcc6:3f95:26eb?\n ([2600:8803:e7e4:500:e14e:bcc6:3f95:26eb])\n        by smtp.gmail.com with ESMTPSA id\n a92af1059eb24-12bedd4e623sm7107228c88.3.2026.04.04.08.34.30\n        (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n        Sat, 04 Apr 2026 08:34:31 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775316876; cv=none;\n b=dkOrqbnfzND/uZ98rIUylenAQidmOrA4qZ/4JZQ5F1qdXh8OJwelHSzpsb8zjJartRHvgUXP4Bchk7l/toxYNs0WJS2IxVMn+B6sWBUX8lIZm0ORybKBqrJyrvmL+C43DF9PuoPfRwtK1a1HCmbkhMbitSyZdy/nPtukFeuGaQM=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775316876; c=relaxed/simple;\n\tbh=G6SnkhOr1As6FBry8Y9C+66hbGOqOMztZoUWLj98G3U=;\n\th=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From:\n\t In-Reply-To:Content-Type;\n b=LzwbM3ZQgwWR2kc0RMNjBFEt+9HMYE7llPKjTOYKo+ltd0oZNNgQHtBBpOnkPXZUSx6QF6oHAFlGUIuadUmxhgZJw99rj1af4Rj1f9NGn2+1hN/65DMsNQo1nu/0DxCCHAuiXq1dUyxxJFKczH0iHjLDNacHkxM6i/NMjICx/nM=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=baylibre.com;\n spf=pass smtp.mailfrom=baylibre.com;\n dkim=pass (2048-bit key) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.b=I+7nU80C;\n arc=none smtp.client-ip=74.125.82.48","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=baylibre-com.20251104.gappssmtp.com; s=20251104; t=1775316872;\n x=1775921672; darn=vger.kernel.org;\n        h=content-transfer-encoding:in-reply-to:from:content-language\n         :references:cc:to:subject:user-agent:mime-version:date:message-id\n         :from:to:cc:subject:date:message-id:reply-to;\n        bh=tNIG7oIixMtjXoOLBTF4Km9l7YWSSk5sSBtCTRK9+1s=;\n        b=I+7nU80CT2MQ5/bERnsTkyLfdhVcN2ESCof2BHYIjoDDHBjVa8UNBZAxjcQK6a++9K\n         8lBhyNrqkuOr/Jusy3y+iu8uiXkGEGrkSUoquTTzEBAQsFkL593viXszR1nDlRJ+y+5/\n         t5fVz/2Hjyff6rTcGR2/BjRKaMAe+F95LaGSYn3bZEh/V0iGLWgKPwGyLibnc5oXdFxb\n         Qa4HFHqUchQpp94JSFnf6LLjUuviztpedFWKEzHJxgDQWrhuGZwhr6LnF8XzS0TZ8YXy\n         XoiYJphm5pQvFOS1ehFJ8iEg/sEIRWGHK8gKNksdbCh6YD4sOv+Mr333XASrzPKiQMxt\n         fxHg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1775316872; x=1775921672;\n        h=content-transfer-encoding:in-reply-to:from:content-language\n         :references:cc:to:subject:user-agent:mime-version:date:message-id\n         :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n         :reply-to;\n        bh=tNIG7oIixMtjXoOLBTF4Km9l7YWSSk5sSBtCTRK9+1s=;\n        b=kqePrSmNFadrMzy39orecqti6a+illXRsD6Lhsir48h/z4jPiz5W35QUcWmqwlt+pD\n         wa0VVf/gnYmWakhh/9sYVVs6e2R2zOa42DTes7fzm6bhAXgH6U6rhWcyDKyBVR/z5ctA\n         i2xVSR4zRVaZfcMu98VULzQJ3IiJ4dGY22KIaUMhmCpldYmIHpjQyD2QlcUtriIv1n5P\n         qgTZep5lAun6YhLLBR70U+QVv/vKGG2NYvy5ikwdWEaOFqvn0WS9ZebZ4qyGMJtS9VGI\n         32iqI/8CeD7LxLw8AJdTVCjTj9lLujSPf0+fUyihHCt3Hf73TygHz1r80P9xxuz5gokG\n         P4iQ==","X-Forwarded-Encrypted":"i=1;\n AJvYcCXW6OeRRZHb7apCdHmWNxNs/2o7T9X5DxHtAiZD9bxPHB3nYLkcyNA/Y5+pSlJnOSwQNWJYGMPWBz8=@vger.kernel.org","X-Gm-Message-State":"AOJu0YyicsBXlASjVoKb6VcN2YpBwn1Gn+zjJ6a9GTOmPy6dw/aHLyQ4\n\tXLFi7IeFhYjnSGU/0NKWTpe4e2HqFk7X/RsvMGh/+f+wA3QD20fzCx/XPysYWcnCKn0=","X-Gm-Gg":"AeBDievLVFuHaT+YFKiCgz3UQ5fWD1uLKGAzGl8SsqC/pX6fXodjr+vM6VOoQHznoAz\n\tR94A5dgZDS/+78KVahzHozpZcgcHI8PpblnjfK5ZP8d8ShGAYHixEd2IhSrIBW4mY7ck0Ldldkx\n\tbH1A5eJmqnXeu+qYA7T+RD/N2JIj38kLZK4cksda+a64yo/IDHQ965IZa0tNVoYjLOnM4rpt3Wt\n\t0sBfqToSP2i82US4mLmxQTmGETYMqSNiGu/KtQsV9abM6PCNrFpEpiW538ep95Pk73sw0EnLhio\n\tjjwgYJsXeM8HlmYi3Z8wRzTBD7/35ezNzfkq+DX+sYJDE+glllfVVEuIbGeytQ/4qxdHW+kVvFW\n\t3sgao9BygeHu8LMhkSJ190fmBGt8bk3KqfakxJ9hHiNl/ZMsvcxMIhtmbUefluUrVU2IzPr3I2z\n\tOnPl3K+uGgjJKeEz/ecoDtZq8hvnuGECJ9FWB9ViHHvuTzhtHSWpUIKk5oudpCvv28gxvkVdV2b\n\tQ==","X-Received":"by 2002:a05:7022:e09:b0:11a:342e:8a98 with SMTP id\n a92af1059eb24-12bfb63267fmr3266514c88.0.1775316872075;\n        Sat, 04 Apr 2026 08:34:32 -0700 (PDT)","Message-ID":"<1d0d41c8-7867-4459-a91a-a2c6774b1885@baylibre.com>","Date":"Sat, 4 Apr 2026 10:34:29 -0500","Precedence":"bulk","X-Mailing-List":"linux-pwm@vger.kernel.org","List-Id":"<linux-pwm.vger.kernel.org>","List-Subscribe":"<mailto:linux-pwm+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-pwm+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v6 4/4] iio: adc: ad4691: add SPI offload support","To":"radu.sabau@analog.com, Lars-Peter Clausen <lars@metafoo.de>,\n Michael Hennerich <Michael.Hennerich@analog.com>,\n Jonathan Cameron <jic23@kernel.org>,\n =?utf-8?q?Nuno_S=C3=A1?= <nuno.sa@analog.com>,\n Andy Shevchenko <andy@kernel.org>, Rob Herring <robh@kernel.org>,\n Krzysztof Kozlowski <krzk+dt@kernel.org>, Conor Dooley <conor+dt@kernel.org>,\n =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= <ukleinek@kernel.org>,\n Liam Girdwood <lgirdwood@gmail.com>, Mark Brown <broonie@kernel.org>,\n Linus Walleij <linusw@kernel.org>, Bartosz Golaszewski <brgl@kernel.org>,\n Philipp Zabel <p.zabel@pengutronix.de>, Jonathan Corbet <corbet@lwn.net>,\n Shuah Khan <skhan@linuxfoundation.org>","Cc":"linux-iio@vger.kernel.org, devicetree@vger.kernel.org,\n linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,\n linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org","References":"\n <20260403-ad4692-multichannel-sar-adc-driver-v6-0-fa2a01a57c4e@analog.com>\n <20260403-ad4692-multichannel-sar-adc-driver-v6-4-fa2a01a57c4e@analog.com>","Content-Language":"en-US","From":"David Lechner <dlechner@baylibre.com>","In-Reply-To":"\n <20260403-ad4692-multichannel-sar-adc-driver-v6-4-fa2a01a57c4e@analog.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit"}},{"id":3673467,"web_url":"http://patchwork.ozlabs.org/comment/3673467/","msgid":"<22b44acb-bfb5-4b97-8fa2-aeb4aec704c2@baylibre.com>","list_archive_url":null,"date":"2026-04-04T15:57:19","subject":"Re: [PATCH v6 4/4] iio: adc: ad4691: add SPI offload support","submitter":{"id":87228,"url":"http://patchwork.ozlabs.org/api/people/87228/","name":"David Lechner","email":"dlechner@baylibre.com"},"content":"On 4/3/26 6:03 AM, Radu Sabau via B4 Relay wrote:\n> From: Radu Sabau <radu.sabau@analog.com>\n> \n> Add SPI offload support to enable DMA-based, CPU-independent data\n> acquisition using the SPI Engine offload framework.\n> \n> When an SPI offload is available (devm_spi_offload_get() succeeds),\n> the driver registers a DMA engine IIO buffer and uses dedicated buffer\n> setup operations. If no offload is available the existing software\n> triggered buffer path is used unchanged.\n> \n> Both CNV Burst Mode and Manual Mode support offload, but use different\n> trigger mechanisms:\n> \n> CNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY\n> signal on the GP pin specified by the trigger-source consumer reference\n> in the device tree (one cell = GP pin number 0-3). For this mode the\n> driver acts as both an SPI offload consumer (DMA RX stream, message\n> optimization) and a trigger source provider: it registers the\n> GP/DATA_READY output via devm_spi_offload_trigger_register() so the\n> offload framework can match the '#trigger-source-cells' phandle and\n> automatically fire the SPI Engine DMA transfer at end-of-conversion.\n> \n> Manual Mode: the SPI Engine is triggered by a periodic trigger at\n> the configured sampling frequency. The pre-built SPI message uses\n> the pipelined CNV-on-CS protocol: N+1 4-byte transfers are issued\n> for N active channels (the first result is discarded as garbage from\n> the pipeline flush) and the remaining N results are captured by DMA.\n> \n> All offload transfers use 32-bit frames (bits_per_word=32, len=4) for\n> DMA word alignment. This patch promotes the channel scan_type from\n> storagebits=16 (triggered-buffer path) to storagebits=32 to match the\n> DMA word size; the triggered-buffer paths are updated to the same layout\n> for consistency. CNV Burst Mode channel data arrives in the lower 16\n> bits of the 32-bit word (shift=0); Manual Mode data arrives in the upper\n> 16 bits (shift=16), matching the 4-byte SPI transfer layout\n> [data_hi, data_lo, 0, 0]. A separate ad4691_manual_channels[] array\n> encodes the shift=16 scan type for manual mode.\n> \n> Add driver documentation under Documentation/iio/ad4691.rst covering\n> operating modes, oversampling, reference voltage, SPI offload paths,\n> and buffer data layout; register in MAINTAINERS and index.rst\n> \n> Kconfig gains a dependency on IIO_BUFFER_DMAENGINE.\n> \n> Signed-off-by: Radu Sabau <radu.sabau@analog.com>\n> ---\n>  Documentation/iio/ad4691.rst | 259 ++++++++++++++++++++++++++\n>  Documentation/iio/index.rst  |   1 +\n>  MAINTAINERS                  |   1 +\n>  drivers/iio/adc/Kconfig      |   1 +\n>  drivers/iio/adc/ad4691.c     | 422 ++++++++++++++++++++++++++++++++++++++++++-\n>  5 files changed, 676 insertions(+), 8 deletions(-)\n> \n\n...\n\n>  ANALOG DEVICES INC AD4695 DRIVER\n> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig\n> index d498f16c0816..93f090e9a562 100644\n> --- a/drivers/iio/adc/Kconfig\n> +++ b/drivers/iio/adc/Kconfig\n> @@ -144,6 +144,7 @@ config AD4691\n>  \tdepends on SPI\n>  \tselect IIO_BUFFER\n>  \tselect IIO_TRIGGERED_BUFFER\n> +\tselect IIO_BUFFER_DMAENGINE\n>  \tselect REGMAP\n>  \thelp\n>  \t  Say yes here to build support for Analog Devices AD4691 Family MuxSAR\n> diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c\n> index f2a7273e43b9..cc2138e47feb 100644\n> --- a/drivers/iio/adc/ad4691.c\n> +++ b/drivers/iio/adc/ad4691.c\n> @@ -11,6 +11,7 @@\n>  #include <linux/delay.h>\n>  #include <linux/dev_printk.h>\n>  #include <linux/device/devres.h>\n> +#include <linux/dmaengine.h>\n>  #include <linux/err.h>\n>  #include <linux/interrupt.h>\n>  #include <linux/math.h>\n> @@ -22,10 +23,14 @@\n>  #include <linux/regulator/consumer.h>\n>  #include <linux/reset.h>\n>  #include <linux/spi/spi.h>\n> +#include <linux/spi/offload/consumer.h>\n> +#include <linux/spi/offload/provider.h>\n>  #include <linux/units.h>\n>  #include <linux/unaligned.h>\n>  \n>  #include <linux/iio/buffer.h>\n> +#include <linux/iio/buffer-dma.h>\n> +#include <linux/iio/buffer-dmaengine.h>\n>  #include <linux/iio/iio.h>\n>  #include <linux/iio/sysfs.h>\n>  #include <linux/iio/trigger.h>\n> @@ -40,6 +45,7 @@\n>  #define AD4691_VREF_4P096_uV_MAX\t\t4500000\n>  \n>  #define AD4691_CNV_DUTY_CYCLE_NS\t\t380\n> +#define AD4691_CNV_HIGH_TIME_NS\t\t\t430\n>  \n>  #define AD4691_SPI_CONFIG_A_REG\t\t\t0x000\n>  #define AD4691_SW_RESET\t\t\t\t(BIT(7) | BIT(0))\n> @@ -92,6 +98,8 @@\n>  #define AD4691_ACC_IN(n)\t\t\t(0x252 + (3 * (n)))\n>  #define AD4691_ACC_STS_DATA(n)\t\t\t(0x283 + (4 * (n)))\n>  \n> +#define AD4691_OFFLOAD_BITS_PER_WORD\t\t32\n> +\n>  static const char * const ad4691_supplies[] = { \"avdd\", \"vio\" };\n>  \n>  enum ad4691_ref_ctrl {\n> @@ -109,6 +117,31 @@ struct ad4691_chip_info {\n>  \tunsigned int max_rate;\n>  };\n>  \n> +enum {\n> +\tAD4691_SCAN_TYPE_NORMAL,         /* triggered buffer:  storagebits=16, shift=0  */\n> +\tAD4691_SCAN_TYPE_OFFLOAD_CNV,    /* CNV burst offload: storagebits=32, shift=0  */\n> +\tAD4691_SCAN_TYPE_OFFLOAD_MANUAL, /* manual offload:    storagebits=32, shift=16 */\n> +};\n> +\n> +static const struct iio_scan_type ad4691_scan_types[] = {\n> +\t[AD4691_SCAN_TYPE_NORMAL] = {\n> +\t\t.sign = 'u',\n> +\t\t.realbits = 16,\n> +\t\t.storagebits = 16,\n> +\t},\n> +\t[AD4691_SCAN_TYPE_OFFLOAD_CNV] = {\n> +\t\t.sign = 'u',\n> +\t\t.realbits = 16,\n> +\t\t.storagebits = 32,\n> +\t},\n> +\t[AD4691_SCAN_TYPE_OFFLOAD_MANUAL] = {\n> +\t\t.sign = 'u',\n> +\t\t.realbits = 16,\n> +\t\t.storagebits = 32,\n> +\t\t.shift = 16,\n> +\t},\n> +};\n> +\n>  #define AD4691_CHANNEL(ch)\t\t\t\t\t\t\\\n>  \t{\t\t\t\t\t\t\t\t\\\n>  \t\t.type = IIO_VOLTAGE,\t\t\t\t\t\\\n> @@ -122,11 +155,9 @@ struct ad4691_chip_info {\n>  \t\t.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),\t\\\n>  \t\t.channel = ch,\t\t\t\t\t\t\\\n>  \t\t.scan_index = ch,\t\t\t\t\t\\\n> -\t\t.scan_type = {\t\t\t\t\t\t\\\n> -\t\t\t.sign = 'u',\t\t\t\t\t\\\n> -\t\t\t.realbits = 16,\t\t\t\t\t\\\n> -\t\t\t.storagebits = 16,\t\t\t\t\\\n> -\t\t},\t\t\t\t\t\t\t\\\n> +\t\t.has_ext_scan_type = 1,\t\t\t\t\t\\\n> +\t\t.ext_scan_type = ad4691_scan_types,\t\t\t\\\n> +\t\t.num_ext_scan_type = ARRAY_SIZE(ad4691_scan_types),\t\\\n\nUsually, we just make two separte ad4691_chip_info structs for offload\nvs. not offload.\n\next_scan_type is generally only used when the scan type can change\ndynamically after probe.\n\n>  \t}\n>  \n>  static const struct iio_chan_spec ad4691_channels[] = {\n> @@ -221,6 +252,17 @@ static const struct ad4691_chip_info ad4694_chip_info = {\n>  \t.max_rate = 1 * HZ_PER_MHZ,\n>  };\n>  \n> +struct ad4691_offload_state {\n> +\tstruct spi_offload *spi;\n> +\tstruct spi_offload_trigger *trigger;\n> +\tu64 trigger_hz;\n> +\tstruct spi_message msg;\n> +\t/* Max 16 channel xfers + 1 state-reset or NOOP */\n> +\tstruct spi_transfer xfer[17];\n> +\tu8 tx_cmd[17][4];\n> +\tu8 tx_reset[4];\n> +};\n> +\n>  struct ad4691_state {\n>  \tconst struct ad4691_chip_info *info;\n>  \tstruct regmap *regmap;\n> @@ -251,6 +293,8 @@ struct ad4691_state {\n>  \tstruct spi_transfer *scan_xfers;\n>  \t__be16 *scan_tx;\n>  \t__be16 *scan_rx;\n> +\t/* NULL when no SPI offload hardware is present */\n> +\tstruct ad4691_offload_state *offload;\n>  \t/* Scan buffer: one slot per channel plus timestamp */\n>  \tstruct {\n>  \t\tu16 vals[16];\n> @@ -273,6 +317,46 @@ static int ad4691_gpio_setup(struct ad4691_state *st, unsigned int gp_num)\n>  \t\t\t\t  AD4691_GP_MODE_DATA_READY << shift);\n>  }\n>  \n> +static const struct spi_offload_config ad4691_offload_config = {\n> +\t.capability_flags = SPI_OFFLOAD_CAP_TRIGGER |\n> +\t\t\t    SPI_OFFLOAD_CAP_RX_STREAM_DMA,\n> +};\n> +\n> +static bool ad4691_offload_trigger_match(struct spi_offload_trigger *trigger,\n> +\t\t\t\t\t enum spi_offload_trigger_type type,\n> +\t\t\t\t\t u64 *args, u32 nargs)\n> +{\n> +\treturn type == SPI_OFFLOAD_TRIGGER_DATA_READY &&\n> +\t       nargs == 1 && args[0] <= 3;\n> +}\n> +\n> +static int ad4691_offload_trigger_request(struct spi_offload_trigger *trigger,\n> +\t\t\t\t\t  enum spi_offload_trigger_type type,\n> +\t\t\t\t\t  u64 *args, u32 nargs)\n> +{\n> +\tstruct ad4691_state *st = spi_offload_trigger_get_priv(trigger);\n> +\n> +\tif (nargs != 1)\n> +\t\treturn -EINVAL;\n> +\n> +\treturn ad4691_gpio_setup(st, (unsigned int)args[0]);\n\nShould be fine to leave out the cast here.\n\n> +}\n> +\n> +static int ad4691_offload_trigger_validate(struct spi_offload_trigger *trigger,\n> +\t\t\t\t\t   struct spi_offload_trigger_config *config)\n> +{\n> +\tif (config->type != SPI_OFFLOAD_TRIGGER_DATA_READY)\n> +\t\treturn -EINVAL;\n> +\n> +\treturn 0;\n> +}\n> +\n> +static const struct spi_offload_trigger_ops ad4691_offload_trigger_ops = {\n> +\t.match    = ad4691_offload_trigger_match,\n> +\t.request  = ad4691_offload_trigger_request,\n> +\t.validate = ad4691_offload_trigger_validate,\n> +};\n> +\n>  static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)\n>  {\n>  \tstruct spi_device *spi = context;\n> @@ -553,10 +637,17 @@ static int ad4691_read_raw(struct iio_dev *indio_dev,\n>  \tcase IIO_CHAN_INFO_OVERSAMPLING_RATIO:\n>  \t\t*val = st->osr[chan->scan_index];\n>  \t\treturn IIO_VAL_INT;\n> -\tcase IIO_CHAN_INFO_SCALE:\n> +\tcase IIO_CHAN_INFO_SCALE: {\n> +\t\tconst struct iio_scan_type *scan_type;\n> +\n> +\t\tscan_type = iio_get_current_scan_type(indio_dev, chan);\n> +\t\tif (IS_ERR(scan_type))\n> +\t\t\treturn PTR_ERR(scan_type);\n> +\n>  \t\t*val = st->vref_uV / (MICRO / MILLI);\n> -\t\t*val2 = chan->scan_type.realbits;\n> +\t\t*val2 = scan_type->realbits;\n>  \t\treturn IIO_VAL_FRACTIONAL_LOG2;\n> +\t}\n>  \tdefault:\n>  \t\treturn -EINVAL;\n>  \t}\n> @@ -856,6 +947,213 @@ static const struct iio_buffer_setup_ops ad4691_cnv_burst_buffer_setup_ops = {\n>  \t.postdisable = &ad4691_cnv_burst_buffer_postdisable,\n>  };\n>  \n> +static int ad4691_manual_offload_buffer_postenable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tstruct ad4691_offload_state *offload = st->offload;\n> +\tstruct device *dev = regmap_get_device(st->regmap);\n> +\tstruct spi_device *spi = to_spi_device(dev);\n> +\tstruct spi_offload_trigger_config config = {\n> +\t\t.type = SPI_OFFLOAD_TRIGGER_PERIODIC,\n> +\t};\n> +\tunsigned int bit, k;\n> +\tint ret;\n> +\n> +\tret = ad4691_enter_conversion_mode(st);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tmemset(offload->xfer, 0, sizeof(offload->xfer));\n> +\n> +\t/*\n> +\t * N+1 transfers for N channels. Each CS-low period triggers\n> +\t * a conversion AND returns the previous result (pipelined).\n> +\t *   TX: [AD4691_ADC_CHAN(n), 0x00, 0x00, 0x00]\n> +\t *   RX: [data_hi, data_lo, 0x00, 0x00]   (shift=16)\n> +\t * Transfer 0 RX is garbage; transfers 1..N carry real data.\n> +\t */\n> +\tk = 0;\n> +\tiio_for_each_active_channel(indio_dev, bit) {\n> +\t\toffload->tx_cmd[k][0] = AD4691_ADC_CHAN(bit);\n> +\t\toffload->xfer[k].tx_buf = offload->tx_cmd[k];\n> +\t\toffload->xfer[k].len = sizeof(offload->tx_cmd[k]);\n> +\t\toffload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n> +\t\toffload->xfer[k].cs_change = 1;\n> +\t\toffload->xfer[k].cs_change_delay.value = AD4691_CNV_HIGH_TIME_NS;\n> +\t\toffload->xfer[k].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;\n> +\t\t/* First transfer RX is garbage — skip it. */\n> +\t\tif (k > 0)\n> +\t\t\toffload->xfer[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;\n> +\t\tk++;\n> +\t}\n> +\n> +\t/* Final NOOP to flush pipeline and capture last channel. */\n> +\toffload->tx_cmd[k][0] = AD4691_NOOP;\n> +\toffload->xfer[k].tx_buf = offload->tx_cmd[k];\n> +\toffload->xfer[k].len = sizeof(offload->tx_cmd[k]);\n> +\toffload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n> +\toffload->xfer[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;\n> +\tk++;\n> +\n> +\tspi_message_init_with_transfers(&offload->msg, offload->xfer, k);\n> +\toffload->msg.offload = offload->spi;\n> +\n> +\tret = spi_optimize_message(spi, &offload->msg);\n> +\tif (ret)\n> +\t\tgoto err_exit_conversion;\n> +\n> +\tconfig.periodic.frequency_hz = offload->trigger_hz;\n> +\tret = spi_offload_trigger_enable(offload->spi, offload->trigger, &config);\n> +\tif (ret)\n> +\t\tgoto err_unoptimize;\n> +\n> +\treturn 0;\n> +\n> +err_unoptimize:\n> +\tspi_unoptimize_message(&offload->msg);\n> +err_exit_conversion:\n> +\tad4691_exit_conversion_mode(st);\n> +\treturn ret;\n> +}\n> +\n> +static int ad4691_manual_offload_buffer_predisable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tstruct ad4691_offload_state *offload = st->offload;\n> +\n> +\tspi_offload_trigger_disable(offload->spi, offload->trigger);\n> +\tspi_unoptimize_message(&offload->msg);\n> +\n> +\treturn ad4691_exit_conversion_mode(st);\n> +}\n> +\n> +static const struct iio_buffer_setup_ops ad4691_manual_offload_buffer_setup_ops = {\n> +\t.postenable = &ad4691_manual_offload_buffer_postenable,\n> +\t.predisable = &ad4691_manual_offload_buffer_predisable,\n> +};\n> +\n> +static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tstruct ad4691_offload_state *offload = st->offload;\n> +\tstruct device *dev = regmap_get_device(st->regmap);\n> +\tstruct spi_device *spi = to_spi_device(dev);\n> +\tstruct spi_offload_trigger_config config = {\n> +\t\t.type = SPI_OFFLOAD_TRIGGER_DATA_READY,\n> +\t};\n> +\tunsigned int n_active = bitmap_weight(indio_dev->active_scan_mask,\n> +\t\t\t\t\t      iio_get_masklength(indio_dev));\n> +\tunsigned int bit, k;\n> +\tint ret;\n> +\n> +\tret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,\n> +\t\t\t   bitmap_read(indio_dev->active_scan_mask, 0,\n> +\t\t\t\t       iio_get_masklength(indio_dev)));\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,\n> +\t\t\t   ~bitmap_read(indio_dev->active_scan_mask, 0,\n> +\t\t\t\tiio_get_masklength(indio_dev)) & GENMASK(15, 0));\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tiio_for_each_active_channel(indio_dev, bit) {\n> +\t\tret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(bit),\n> +\t\t\t\t   st->osr[bit]);\n> +\t\tif (ret)\n> +\t\t\treturn ret;\n> +\t}\n> +\n> +\tret = ad4691_enter_conversion_mode(st);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tmemset(offload->xfer, 0, sizeof(offload->xfer));\n> +\n> +\t/*\n> +\t * N transfers to read N AVG_IN registers plus one state-reset\n> +\t * transfer (no RX) to re-arm DATA_READY.\n> +\t *   TX: [reg_hi | 0x80, reg_lo, 0x00, 0x00]\n> +\t *   RX: [0x00, 0x00, data_hi, data_lo]   (shift=0)\n> +\t */\n> +\tk = 0;\n> +\tiio_for_each_active_channel(indio_dev, bit) {\n> +\t\tunsigned int reg = AD4691_AVG_IN(bit);\n> +\n> +\t\toffload->tx_cmd[k][0] = (reg >> 8) | 0x80;\n> +\t\toffload->tx_cmd[k][1] = reg & 0xFF;\n\nCan we use put_unaligned_be16()?\n\n> +\t\toffload->xfer[k].tx_buf = offload->tx_cmd[k];\n> +\t\toffload->xfer[k].len = sizeof(offload->tx_cmd[k]);\n> +\t\toffload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n> +\t\toffload->xfer[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;\n> +\t\tif (k < n_active - 1)\n> +\t\t\toffload->xfer[k].cs_change = 1;\n> +\t\tk++;\n> +\t}\n> +\n> +\t/* State reset to re-arm DATA_READY for the next scan. */\n> +\toffload->tx_reset[0] = AD4691_STATE_RESET_REG >> 8;\n> +\toffload->tx_reset[1] = AD4691_STATE_RESET_REG & 0xFF;\n\nditto.\n\n> +\toffload->tx_reset[2] = AD4691_STATE_RESET_ALL;\n> +\toffload->xfer[k].tx_buf = offload->tx_reset;\n> +\toffload->xfer[k].len = sizeof(offload->tx_reset);\n> +\toffload->xfer[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n> +\tk++;\n> +\n> +\tspi_message_init_with_transfers(&offload->msg, offload->xfer, k);\n> +\toffload->msg.offload = offload->spi;\n> +\n> +\tret = spi_optimize_message(spi, &offload->msg);\n> +\tif (ret)\n> +\t\tgoto err_exit_conversion;\n> +\n> +\tret = ad4691_sampling_enable(st, true);\n> +\tif (ret)\n> +\t\tgoto err_unoptimize;\n> +\n> +\tret = spi_offload_trigger_enable(offload->spi, offload->trigger, &config);\n> +\tif (ret)\n> +\t\tgoto err_sampling_disable;\n> +\n> +\treturn 0;\n> +\n> +err_sampling_disable:\n> +\tad4691_sampling_enable(st, false);\n> +err_unoptimize:\n> +\tspi_unoptimize_message(&offload->msg);\n> +err_exit_conversion:\n> +\tad4691_exit_conversion_mode(st);\n> +\treturn ret;\n> +}\n> +\n> +static int ad4691_cnv_burst_offload_buffer_predisable(struct iio_dev *indio_dev)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\tstruct ad4691_offload_state *offload = st->offload;\n> +\tint ret;\n> +\n> +\tspi_offload_trigger_disable(offload->spi, offload->trigger);\n> +\n> +\tret = ad4691_sampling_enable(st, false);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,\n> +\t\t\t   AD4691_SEQ_ALL_CHANNELS_OFF);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tspi_unoptimize_message(&offload->msg);\n> +\n> +\treturn ad4691_exit_conversion_mode(st);\n> +}\n> +\n> +static const struct iio_buffer_setup_ops ad4691_cnv_burst_offload_buffer_setup_ops = {\n> +\t.postenable = &ad4691_cnv_burst_offload_buffer_postenable,\n> +\t.predisable = &ad4691_cnv_burst_offload_buffer_predisable,\n> +};\n> +\n>  static ssize_t sampling_frequency_show(struct device *dev,\n>  \t\t\t\t       struct device_attribute *attr,\n>  \t\t\t\t       char *buf)\n> @@ -863,6 +1161,9 @@ static ssize_t sampling_frequency_show(struct device *dev,\n>  \tstruct iio_dev *indio_dev = dev_to_iio_dev(dev);\n>  \tstruct ad4691_state *st = iio_priv(indio_dev);\n>  \n> +\tif (st->manual_mode && st->offload)\n> +\t\treturn sysfs_emit(buf, \"%llu\\n\", st->offload->trigger_hz);\n> +\n>  \treturn sysfs_emit(buf, \"%u\\n\", (u32)(NSEC_PER_SEC / st->cnv_period_ns));\n>  }\n>  \n> @@ -883,6 +1184,20 @@ static ssize_t sampling_frequency_store(struct device *dev,\n>  \tif (iio_buffer_enabled(indio_dev))\n>  \t\treturn -EBUSY;\n>  \n> +\tif (st->manual_mode && st->offload) {\n> +\t\tstruct spi_offload_trigger_config config = {\n> +\t\t\t.type = SPI_OFFLOAD_TRIGGER_PERIODIC,\n> +\t\t\t.periodic = { .frequency_hz = freq },\n> +\t\t};\n\nSame comment as other patches. This needs to account for oversampling ratio.\n\n> +\n> +\t\tret = spi_offload_trigger_validate(st->offload->trigger, &config);\n> +\t\tif (ret)\n> +\t\t\treturn ret;\n> +\n> +\t\tst->offload->trigger_hz = config.periodic.frequency_hz;\n> +\t\treturn len;\n> +\t}\n> +\n>  \tret = ad4691_set_pwm_freq(st, freq);\n>  \tif (ret)\n>  \t\treturn ret;\n> @@ -968,10 +1283,23 @@ static irqreturn_t ad4691_trigger_handler(int irq, void *p)\n>  \treturn IRQ_HANDLED;\n>  }\n>  \n> +static int ad4691_get_current_scan_type(const struct iio_dev *indio_dev,\n> +\t\t\t\t\t const struct iio_chan_spec *chan)\n> +{\n> +\tstruct ad4691_state *st = iio_priv(indio_dev);\n> +\n> +\tif (!st->offload)\n> +\t\treturn AD4691_SCAN_TYPE_NORMAL;\n> +\tif (st->manual_mode)\n> +\t\treturn AD4691_SCAN_TYPE_OFFLOAD_MANUAL;\n> +\treturn AD4691_SCAN_TYPE_OFFLOAD_CNV;\n> +}\n> +\n>  static const struct iio_info ad4691_info = {\n>  \t.read_raw = &ad4691_read_raw,\n>  \t.write_raw = &ad4691_write_raw,\n>  \t.read_avail = &ad4691_read_avail,\n> +\t.get_current_scan_type = &ad4691_get_current_scan_type,\n>  \t.debugfs_reg_access = &ad4691_reg_access,\n>  };\n>  \n> @@ -1195,9 +1523,75 @@ static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,\n>  \t\t\t\t\t       &ad4691_manual_buffer_setup_ops);\n>  }\n>  \n> +static int ad4691_setup_offload(struct iio_dev *indio_dev,\n> +\t\t\t\tstruct ad4691_state *st,\n> +\t\t\t\tstruct spi_offload *spi_offload)\n> +{\n> +\tstruct device *dev = regmap_get_device(st->regmap);\n> +\tstruct ad4691_offload_state *offload;\n> +\tstruct dma_chan *rx_dma;\n> +\tint ret;\n> +\n> +\toffload = devm_kzalloc(dev, sizeof(*offload), GFP_KERNEL);\n> +\tif (!offload)\n> +\t\treturn -ENOMEM;\n> +\n> +\toffload->spi = spi_offload;\n> +\tst->offload = offload;\n> +\n> +\tif (st->manual_mode) {\n> +\t\toffload->trigger =\n> +\t\t\tdevm_spi_offload_trigger_get(dev, offload->spi,\n> +\t\t\t\t\t\t     SPI_OFFLOAD_TRIGGER_PERIODIC);\n> +\t\tif (IS_ERR(offload->trigger))\n> +\t\t\treturn dev_err_probe(dev, PTR_ERR(offload->trigger),\n> +\t\t\t\t\t     \"Failed to get periodic offload trigger\\n\");\n> +\n> +\t\toffload->trigger_hz = st->info->max_rate;\n\nI think I mentioned this elsewhere, but can we really get max_rate in manual mode\ndue to the extra SPI overhead? Probably safer to start with a lower rate.\n\n> +\t} else {\n> +\t\tstruct spi_offload_trigger_info trigger_info = {\n> +\t\t\t.fwnode = dev_fwnode(dev),\n> +\t\t\t.ops    = &ad4691_offload_trigger_ops,\n> +\t\t\t.priv   = st,\n> +\t\t};\n> +\n> +\t\tret = devm_spi_offload_trigger_register(dev, &trigger_info);\n> +\t\tif (ret)\n> +\t\t\treturn dev_err_probe(dev, ret,\n> +\t\t\t\t\t     \"Failed to register offload trigger\\n\");\n> +\n> +\t\toffload->trigger =\n> +\t\t\tdevm_spi_offload_trigger_get(dev, offload->spi,\n> +\t\t\t\t\t\t     SPI_OFFLOAD_TRIGGER_DATA_READY);\n> +\t\tif (IS_ERR(offload->trigger))\n> +\t\t\treturn dev_err_probe(dev, PTR_ERR(offload->trigger),\n> +\t\t\t\t\t     \"Failed to get DATA_READY offload trigger\\n\");\n> +\t}\n> +\n> +\trx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, offload->spi);\n> +\tif (IS_ERR(rx_dma))\n> +\t\treturn dev_err_probe(dev, PTR_ERR(rx_dma),\n> +\t\t\t\t     \"Failed to get offload RX DMA channel\\n\");\n> +\n> +\tif (st->manual_mode)\n> +\t\tindio_dev->setup_ops = &ad4691_manual_offload_buffer_setup_ops;\n> +\telse\n> +\t\tindio_dev->setup_ops = &ad4691_cnv_burst_offload_buffer_setup_ops;\n> +\n> +\tret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,\n> +\t\t\t\t\t\t\t  IIO_BUFFER_DIRECTION_IN);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tindio_dev->buffer->attrs = ad4691_buffer_attrs;\n\nShould including ad4691_buffer_attrs depend on st->manual_mode?\n\nI thought it was only used when PWM is connected to CNV.\n\n> +\n> +\treturn 0;\n> +}\n> +\n>  static int ad4691_probe(struct spi_device *spi)\n>  {\n>  \tstruct device *dev = &spi->dev;\n> +\tstruct spi_offload *spi_offload;\n>  \tstruct iio_dev *indio_dev;\n>  \tstruct ad4691_state *st;\n>  \tint ret;\n> @@ -1232,6 +1626,13 @@ static int ad4691_probe(struct spi_device *spi)\n>  \tif (ret)\n>  \t\treturn ret;\n>  \n> +\tspi_offload = devm_spi_offload_get(dev, spi, &ad4691_offload_config);\n> +\tret = PTR_ERR_OR_ZERO(spi_offload);\n> +\tif (ret == -ENODEV)\n> +\t\tspi_offload = NULL;\n> +\telse if (ret)\n> +\t\treturn dev_err_probe(dev, ret, \"Failed to get SPI offload\\n\");\n> +\n>  \tindio_dev->name = st->info->name;\n>  \tindio_dev->info = &ad4691_info;\n>  \tindio_dev->modes = INDIO_DIRECT_MODE;\n> @@ -1239,7 +1640,10 @@ static int ad4691_probe(struct spi_device *spi)\n>  \tindio_dev->channels = st->info->channels;\n>  \tindio_dev->num_channels = st->info->num_channels;\n\nAs mentioned earlier, we generally want separate channel structs\nfor SPI offload. These will also have different num_channels because\nthere is no timestamp channel in SPI offload.\n\n>  \n> -\tret = ad4691_setup_triggered_buffer(indio_dev, st);\n> +\tif (spi_offload)\n> +\t\tret = ad4691_setup_offload(indio_dev, st, spi_offload);\n> +\telse\n> +\t\tret = ad4691_setup_triggered_buffer(indio_dev, st);\n>  \tif (ret)\n>  \t\treturn ret;\n>  \n> @@ -1277,3 +1681,5 @@ module_spi_driver(ad4691_driver);\n>  MODULE_AUTHOR(\"Radu Sabau <radu.sabau@analog.com>\");\n>  MODULE_DESCRIPTION(\"Analog Devices AD4691 Family ADC Driver\");\n>  MODULE_LICENSE(\"GPL\");\n> +MODULE_IMPORT_NS(\"IIO_DMA_BUFFER\");\n> +MODULE_IMPORT_NS(\"IIO_DMAENGINE_BUFFER\");\n>","headers":{"Return-Path":"\n <linux-pwm+bounces-8476-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-pwm@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=Zuq0TCBL;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-pwm+bounces-8476-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.b=\"Zuq0TCBL\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.161.49","smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=baylibre.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=baylibre.com"],"Received":["from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fp0bZ72Ffz1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Sun, 05 Apr 2026 02:57:30 +1100 (AEDT)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 3AE4C300C036\n\tfor <incoming@patchwork.ozlabs.org>; Sat,  4 Apr 2026 15:57:27 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 8CE3F322B8B;\n\tSat,  4 Apr 2026 15:57:26 +0000 (UTC)","from mail-oo1-f49.google.com (mail-oo1-f49.google.com\n [209.85.161.49])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 21C9F31960A\n\tfor <linux-pwm@vger.kernel.org>; Sat,  4 Apr 2026 15:57:23 +0000 (UTC)","by mail-oo1-f49.google.com with SMTP id\n 006d021491bc7-66f747175d8so1472896eaf.0\n        for <linux-pwm@vger.kernel.org>; Sat, 04 Apr 2026 08:57:22 -0700 (PDT)","from ?IPV6:2600:8803:e7e4:500:e14e:bcc6:3f95:26eb?\n ([2600:8803:e7e4:500:e14e:bcc6:3f95:26eb])\n        by smtp.gmail.com with ESMTPSA id\n 006d021491bc7-68448c1c15dsm673767eaf.0.2026.04.04.08.57.19\n        (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n        Sat, 04 Apr 2026 08:57:21 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775318246; cv=none;\n b=czeWaLPiQfU1oB3IwXSab08dsN4Ah18cMtpSijheBDP5SU7PFYMUZaW+B75UQkGbLlJxhy50V7u0Wh+4dKL/1FZ5izosYcoGHSV8iX6vnUpWW8dXb7IUYhvJ9y/yWRHKgrpXfxuVjKv6VgEXyT/DkEKTHUvjFhIJ2zf3w2rWz6k=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775318246; c=relaxed/simple;\n\tbh=I9Zc1R7+yUYIp68dd8Jqc6iCF/xRFUnDWe+7ow9SHeQ=;\n\th=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From:\n\t In-Reply-To:Content-Type;\n b=i8ih9t+Axz44apGT7RXuPC549aWLA9j9zNyuYveWoZv7RzqwOBJWgNc0HGYOlvSk+oY6UvKjk7cPU/U+UpIDPT4nT7kuzlpoJj7VTIFiLrgeKB8AhlYD7Vkd+ke/XAdt6Mhp10yXxzgpuchWBMbmLlU88Q1WjIeR1+QnVgV0xKI=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=none (p=none dis=none) header.from=baylibre.com;\n spf=pass smtp.mailfrom=baylibre.com;\n dkim=pass (2048-bit key) header.d=baylibre-com.20251104.gappssmtp.com\n header.i=@baylibre-com.20251104.gappssmtp.com header.b=Zuq0TCBL;\n arc=none smtp.client-ip=209.85.161.49","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=baylibre-com.20251104.gappssmtp.com; s=20251104; t=1775318242;\n x=1775923042; darn=vger.kernel.org;\n        h=content-transfer-encoding:in-reply-to:from:content-language\n         :references:cc:to:subject:user-agent:mime-version:date:message-id\n         :from:to:cc:subject:date:message-id:reply-to;\n        bh=OBJlkG7WAtThtA0KugNK0B/B0Wk0ExPVBWNuyoZfRiw=;\n        b=Zuq0TCBL+X7JnG9hWQKoIoOWQc/b9PjxnZ6azoWz74E8I9ZyzifyYWsVqSy0ZM/BdE\n         brfib2/Cn/PKC6uOPibWsLuLESFUMGdZcmaxQet7DIUuCdyFw4jMz8oO84hqLMZYlPD1\n         nqK6l3CPgSZCBUH35g7RzBj1G0eaKwLXLptiaQldt4Sq4l2pj9fbc0X96WcfnFk0DMfa\n         xyzHGoXbJNhXXyY0/dR+zCAClUE8jHDLQC0pTfPuB7IP+ruWjJNO/LoUMmHF2DtCP69A\n         DBLsAx+DwJrk//SC/krEYe9OgwwRks0vwzOFoJapQSbUSBrR+a7zLJWqBGII9o7ESzcw\n         XnFA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1775318242; x=1775923042;\n        h=content-transfer-encoding:in-reply-to:from:content-language\n         :references:cc:to:subject:user-agent:mime-version:date:message-id\n         :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n         :reply-to;\n        bh=OBJlkG7WAtThtA0KugNK0B/B0Wk0ExPVBWNuyoZfRiw=;\n        b=UB3DAcyCZ1wmCf3d3XqfhZx53x5i12grubgURZkY1Ifee0fdgtDL8NSdHXEdiX7nc4\n         zh+7O4xjYaw3CUvDDABmAMaQqsc2QT9Ns6VERykPGezaDSFbH/tJO1UwgPvKK2rBTlgN\n         aG2Ubc+uWhTgwD2cZRPdqevKmjoyYjXf3OLpCsNQ73h0EaoYdHkR67N8eiGJqgnn1Eoq\n         cPFJXNRSw+8t06YBQpkdFxLq5cojqA8jmZQ6V9aox/USNqnwY1PBbcyl1wcWrIAF8Wxs\n         dX6EOZVz05dnkR1u1Oqsabb+WCT9XXZJuzVsjU66L6aEZqHKLAhkCyTZgETavQLSW9OZ\n         N32Q==","X-Forwarded-Encrypted":"i=1;\n AJvYcCWiKc1OjqINFv3TVPf/viCgBv3vLlWAEWuHAcpak0EOrALrw8fd67x/twkioG/rwEAlCZb9D17CqYU=@vger.kernel.org","X-Gm-Message-State":"AOJu0YyPTG5T0FN0KmRpXLroQOQWU/atQS1q9UStku5bsY08sqVm/3vi\n\t8+jQVhCUXxewOsUTFwcMcyb++5j/k8Mf2WSHL3UoUQT0sF7BefVvddQV0zahJyrrcaw=","X-Gm-Gg":"AeBDietccg4LsfrYUoF6KjWR1aF4wg0AS44sKeKaeU2euORsGLCjS4FfNarOf2PHV9X\n\tDDJpE6y52MyybEuy0aV7G5jybifB3pt2TJEiyJcsnH1MlNlGWJJGqWkJessZUYZxLgpDCHB/1Dk\n\t0N8VsZoAKPJYb8OtChPGrRK4+hWpovCJQTPhT71PtLZR/6fQ9vBKAwGmqWQOt1welt9xFIV7j2H\n\t2GuJEwIZIHcIPZwXEhwK4/xEeeA8CLSuBXJFS/PwC1cLBlEy6WsWcMNS//f9k+1ZERuQL6QVOon\n\tabIyc+BgIKvu03rSiNWuTgNqM5jLNV13HdYth7WQfwbRKcDnEHQe9jPbdyrYvL240+3I4MizvpB\n\tBDTPrK3lhF2cxBF87figoO6H5JG4WAnG/euKyCIbHR/m7o4MEP5JpuTuvozht19M3pNm0wudBbG\n\tq+9VdE3RVCOkI4MO36qi5cIIva8FRfuKXh7MjrwqiH/wqCRrG7+4fq2xogit/WpnzyeEUZtjy9x\n\tw==","X-Received":"by 2002:a05:6820:4c0b:b0:67a:1eaf:a912 with SMTP id\n 006d021491bc7-6821d747f37mr5208597eaf.2.1775318241917;\n        Sat, 04 Apr 2026 08:57:21 -0700 (PDT)","Message-ID":"<22b44acb-bfb5-4b97-8fa2-aeb4aec704c2@baylibre.com>","Date":"Sat, 4 Apr 2026 10:57:19 -0500","Precedence":"bulk","X-Mailing-List":"linux-pwm@vger.kernel.org","List-Id":"<linux-pwm.vger.kernel.org>","List-Subscribe":"<mailto:linux-pwm+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-pwm+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v6 4/4] iio: adc: ad4691: add SPI offload support","To":"radu.sabau@analog.com, Lars-Peter Clausen <lars@metafoo.de>,\n Michael Hennerich <Michael.Hennerich@analog.com>,\n Jonathan Cameron <jic23@kernel.org>,\n =?utf-8?q?Nuno_S=C3=A1?= <nuno.sa@analog.com>,\n Andy Shevchenko <andy@kernel.org>, Rob Herring <robh@kernel.org>,\n Krzysztof Kozlowski <krzk+dt@kernel.org>, Conor Dooley <conor+dt@kernel.org>,\n =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= <ukleinek@kernel.org>,\n Liam Girdwood <lgirdwood@gmail.com>, Mark Brown <broonie@kernel.org>,\n Linus Walleij <linusw@kernel.org>, Bartosz Golaszewski <brgl@kernel.org>,\n Philipp Zabel <p.zabel@pengutronix.de>, Jonathan Corbet <corbet@lwn.net>,\n Shuah Khan <skhan@linuxfoundation.org>","Cc":"linux-iio@vger.kernel.org, devicetree@vger.kernel.org,\n linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org,\n linux-gpio@vger.kernel.org, linux-doc@vger.kernel.org","References":"\n <20260403-ad4692-multichannel-sar-adc-driver-v6-0-fa2a01a57c4e@analog.com>\n <20260403-ad4692-multichannel-sar-adc-driver-v6-4-fa2a01a57c4e@analog.com>","Content-Language":"en-US","From":"David Lechner <dlechner@baylibre.com>","In-Reply-To":"\n <20260403-ad4692-multichannel-sar-adc-driver-v6-4-fa2a01a57c4e@analog.com>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit"}}]