Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.2/patches/2221511/?format=api
{ "id": 2221511, "url": "http://patchwork.ozlabs.org/api/1.2/patches/2221511/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260409-ad4692-multichannel-sar-adc-driver-v7-4-be375d4df2c5@analog.com/", "project": { "id": 42, "url": "http://patchwork.ozlabs.org/api/1.2/projects/42/?format=api", "name": "Linux GPIO development", "link_name": "linux-gpio", "list_id": "linux-gpio.vger.kernel.org", "list_email": "linux-gpio@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260409-ad4692-multichannel-sar-adc-driver-v7-4-be375d4df2c5@analog.com>", "list_archive_url": null, "date": "2026-04-09T15:28:25", "name": "[v7,4/6] iio: adc: ad4691: add SPI offload support", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "062c5d1730b45a9e3f12ac65ac42e2bae99a7a3f", "submitter": { "id": 92791, "url": "http://patchwork.ozlabs.org/api/1.2/people/92791/?format=api", "name": "Radu Sabau via B4 Relay", "email": "devnull+radu.sabau.analog.com@kernel.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-gpio/patch/20260409-ad4692-multichannel-sar-adc-driver-v7-4-be375d4df2c5@analog.com/mbox/", "series": [ { "id": 499317, "url": "http://patchwork.ozlabs.org/api/1.2/series/499317/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-gpio/list/?series=499317", "date": "2026-04-09T15:28:22", "name": "iio: adc: ad4691: add driver for AD4691 multichannel SAR ADC family", "version": 7, "mbox": "http://patchwork.ozlabs.org/series/499317/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2221511/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2221511/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-gpio+bounces-34962-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-gpio@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=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=AsZT1IYd;\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-gpio+bounces-34962-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=\"AsZT1IYd\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201" ], "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 4fs3qj0fZdz1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 10 Apr 2026 01:33:37 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sto.lore.kernel.org (Postfix) with ESMTP id CEB8B305FDCA\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 9 Apr 2026 15:29:14 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id BC40C3E122E;\n\tThu, 9 Apr 2026 15:28:29 +0000 (UTC)", "from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org\n [10.30.226.201])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 66D073E0C73;\n\tThu, 9 Apr 2026 15:28:29 +0000 (UTC)", "by smtp.kernel.org (Postfix) with ESMTPS id 39113C2BCC6;\n\tThu, 9 Apr 2026 15:28:29 +0000 (UTC)", "from aws-us-west-2-korg-lkml-1.web.codeaurora.org\n (localhost.localdomain [127.0.0.1])\n\tby smtp.lore.kernel.org (Postfix) with ESMTP id 30B2DF31E29;\n\tThu, 9 Apr 2026 15:28:29 +0000 (UTC)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775748509; cv=none;\n b=Ht47Vn4ZXtIjKRf7RhOlZELhGalbuoDoIA6hKemMIWLDw+9VFn+HAaIXsDvMPJ6T5HzS/X22S9Ph9NGt7q2xGEMGkrapda8Zk8L4hyygXtA3rHKfkZQG0wFB9DDCwgaN/zv4fottF1aRaCOI2YHwfcUNS4jKABxs9ElUrWzGn4k=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775748509; c=relaxed/simple;\n\tbh=a3sivRVcRVgdEWalCeAfQYpuQpE1QVustkNCWadtQiw=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=thGChUBVXluviBKiUUzjYCBhLFRSwsSDjW0iutlGlu+G17dKdkx4s9neRh7kTloVQGaxBtKYxlEmwetU8bpMvvyXC3QZKeNDWkQL/0D6L0C0BUXKchqBZFxSLzm18t2Sdj8hrqx+xOtIf0V0TzZ0IhL+uoaR6E5glI5Scx5n2qA=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=AsZT1IYd; arc=none smtp.client-ip=10.30.226.201", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;\n\ts=k20201202; t=1775748509;\n\tbh=a3sivRVcRVgdEWalCeAfQYpuQpE1QVustkNCWadtQiw=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From;\n\tb=AsZT1IYdqHl0fWG9Wtm24tBJLAU1GA3U6VpMhapuuOR1zvX3WYIHx9yYF4R4lC28t\n\t Idp4Ue3/HC/F89oADBJcGZg/g4kjhFGwRTykza5IWJOlvdvURC0JydLejrw12BEnFB\n\t CG9N16aI0xWjOqCHye49YNzOtewiSUhhjNp90EoPdREQxEid5nMBuv6MX7BY1K5FhG\n\t cwhVx/mN3M2fVzCiS7PVuCylzNPbmnCh7ID2S3Fv33eq8uacy6AclcMnn0KEc5QKd6\n\t eiYLICQuHQBt+nXLWz+R3/rfUUndIRVk00Om5xhokia0Io01zW2Ml+T3FxDRhoDREZ\n\t E8sPwYzjmj1Fg==", "From": "Radu Sabau via B4 Relay <devnull+radu.sabau.analog.com@kernel.org>", "Date": "Thu, 09 Apr 2026 18:28:25 +0300", "Subject": "[PATCH v7 4/6] iio: adc: ad4691: add SPI offload support", "Precedence": "bulk", "X-Mailing-List": "linux-gpio@vger.kernel.org", "List-Id": "<linux-gpio.vger.kernel.org>", "List-Subscribe": "<mailto:linux-gpio+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-gpio+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "8bit", "Message-Id": "\n <20260409-ad4692-multichannel-sar-adc-driver-v7-4-be375d4df2c5@analog.com>", "References": "\n <20260409-ad4692-multichannel-sar-adc-driver-v7-0-be375d4df2c5@analog.com>", "In-Reply-To": "\n <20260409-ad4692-multichannel-sar-adc-driver-v7-0-be375d4df2c5@analog.com>", "To": "Lars-Peter Clausen <lars@metafoo.de>,\n Michael Hennerich <Michael.Hennerich@analog.com>,\n Jonathan Cameron <jic23@kernel.org>, David Lechner <dlechner@baylibre.com>,\n\t=?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>,\n 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,\n Radu Sabau <radu.sabau@analog.com>", "X-Mailer": "b4 0.14.3", "X-Developer-Signature": "v=1; a=ed25519-sha256; t=1775748505; l=20066;\n i=radu.sabau@analog.com; s=20260220; h=from:subject:message-id;\n bh=Ousdf/UUlNDEkFs02oWb2zGh3S08PDZ2UYODcd0dY2M=;\n b=YazVMq8egOV5J2t0sFtya0WAQ6bOi+1C+akpV85XA7eNZwq+4bdg+Vb718mslBmS6x8nIgDXm\n DXOgDb4vDIIB2O36DwtR8TILGD0MaKqH4KkZEXpiig5dApKtmDVM/dR", "X-Developer-Key": "i=radu.sabau@analog.com; a=ed25519;\n pk=lDPQHgn9jTdt0vo58Na9lLxLaE2mb330if71Cn+EvFU=", "X-Endpoint-Received": "by B4 Relay for radu.sabau@analog.com/20260220 with\n auth_id=642", "X-Original-From": "Radu Sabau <radu.sabau@analog.com>", "Reply-To": "radu.sabau@analog.com" }, "content": "From: Radu Sabau <radu.sabau@analog.com>\n\nAdd SPI offload support to enable DMA-based, CPU-independent data\nacquisition using the SPI Engine offload framework.\n\nWhen an SPI offload is available (devm_spi_offload_get() succeeds),\nthe driver registers a DMA engine IIO buffer and uses dedicated buffer\nsetup operations. If no offload is available the existing software\ntriggered buffer path is used unchanged.\n\nBoth CNV Burst Mode and Manual Mode support offload, but use different\ntrigger mechanisms:\n\nCNV Burst Mode: the SPI Engine is triggered by the ADC's DATA_READY\nsignal on the GP pin specified by the trigger-source consumer reference\nin the device tree (one cell = GP pin number 0-3). For this mode the\ndriver acts as both an SPI offload consumer (DMA RX stream, message\noptimization) and a trigger source provider: it registers the\nGP/DATA_READY output via devm_spi_offload_trigger_register() so the\noffload framework can match the '#trigger-source-cells' phandle and\nautomatically fire the SPI Engine DMA transfer at end-of-conversion.\n\nManual Mode: the SPI Engine is triggered by a periodic trigger at\nthe configured sampling frequency. The pre-built SPI message uses\nthe pipelined CNV-on-CS protocol: N+1 16-bit transfers are issued\nfor N active channels (the first result is discarded as garbage from\nthe pipeline flush) and the remaining N results are captured by DMA.\n\nAll offload transfers use 16-bit frames (bits_per_word=16, len=2).\nThe channel scan_type (storagebits=16, shift=0, IIO_BE) is shared\nbetween the software triggered-buffer and offload paths; no separate\nscan_type or channel array is needed for the offload case. The\nad4691_manual_channels[] array introduced in the triggered-buffer\ncommit is reused here: it hides the IIO_CHAN_INFO_OVERSAMPLING_RATIO\nattribute, which is not applicable in Manual Mode.\n\nKconfig gains a dependency on IIO_BUFFER_DMAENGINE.\n\nSigned-off-by: Radu Sabau <radu.sabau@analog.com>\n---\n drivers/iio/adc/Kconfig | 2 +\n drivers/iio/adc/ad4691.c | 398 ++++++++++++++++++++++++++++++++++++++++++++++-\n 2 files changed, 395 insertions(+), 5 deletions(-)", "diff": "diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig\nindex d498f16c0816..fdc6565933c5 100644\n--- a/drivers/iio/adc/Kconfig\n+++ b/drivers/iio/adc/Kconfig\n@@ -143,8 +143,10 @@ config AD4691\n \ttristate \"Analog Devices AD4691 Family ADC Driver\"\n \tdepends on SPI\n \tselect IIO_BUFFER\n+\tselect IIO_BUFFER_DMAENGINE\n \tselect IIO_TRIGGERED_BUFFER\n \tselect REGMAP\n+\tselect SPI_OFFLOAD\n \thelp\n \t Say yes here to build support for Analog Devices AD4691 Family MuxSAR\n \t SPI analog to digital converters (ADC).\ndiff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c\nindex 3e5caa0972eb..839ea7f44c78 100644\n--- a/drivers/iio/adc/ad4691.c\n+++ b/drivers/iio/adc/ad4691.c\n@@ -22,6 +22,8 @@\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@@ -43,6 +45,11 @@\n \n #define AD4691_CNV_DUTY_CYCLE_NS\t\t380\n #define AD4691_CNV_HIGH_TIME_NS\t\t\t430\n+/*\n+ * Conservative default for the manual offload periodic trigger. Low enough\n+ * to work safely out of the box across all OSR and channel count combinations.\n+ */\n+#define AD4691_OFFLOAD_INITIAL_TRIGGER_HZ\t(100 * HZ_PER_KHZ)\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@@ -95,6 +102,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\t16\n+\n static const char * const ad4691_supplies[] = { \"avdd\", \"vio\" };\n \n enum ad4691_ref_ctrl {\n@@ -114,6 +123,7 @@ struct ad4691_chip_info {\n \tconst char *name;\n \tunsigned int max_rate;\n \tconst struct ad4691_channel_info *sw_info;\n+\tconst struct ad4691_channel_info *offload_info;\n };\n \n #define AD4691_CHANNEL(ch)\t\t\t\t\t\t\\\n@@ -177,6 +187,18 @@ static const struct ad4691_channel_info ad4693_sw_info = {\n \t.num_channels = ARRAY_SIZE(ad4693_channels),\n };\n \n+static const struct ad4691_channel_info ad4691_offload_info = {\n+\t.channels = ad4691_channels,\n+\t/* No soft timestamp; num_channels caps access to 16. */\n+\t.num_channels = 16,\n+};\n+\n+static const struct ad4691_channel_info ad4693_offload_info = {\n+\t.channels = ad4693_channels,\n+\t/* No soft timestamp; num_channels caps access to 8. */\n+\t.num_channels = 8,\n+};\n+\n /*\n * Internal oscillator frequency table. Index is the OSC_FREQ_REG[3:0] value.\n * Index 0 (1 MHz) is only valid for AD4692/AD4694; AD4691/AD4693 support\n@@ -207,24 +229,36 @@ static const struct ad4691_chip_info ad4691_chip_info = {\n \t.name = \"ad4691\",\n \t.max_rate = 500 * HZ_PER_KHZ,\n \t.sw_info = &ad4691_sw_info,\n+\t.offload_info = &ad4691_offload_info,\n };\n \n static const struct ad4691_chip_info ad4692_chip_info = {\n \t.name = \"ad4692\",\n \t.max_rate = 1 * HZ_PER_MHZ,\n \t.sw_info = &ad4691_sw_info,\n+\t.offload_info = &ad4691_offload_info,\n };\n \n static const struct ad4691_chip_info ad4693_chip_info = {\n \t.name = \"ad4693\",\n \t.max_rate = 500 * HZ_PER_KHZ,\n \t.sw_info = &ad4693_sw_info,\n+\t.offload_info = &ad4693_offload_info,\n };\n \n static const struct ad4691_chip_info ad4694_chip_info = {\n \t.name = \"ad4694\",\n \t.max_rate = 1 * HZ_PER_MHZ,\n \t.sw_info = &ad4693_sw_info,\n+\t.offload_info = &ad4693_offload_info,\n+};\n+\n+struct ad4691_offload_state {\n+\tstruct spi_offload *spi;\n+\tstruct spi_offload_trigger *trigger;\n+\tu64 trigger_hz;\n+\tu8 tx_cmd[17][2];\n+\tu8 tx_reset[4];\n };\n \n struct ad4691_state {\n@@ -253,7 +287,10 @@ struct ad4691_state {\n \t *\t\t transfers in one go.\n \t */\n \tstruct spi_message scan_msg;\n-\t/* max 16 + 1 NOOP (manual) or 2*16 + 2 (CNV burst). */\n+\t/*\n+\t * Non-offload: max 16 + 1 NOOP (manual) or 2*16 + 2 (CNV burst).\n+\t * Offload reuses this array — both paths are mutually exclusive.\n+\t */\n \tstruct spi_transfer scan_xfers[34];\n \t/*\n \t * CNV burst: 16 AVG_IN addresses + state-reset address + state-reset\n@@ -265,6 +302,8 @@ struct ad4691_state {\n \t\t__be16 vals[16];\n \t\taligned_s64 ts;\n \t} scan;\n+\t/* NULL when no SPI offload hardware is present */\n+\tstruct ad4691_offload_state *offload;\n };\n \n /*\n@@ -284,6 +323,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, args[0]);\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@@ -807,6 +886,214 @@ 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(st->scan_xfers, 0, sizeof(st->scan_xfers));\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]\n+\t * RX: [data_hi, data_lo] (storagebits=16, shift=0)\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\tst->scan_xfers[k].tx_buf = offload->tx_cmd[k];\n+\t\tst->scan_xfers[k].len = sizeof(offload->tx_cmd[k]);\n+\t\tst->scan_xfers[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n+\t\tst->scan_xfers[k].cs_change = 1;\n+\t\tst->scan_xfers[k].cs_change_delay.value = AD4691_CNV_HIGH_TIME_NS;\n+\t\tst->scan_xfers[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\tst->scan_xfers[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+\tst->scan_xfers[k].tx_buf = offload->tx_cmd[k];\n+\tst->scan_xfers[k].len = sizeof(offload->tx_cmd[k]);\n+\tst->scan_xfers[k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n+\tst->scan_xfers[k].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;\n+\tk++;\n+\n+\tspi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, k);\n+\tst->scan_msg.offload = offload->spi;\n+\n+\tret = spi_optimize_message(spi, &st->scan_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(&st->scan_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(&st->scan_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;\n+\tunsigned int bit, k;\n+\tint ret;\n+\n+\tn_active = bitmap_weight(indio_dev->active_scan_mask, iio_get_masklength(indio_dev));\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+\tret = ad4691_enter_conversion_mode(st);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tmemset(st->scan_xfers, 0, sizeof(st->scan_xfers));\n+\n+\t/*\n+\t * Each AVG_IN register read uses two 16-bit transfers:\n+\t * TX: [reg_hi | 0x80, reg_lo] (address, CS stays asserted)\n+\t * RX: [data_hi, data_lo] (data, storagebits=16, shift=0)\n+\t * The state reset is also split into two 16-bit transfers\n+\t * (address then value) to keep bits_per_word uniform throughout.\n+\t */\n+\tk = 0;\n+\tiio_for_each_active_channel(indio_dev, bit) {\n+\t\tput_unaligned_be16(0x8000 | AD4691_AVG_IN(bit), offload->tx_cmd[k]);\n+\n+\t\t/* TX: address phase, CS stays asserted into data phase */\n+\t\tst->scan_xfers[2 * k].tx_buf = offload->tx_cmd[k];\n+\t\tst->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);\n+\t\tst->scan_xfers[2 * k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n+\n+\t\t/* RX: data phase, CS toggles after to delimit the next register op */\n+\t\tst->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);\n+\t\tst->scan_xfers[2 * k + 1].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n+\t\tst->scan_xfers[2 * k + 1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;\n+\t\tst->scan_xfers[2 * k + 1].cs_change = 1;\n+\t\tk++;\n+\t}\n+\n+\t/* State reset to re-arm DATA_READY for the next scan. */\n+\tput_unaligned_be16(AD4691_STATE_RESET_REG, offload->tx_reset);\n+\toffload->tx_reset[2] = AD4691_STATE_RESET_ALL;\n+\n+\tst->scan_xfers[2 * k].tx_buf = offload->tx_reset;\n+\tst->scan_xfers[2 * k].len = sizeof(offload->tx_cmd[k]);\n+\tst->scan_xfers[2 * k].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n+\n+\tst->scan_xfers[2 * k + 1].tx_buf = &offload->tx_reset[2];\n+\tst->scan_xfers[2 * k + 1].len = sizeof(offload->tx_cmd[k]);\n+\tst->scan_xfers[2 * k + 1].bits_per_word = AD4691_OFFLOAD_BITS_PER_WORD;\n+\tst->scan_xfers[2 * k + 1].cs_change = 1;\n+\n+\tspi_message_init_with_transfers(&st->scan_msg, st->scan_xfers, 2 * k + 2);\n+\tst->scan_msg.offload = offload->spi;\n+\n+\tret = spi_optimize_message(spi, &st->scan_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(&st->scan_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(&st->scan_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@@ -814,7 +1101,10 @@ 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-\treturn sysfs_emit(buf, \"%u\\n\", NSEC_PER_SEC / st->cnv_period_ns);\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, \"%lu\\n\", NSEC_PER_SEC / st->cnv_period_ns);\n }\n \n static ssize_t sampling_frequency_store(struct device *dev,\n@@ -833,6 +1123,23 @@ static ssize_t sampling_frequency_store(struct device *dev,\n \tif (ret)\n \t\treturn ret;\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+\n+\t\tret = spi_offload_trigger_validate(st->offload->trigger, &config);\n+\t\tif (ret) {\n+\t\t\tiio_device_release_direct(indio_dev);\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tst->offload->trigger_hz = config.periodic.frequency_hz;\n+\t\tiio_device_release_direct(indio_dev);\n+\t\treturn len;\n+\t}\n+\n \tret = ad4691_set_pwm_freq(st, freq);\n \tiio_device_release_direct(indio_dev);\n \tif (ret)\n@@ -1132,9 +1439,75 @@ static int ad4691_setup_triggered_buffer(struct iio_dev *indio_dev,\n \t\t\t\t\t\t ad4691_buffer_attrs);\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 = AD4691_OFFLOAD_INITIAL_TRIGGER_HZ;\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+\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@@ -1168,13 +1541,26 @@ 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 \n-\tindio_dev->channels = st->info->sw_info->channels;\n-\tindio_dev->num_channels = st->info->sw_info->num_channels;\n-\tret = ad4691_setup_triggered_buffer(indio_dev, st);\n+\tif (spi_offload) {\n+\t\tindio_dev->channels = st->info->offload_info->channels;\n+\t\tindio_dev->num_channels = st->info->offload_info->num_channels;\n+\t\tret = ad4691_setup_offload(indio_dev, st, spi_offload);\n+\t} else {\n+\t\tindio_dev->channels = st->info->sw_info->channels;\n+\t\tindio_dev->num_channels = st->info->sw_info->num_channels;\n+\t\tret = ad4691_setup_triggered_buffer(indio_dev, st);\n+\t}\n \tif (ret)\n \t\treturn ret;\n \n@@ -1212,3 +1598,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", "prefixes": [ "v7", "4/6" ] }