* [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support
@ 2026-09-04 14:14 Janani Sunil
2026-09-04 14:14 ` [PATCH v6 01/17] iio: adc: adi-axi-adc: Initialize state mutex Janani Sunil
` (16 more replies)
0 siblings, 17 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Add support for Analog Devices AD7768 and AD7768-4, 24-bit simultaneous
sampling Sigma-Delta ADCs with automatic power mode selection.
Datasheet:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-ad7768-4.pdf
**Device Overview:**
The AD7768 is an 8-channel, 24-bit simultaneous sampling ADC. The
AD7768-4 is a 4-channel variant. Both support configurable power modes,
programmable decimation filters, and a high-speed serial interface with
1, 2, 4, or 8 data output lines. Per-channel offset, gain, and phase
calibration registers are available.
**Features Implemented:**
- Support for AD7768 (8-channel) and AD7768-4 (4-channel) via device
match data.
- Automatic low-noise power mode selection (fast, median, low power)
with associated decimation rate and output data rate tables.
- Per-channel offset, gain, and conversion delay via IIO attributes.
- SPI communication with custom regmap bus for the device's protocol.
- IIO backend integration with adi-axi-adc for high-speed data capture.
- CRC data integrity verification over the serial interface.
- Register a GPIO auxiliary device for the separately submitted AD7768
GPIO driver.
- VCM common-mode output exposed through the regulator framework, with
runtime PM management while the output is enabled.
**Patch Summary:**
1. Initialize the AXI ADC state mutex.
2. Add the AD7768 and AD7768-4 devicetree binding.
3. Add the IIO backend CRC operation.
4. Implement CRC control in the AXI ADC backend.
5. Add core AD7768 and AD7768-4 capture support.
6. Validate the master clock rate.
7. Add the power-mode configuration helper.
8. Derive output data rates and the data-clock divider.
9. Configure enabled channels across two sampling profiles.
10. Expose per-channel sampling-frequency controls.
11. Expose per-channel filter controls.
12. Wait for active digital filters to settle.
13. Add per-channel offset and gain calibration controls.
14. Add per-channel conversion delay.
15. Expose the VCM output through the regulator framework.
16. Register the GPIO auxiliary device from the parent driver.
17. Add driver documentation.
**Testing:**
The driver was compiled and tested on the EVAL-AD7768FMCZ using a
Zedboard with a mainline kernel.
**Driver Rationale:**
The AD7768/AD7768-4 warrants its own driver due to several
device-specific characteristics:
1. A custom SPI framing protocol: register reads and writes use 16-bit
frames with embedded read/write bits and register address fields,
requiring a custom regmap bus implementation rather than the standard
SPI regmap backend.
2. A coupled power mode / sample rate model: available output data rates
are not a flat list but depend on the active power mode (fast, median,
low power), each with its own set of valid decimation rates. This
relationship must be managed in the driver.
3. Simultaneous sampling across all channels with per-channel calibration
registers (24-bit offset, gain, and phase) that are laid out
differently between the 8-channel AD7768 and the 4-channel AD7768-4,
requiring device-specific register mapping.
4. IIO backend integration for high-speed data capture via adi-axi-adc,
including CRC data integrity verification over the serial interface.
5. On-chip GPIO: the parent registers an auxiliary device that shares its
regmap and runtime PM state. The GPIO driver and supporting gpio-regmap
changes are handled separately through the GPIO tree.
**Base:**
This series is based on Linux v7.3-rc1, commit
cee9395acd8043be0644b25c34bfa86623f2b935 ("Linux 7.3-rc1").
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
Changes in v6:
- Rebase onto Linux v7.3-rc1 cee9395acd8043be0644b25c34bfa86623f2b935.
- Document why the custom SPI protocol requires regmap bulk transfers to
be split into individual register accesses.
- Simplify register definitions, channel parsing, data-line property
handling, clock-source selection, error reporting, and fixed capture
configuration.
- Use standard unit and bit helpers and keep the scale implementation
stable as sampling-frequency controls are introduced.
- Simplify output-rate derivation, DCLK divider selection, power-mode
selection, filter settling, and conversion-delay arithmetic.
- Add the mutex only when first used and optimize the driver state layout
based on pahole results.
- Split the GPIO support patches into a separate series based on
gpio/for-next:
https://lore.kernel.org/r/20260903-ad7768-gpio-v6-0-4254ef6c9b38@analog.com
- Link to v5: https://lore.kernel.org/r/20260828-ad7768-driver-v5-0-e33ca6f841a2@analog.com
Changes in v5:
- Split configurable sampling support into focused patches for clock
validation, power-mode setup, output-rate derivation, channel profiles,
sampling-frequency controls, filter controls, and filter settling.
- Read the positive and negative reference supply voltages and report the
resulting differential IIO scale for each channel group.
- Allow grounded REF1- and REF2- inputs to omit their regulator supplies,
treating absent negative reference supplies as 0 V.
- Document the per-channel IIO scale attribute.
- Require a named master clock, remove the default clock source, and
clarify why the LVDS clock is enabled only after device configuration.
- Clarify the clock-source, supply, and VCM descriptions in the
Devicetree binding, and drop the unused sync-out-gpios property.
- Simplify scan-mask handling, register-range checks, channel parsing,
reset timing, probe initialization, and error reporting.
- Use the exact 1.66 ms reset start-up delay and standard time-unit
constants for fixed delays.
- Avoid runtime-PM references for cached IIO attributes and acquire them
locally for calibration register accesses.
- Use regmap_assign_bits() for single-bit assignments and sizeof() for
calibration byte-array transfers.
- Rework gpio-regmap runtime-PM guards and synchronize the pm_dev member
and kernel-doc ordering.
- Simplify the AD7768 GPIO auxiliary driver's includes and probe setup,
and drop its cross-tree MAINTAINERS update.
- Note that the AXI ADC mutex fix is already queued in the IIO fixes tree.
- Link to v4: https://lore.kernel.org/r/20260821-ad7768-driver-v4-0-bb8fbd06d4eb@analog.com
Changes in v4:
- Move the AXI ADC mutex initialization fix to the start of the series.
- Clarify the IIO backend CRC documentation for input and output paths.
- Add explicit MCLK, crystal, and LVDS clock-source handling and
sequencing.
- Keep the required channel active for crystal excitation.
- Configure FILTER/GPIO4 for crystal and LVDS clock sources and reserve
it from GPIO consumers while it is used for clock selection.
- Make AVSS optional.
- Validate MCLK, crystal, and LVDS clock rates against datasheet limits.
- Split each differential reference into positive and negative supplies.
- Correct AD7768-4 standby and channel-mode bit handling.
- Program the AD7768-4 channel mode-enable bits correctly.
- Use unshifted register values and FIELD_PREP() consistently.
- Use regmap single-bit helpers where appropriate.
- Use scoped runtime-PM guards and scoped loop variables.
- Use for_each_set_bit() and named channel-child iteration.
- Remove the redundant initial autosuspend request.
- Document the validated 20 ms wake-up delay.
- Make REGULATOR mandatory when VCM regulator support is introduced.
- Add feature dependencies and includes in their respective patches.
- Convert gpio-regmap runtime-PM handling to cleanup guards.
- Use active-only PM references for gpio-regmap operations.
- Mark GPIO chips using runtime PM as sleep-capable.
- Simplify the AD7768 GPIO auxiliary driver.
- Clean up IIO and kernel coding-style issues across the series.
- Drop the redundant Devicetree example.
- Update the link to the latest Rev. D datasheet, update table
references.
- Address the remaining binding description comments.
- Link to v3: https://lore.kernel.org/r/20260813-ad7768-driver-v3-0-cb554399ad26@analog.com
Changes in v3:
- Split the IIO driver into core support and focused patches for sampling
modes, calibration, conversion delay, VCM, and GPIO registration.
- Split AXI ADC mutex initialization into a separate preparatory patch.
- Use PSEC_PER_SEC, explicit frequency units, unsigned frequency storage,
32-bit divider arithmetic, and rounddown_pow_of_two().
- Use endian helpers and bulk regmap operations for calibration transfers,
while retaining single-register writes for the custom SPI protocol.
- Validate the MCLK rate during probe and reject a zero computed DCLK rate.
- Remove redundant SPI and channel-template state, regmap_attach_dev(),
unused includes, and a forward declaration, unused chn_mode assignment.
- Move regulators additionalProperties and drop redundant GPIO
dependencies from the Devicetree binding.
- Split the gpio-regmap changes and add the regmap_test_bits() conversion
as a separate cleanup.
- Limit the generic gpio-regmap extension to optional runtime PM support.
- Use an AD7768 reg_mask_xlate() callback to select the output latch for
output GPIOs and document why it is required.
- Use regmap_test_bits() when reading the VCM enable state.
- Use descriptive runtime PM cleanup labels and verify structure layout
with pahole.
- Set the VCM regulator's regulators_node so constraints from the nested
vcm-output node are parsed.
- Mark gpio-regmap GPIO chips as sleep-capable when a runtime PM device is
configured.
- Reword the conversion-delay and buffered-capture documentation to
clarify their operation.
- Link to v2: https://lore.kernel.org/r/20260806-ad7768-driver-v2-0-027ac5e2a641@analog.com
Changes in v2:
- Renamed and expanded supply descriptions, split reference to
ref1-supply and ref2-supply.
- Added START and synchronization signal descriptions.
- Added variant-specific channel and data-line validation.
- Made adi,data-lines-number optional, defaulting to the maximum.
- Renamed the precharge and reference-buffer properties, removed channel
mode and power mode properties.
- Exposed the VCM output through the regulator framework and updated
documentation accordingly.
- Improved runtime PM initialization and requested initial autosuspend
after probe.
- Automatically assigned channels to two hardware mode profiles.
- Selected the lowest noise compatible power mode when ODR ranges overlap.
- Replaced the raw phase attribute with the standard conversion-delay
attribute.
- Added filter-settling delays based on the selected filter and ODR.
- Fixed AD7768-4 register and channel-bit mapping, including standby,
calibration, phase, precharge and reference-buffer handling.
- Added direct-mode protection around runtime channel configuration.
- Added calibration register range validation and documented
calibration values as raw unsigned 24-bit values.
- Added the required reset pulse delay derived from MCLK.
- Added a generic GPIO-regmap patch providing runtime-PM integration
and separate output-register readback.
- Converted the AD7768 GPIO auxiliary driver to use gpio-regmap.
- Documented that AXI ADC CRC control bit 8 is common to the AXI ADC
cores.
- Added the missing AXI ADC mutex initialization.
- Expanded the driver documentation to describe dynamic channel
grouping, power-mode selection, VCM regulation and conversion delay.
- Link to v1: https://lore.kernel.org/r/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com
---
Janani Sunil (17):
iio: adc: adi-axi-adc: Initialize state mutex
dt-bindings: iio: adc: Add AD7768
iio: backend: Add support for CRC
iio: adc: adi-axi-adc: Add support for CRC
iio: adc: Add AD7768 and AD7768-4 core support
iio: adc: ad7768: Validate master clock rate
iio: adc: ad7768: Add power mode helper
iio: adc: ad7768: Derive output data rates
iio: adc: ad7768: Configure channel sampling profiles
iio: adc: ad7768: Add sampling frequency controls
iio: adc: ad7768: Add per-channel filter controls
iio: adc: ad7768: Wait for digital filters to settle
iio: adc: ad7768: Add calibration controls
iio: adc: ad7768: Add per-channel conversion delay
iio: adc: ad7768: Add VCM regulator support
iio: adc: ad7768: Register GPIO auxiliary device
Documentation: iio: Add AD7768 Documentation
.../devicetree/bindings/iio/adc/adi,ad7768.yaml | 270 +++
Documentation/iio/ad7768.rst | 273 +++
Documentation/iio/index.rst | 1 +
MAINTAINERS | 9 +
drivers/iio/adc/Kconfig | 15 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ad7768.c | 1888 ++++++++++++++++++++
drivers/iio/adc/adi-axi-adc.c | 25 +
drivers/iio/industrialio-backend.c | 33 +
include/linux/iio/backend.h | 6 +
10 files changed, 2521 insertions(+)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260708-ad7768-driver-10cd15296c20
Best regards,
--
Janani Sunil <janani.sunil@analog.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v6 01/17] iio: adc: adi-axi-adc: Initialize state mutex
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
@ 2026-09-04 14:14 ` Janani Sunil
2026-09-04 14:14 ` [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768 Janani Sunil
` (15 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
The AXI ADC register access paths serialize transactions with st->lock,
but probe does not initialize it. Initialize the mutex before registering
the backend.
Fixes: 7ecb8ee5c93b ("iio: adc: adi-axi-adc: support digital interface calibration")
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
This patch is already queued in the IIO fixes-togreg branch as commit
040c2514b25b ("iio: adc: adi-axi-adc: Initialize state mutex").
---
drivers/iio/adc/adi-axi-adc.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 589f03618ecf..fc7cc92b35f0 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -714,6 +714,10 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (!st)
return -ENOMEM;
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
2026-09-04 14:14 ` [PATCH v6 01/17] iio: adc: adi-axi-adc: Initialize state mutex Janani Sunil
@ 2026-09-04 14:14 ` Janani Sunil
2026-09-04 14:21 ` sashiko-bot
2026-09-04 17:08 ` Conor Dooley
2026-09-04 14:14 ` [PATCH v6 03/17] iio: backend: Add support for CRC Janani Sunil
` (14 subsequent siblings)
16 siblings, 2 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Devicetree Bindings for AD7768-4 (4 channel) and AD7768 (8 channel)
simultaneous sampling ADCs.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
.../devicetree/bindings/iio/adc/adi,ad7768.yaml | 270 +++++++++++++++++++++
MAINTAINERS | 7 +
2 files changed, 277 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
new file mode 100644
index 000000000000..d8e489fa1f6f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
@@ -0,0 +1,270 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad7768.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD7768 and AD7768-4 ADC
+
+maintainers:
+ - Janani Sunil <janani.sunil@analog.com>
+
+description: |
+ The AD7768 is an 8-channel, 24-bit simultaneous sampling ADC with configurable
+ power and performance modes. The AD7768-4 is a 4-channel version.
+
+ This binding covers SPI mode only (register access via SPI). Pin control mode,
+ where configuration is set via hardware pins rather than SPI registers, is not
+ supported.
+
+ The io-backend represents the high-speed data output interface, consuming the
+ DOUTx data lines, DCLK (data clock), and DRDY (data ready) signals.
+
+ Datasheet at:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-ad7768-4.pdf
+
+ HDL reference design:
+ https://github.com/analogdevicesinc/hdl/tree/main/projects/ad7768evb
+
+properties:
+ compatible:
+ enum:
+ - adi,ad7768
+ - adi,ad7768-4
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ description:
+ Describes the type of master clock (MCLK) attached to the XTAL1 and
+ XTAL2/MCLK pins. A CMOS clock is supplied on the XTAL2/MCLK pin, a
+ crystal oscillator is connected between the XTAL1 and XTAL2 pins, and an
+ LVDS differential clock is supplied on the XTAL1/XTAL2 pin pair. CMOS and
+ LVDS clocks must operate from 1.15 MHz to 34 MHz. Crystals must operate
+ from 8 MHz to 34 MHz.
+ enum:
+ - mclk
+ - xtal
+ - lvds
+
+ avdd1-supply:
+ description:
+ Analog supply. Its voltage is used to derive the supply-dependent VCM
+ output level, so it must be a regulator that can report its voltage.
+
+ avss-supply:
+ description:
+ Negative analog supply. If omitted, AVSS is assumed to be at system
+ ground.
+
+ avdd2-supply: true
+
+ iovdd-supply: true
+
+ ref1p-supply: true
+
+ ref1n-supply:
+ description:
+ Negative reference input supply for REF1. If omitted, REF1- is assumed
+ to be connected to ground.
+
+ ref2p-supply: true
+
+ ref2n-supply:
+ description:
+ Negative reference input supply for REF2. If omitted, REF2- is assumed
+ to be connected to ground.
+
+ reset-gpios:
+ maxItems: 1
+ description: GPIO connected to the active-low RESET pin
+
+ start-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the START pin. An active-low pulse on this pin
+ synchronizes the device to other AD7768 devices. When not used, this
+ pin must be tied to a logic high level through a pull-up resistor.
+ When used, SYNC_OUT must be wired back to SYNC_IN on all devices in
+ the synchronization chain.
+
+ sync-in-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the SYNC_IN pin. Receives the synchronization pulse
+ generated on SYNC_OUT in response to a START pulse. Must be synchronous
+ to the MCLK clock domain. In multi-device or daisy-chained systems,
+ two successive synchronization pulses must be applied to guarantee all
+ ADCs are synchronized.
+
+ gpio-controller: true
+
+ '#gpio-cells':
+ const: 2
+
+ adi,data-lines-number:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [1, 2, 4, 8]
+ description:
+ Number of DOUTx lines used for the high-speed data output interface
+ (distinct from the SPI control interface). AD7768 supports 1, 2, or 8
+ lines; AD7768-4 supports 1 or 4 lines. Defaults to the maximum number
+ of lines for the selected variant if omitted.
+
+ regulators:
+ type: object
+ description: Container for the on-chip VCM regulator.
+ additionalProperties: false
+
+ properties:
+ vcm-output:
+ type: object
+ description:
+ Buffered common-mode voltage output on the VCM pin. Use
+ regulator-always-on when no consumer node references this output.
+ Channel 0 must be present and enabled while VCM is used, since
+ putting channel 0 in standby disables the VCM output.
+ $ref: /schemas/regulator/regulator.yaml#
+ unevaluatedProperties: false
+
+ io-backends:
+ maxItems: 1
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+patternProperties:
+ "^channel@[0-7]$":
+ type: object
+ description:
+ Represents the external channels which are connected to the device.
+ AD7768 supports channels 0-7, AD7768-4 supports channels 0-3.
+
+ properties:
+ reg:
+ minimum: 0
+ maximum: 7
+ description: The channel number
+
+ adi,prechargebuf-pos-enable:
+ type: boolean
+ description: Enable positive input precharge buffer
+
+ adi,prechargebuf-neg-enable:
+ type: boolean
+ description: Enable negative input precharge buffer
+
+ adi,refbuf-pos-enable:
+ type: boolean
+ description: Enable positive reference buffer
+
+ adi,refbuf-neg-enable:
+ type: boolean
+ description: Enable negative reference buffer
+
+ required:
+ - reg
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - avdd1-supply
+ - avdd2-supply
+ - iovdd-supply
+ - ref1p-supply
+ - ref2p-supply
+ - io-backends
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: adi,ad7768-4
+ then:
+ properties:
+ adi,data-lines-number:
+ enum: [1, 4]
+ patternProperties:
+ "^channel@[0-3]$":
+ properties:
+ reg:
+ maximum: 3
+ "^channel@[4-7]$": false
+ else:
+ properties:
+ adi,data-lines-number:
+ enum: [1, 2, 8]
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "adi,ad7768";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+
+ avdd1-supply = <&avdd1_regulator>;
+ avss-supply = <&avss_regulator>;
+ avdd2-supply = <&avdd2_regulator>;
+ iovdd-supply = <&iovdd_regulator>;
+ ref1p-supply = <&ref1p_regulator>;
+ ref1n-supply = <&ref1n_regulator>;
+ ref2p-supply = <&ref2p_regulator>;
+ ref2n-supply = <&ref2n_regulator>;
+ reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>;
+
+ clocks = <&ad7768_mclk>;
+ clock-names = "mclk";
+
+ adi,data-lines-number = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ io-backends = <&iio_backend>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@0 {
+ reg = <0>;
+ adi,prechargebuf-pos-enable;
+ adi,refbuf-pos-enable;
+ };
+
+ channel@1 {
+ reg = <1>;
+ adi,prechargebuf-pos-enable;
+ adi,prechargebuf-neg-enable;
+ adi,refbuf-pos-enable;
+ adi,refbuf-neg-enable;
+ };
+
+ channel@7 {
+ reg = <7>;
+ adi,prechargebuf-pos-enable;
+ adi,refbuf-pos-enable;
+ };
+ };
+ };
+
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..f05e7f4d364a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1657,6 +1657,13 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
F: drivers/iio/adc/ad7768-1.c
F: include/dt-bindings/iio/adc/adi,ad7768-1.h
+ANALOG DEVICES INC AD7768 DRIVER
+M: Janani Sunil <janani.sunil@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
+
ANALOG DEVICES INC AD7780 DRIVER
M: Michael Hennerich <Michael.Hennerich@analog.com>
M: Renato Lui Geh <renatogeh@gmail.com>
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 03/17] iio: backend: Add support for CRC
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
2026-09-04 14:14 ` [PATCH v6 01/17] iio: adc: adi-axi-adc: Initialize state mutex Janani Sunil
2026-09-04 14:14 ` [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768 Janani Sunil
@ 2026-09-04 14:14 ` Janani Sunil
2026-09-04 14:14 ` [PATCH v6 04/17] iio: adc: adi-axi-adc: " Janani Sunil
` (13 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Add a backend operation to enable or disable direction-specific CRC
handling. On input data paths, the backend handles CRC data generated by
the frontend. On output data paths, the backend generates CRC data for
the frontend to verify.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/industrialio-backend.c | 33 +++++++++++++++++++++++++++++++++
include/linux/iio/backend.h | 6 ++++++
2 files changed, 39 insertions(+)
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index f7a4be8ec320..fbf5c39d7655 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -886,6 +886,39 @@ int iio_backend_num_lanes_set(struct iio_backend *back, unsigned int num_lanes)
}
EXPORT_SYMBOL_NS_GPL(iio_backend_num_lanes_set, "IIO_BACKEND");
+/**
+ * iio_backend_crc_enable - Enable CRC handling
+ * @back: Backend device
+ *
+ * On input data paths, enable backend handling of CRC data generated by the
+ * frontend. On output data paths, enable the backend to generate CRC data for
+ * the frontend to verify.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_crc_enable(struct iio_backend *back)
+{
+ return iio_backend_op_call(back, crc_enable);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_crc_enable, "IIO_BACKEND");
+
+/**
+ * iio_backend_crc_disable - Disable CRC handling
+ * @back: Backend device
+ *
+ * On input data paths, disable backend handling of CRC data generated by the
+ * frontend. On output data paths, disable CRC generation by the backend.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_crc_disable(struct iio_backend *back)
+{
+ return iio_backend_op_call(back, crc_disable);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_crc_disable, "IIO_BACKEND");
+
/**
* iio_backend_ddr_enable - Enable interface DDR (Double Data Rate) mode
* @back: Backend device
diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h
index 3f95ed1fdf9e..9c16b695a602 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -133,6 +133,8 @@ enum iio_backend_capabilities {
* @filter_type_set: Set filter type.
* @interface_data_align: Perform the data alignment process.
* @num_lanes_set: Set the number of lanes enabled.
+ * @crc_enable: Enable direction-specific CRC handling.
+ * @crc_disable: Disable direction-specific CRC handling.
* @ddr_enable: Enable interface DDR (Double Data Rate) mode.
* @ddr_disable: Disable interface DDR (Double Data Rate) mode.
* @data_stream_enable: Enable data stream.
@@ -189,6 +191,8 @@ struct iio_backend_ops {
enum iio_backend_filter_type type);
int (*interface_data_align)(struct iio_backend *back, u32 timeout_us);
int (*num_lanes_set)(struct iio_backend *back, unsigned int num_lanes);
+ int (*crc_enable)(struct iio_backend *back);
+ int (*crc_disable)(struct iio_backend *back);
int (*ddr_enable)(struct iio_backend *back);
int (*ddr_disable)(struct iio_backend *back);
int (*data_stream_enable)(struct iio_backend *back);
@@ -237,6 +241,8 @@ int iio_backend_filter_type_set(struct iio_backend *back,
enum iio_backend_filter_type type);
int iio_backend_interface_data_align(struct iio_backend *back, u32 timeout_us);
int iio_backend_num_lanes_set(struct iio_backend *back, unsigned int num_lanes);
+int iio_backend_crc_enable(struct iio_backend *back);
+int iio_backend_crc_disable(struct iio_backend *back);
int iio_backend_ddr_enable(struct iio_backend *back);
int iio_backend_ddr_disable(struct iio_backend *back);
int iio_backend_data_stream_enable(struct iio_backend *back);
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 04/17] iio: adc: adi-axi-adc: Add support for CRC
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (2 preceding siblings ...)
2026-09-04 14:14 ` [PATCH v6 03/17] iio: backend: Add support for CRC Janani Sunil
@ 2026-09-04 14:14 ` Janani Sunil
2026-09-04 14:24 ` sashiko-bot
2026-09-04 14:14 ` [PATCH v6 05/17] iio: adc: Add AD7768 and AD7768-4 core support Janani Sunil
` (12 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Add support for enabling and disabling Cyclic Redundancy Check (CRC)
processing in the AXI ADC backend. CRC provides data integrity verification
for high-speed ADC data streams, ensuring reliable data transfer between
the ADC frontend and backend processing systems.
Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/adi-axi-adc.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index fc7cc92b35f0..8b7c45d51dbe 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -47,6 +47,9 @@
#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
#define ADI_AXI_ADC_REG_CNTRL_3 0x004c
+/* Bit 8 is common to all AXI ADC cores; bits 7-0 are per-HDL-project. */
+#define ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK BIT(8)
+
#define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2)
#define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0)
#define AXI_AD485X_PACKET_FORMAT_20BIT 0x0
@@ -596,6 +599,22 @@ static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg,
return regmap_write(st->regmap, reg, writeval);
}
+static int axi_adc_crc_enable(struct iio_backend *back)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK);
+}
+
+static int axi_adc_crc_disable(struct iio_backend *back)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK);
+}
+
static const struct regmap_config axi_adc_regmap_config = {
.val_bits = 32,
.reg_bits = 32,
@@ -644,6 +663,8 @@ static const struct iio_backend_ops adi_axi_adc_ops = {
.num_lanes_set = axi_adc_num_lanes_set,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
.debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
+ .crc_enable = axi_adc_crc_enable,
+ .crc_disable = axi_adc_crc_disable,
};
static const struct iio_backend_info adi_axi_adc_generic = {
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 05/17] iio: adc: Add AD7768 and AD7768-4 core support
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (3 preceding siblings ...)
2026-09-04 14:14 ` [PATCH v6 04/17] iio: adc: adi-axi-adc: " Janani Sunil
@ 2026-09-04 14:14 ` Janani Sunil
2026-09-04 14:31 ` sashiko-bot
2026-09-04 14:14 ` [PATCH v6 06/17] iio: adc: ad7768: Validate master clock rate Janani Sunil
` (11 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Add core support for the AD7768 and AD7768-4 simultaneous sampling ADCs.
Configure supplies, clock and reset, use a custom regmap bus for the SPI
protocol, and parse the enabled channels and input buffer settings from
devicetree.
Connect the converter to an IIO backend for buffered capture with CRC,
provide a fixed safe wideband sampling configuration and add runtime
power management.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
MAINTAINERS | 1 +
drivers/iio/adc/Kconfig | 14 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ad7768.c | 1000 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1016 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index f05e7f4d364a..de91903b0b45 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1663,6 +1663,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
+F: drivers/iio/adc/ad7768.c
ANALOG DEVICES INC AD7780 DRIVER
M: Michael Hennerich <Michael.Hennerich@analog.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 415e519ad4eb..5164608fa418 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -431,6 +431,20 @@ config AD7766
To compile this driver as a module, choose M here: the module will be
called ad7766.
+config AD7768
+ tristate "Analog Devices AD7768/AD7768-4 ADC driver"
+ depends on SPI
+ depends on REGULATOR || COMPILE_TEST
+ select IIO_BUFFER
+ select IIO_BACKEND
+ select REGMAP
+ help
+ Say yes here to build support for Analog Devices AD7768 and AD7768-4
+ SPI analog to digital converters.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7768.
+
config AD7768_1
tristate "Analog Devices AD7768-1 ADC driver"
depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index dcec0abb03b7..c84f1a5eeac0 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
obj-$(CONFIG_AD7606) += ad7606.o
obj-$(CONFIG_AD7625) += ad7625.o
obj-$(CONFIG_AD7766) += ad7766.o
+obj-$(CONFIG_AD7768) += ad7768.o
obj-$(CONFIG_AD7768_1) += ad7768-1.o
obj-$(CONFIG_AD7779) += ad7779.o
obj-$(CONFIG_AD7780) += ad7780.o
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
new file mode 100644
index 000000000000..c80056a9edd8
--- /dev/null
+++ b/drivers/iio/adc/ad7768.c
@@ -0,0 +1,1000 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7768 ADC driver
+ *
+ * Copyright 2018-2026 Analog Devices Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
+
+#define AD7768_REG_CH_STANDBY 0x00
+
+#define AD7768_REG_CH_MODE(x) (0x01 + (x))
+#define AD7768_CH_MODE_FILTER_TYPE_MSK BIT(3)
+#define AD7768_CH_MODE_FILTER_TYPE_WIDEBAND 0x0
+#define AD7768_CH_MODE_DEC_RATE_MSK GENMASK(2, 0)
+#define AD7768_CH_MODE_DEC_RATE_64 0x1
+
+#define AD7768_REG_CH_MODE_SEL 0x03
+
+#define AD7768_REG_POWER_MODE 0x04
+#define AD7768_SLEEP_MODE_MSK BIT(7)
+#define AD7768_POWER_MODE_POWER_MODE_MSK GENMASK(5, 4)
+#define AD7768_POWER_MODE_POWER_MODE_FAST 0x3
+#define AD7768_POWER_MODE_LVDS_ENABLE BIT(3)
+#define AD7768_POWER_MODE_MCLK_DIV_MSK GENMASK(1, 0)
+
+#define AD7768_REG_DATA_CONTROL 0x06
+#define AD7768_DATA_CONTROL_SPI_RESET_1 0x03
+#define AD7768_DATA_CONTROL_SPI_RESET_2 0x02
+#define AD7768_DATA_CONTROL_SPI_SYNC BIT(7)
+
+#define AD7768_REG_INTERFACE_CFG 0x07
+#define AD7768_INTERFACE_CFG_CRC_SELECT_MSK GENMASK(3, 2)
+#define AD7768_INTERFACE_CFG_CRC_SELECT_4 0x1
+#define AD7768_INTERFACE_CFG_DCLK_DIV_MSK GENMASK(1, 0)
+#define AD7768_INTERFACE_CFG_DCLK_DIV(x) (4 - ffs(x))
+
+#define AD7768_REG_REV_ID 0x0A
+#define AD7768_REV_ID_VAL 0x06
+
+#define AD7768_REG_GPIO_CONTROL 0x0E
+#define AD7768_GPIO_UGPIO_ENABLE BIT(7)
+#define AD7768_GPIO4_OUTPUT_ENABLE BIT(4)
+
+#define AD7768_REG_GPIO_WRITE 0x0F
+
+#define AD7768_REG_PRECHARGE_BUF1 0x11
+#define AD7768_REG_PRECHARGE_BUF2 0x12
+#define AD7768_PREBUF_POS_EN(ch) BIT((ch) * 2)
+#define AD7768_PREBUF_NEG_EN(ch) BIT(((ch) * 2) + 1)
+
+#define AD7768_REG_REFP_BUF 0x13
+#define AD7768_REG_REFN_BUF 0x14
+
+#define AD7768_REG_OFFSET_BASE 0x1E
+#define AD7768_REG_GAIN_BASE 0x36
+#define AD7768_REG_PHASE_BASE 0x4E
+#define AD7768_REG_OFFSET(ch) (AD7768_REG_OFFSET_BASE + (3 * (ch)))
+#define AD7768_REG_GAIN(ch) (AD7768_REG_GAIN_BASE + (3 * (ch)))
+#define AD7768_REG_PHASE(ch) (AD7768_REG_PHASE_BASE + (ch))
+#define __AD7768_4_REG_MAP(ch) ((ch) < 2 ? (ch) : ((ch) + 2))
+#define AD7768_4_REG_OFFSET(ch) \
+ (AD7768_REG_OFFSET_BASE + (3 * __AD7768_4_REG_MAP(ch)))
+#define AD7768_4_REG_GAIN(ch) \
+ (AD7768_REG_GAIN_BASE + (3 * __AD7768_4_REG_MAP(ch)))
+#define AD7768_4_REG_PHASE(ch) \
+ (AD7768_REG_PHASE_BASE + __AD7768_4_REG_MAP(ch))
+
+#define AD7768_REG_DIAGNOSTIC_RX 0x56
+
+#define AD7768_REG_CHOP_CTRL 0x59
+
+#define AD7768_SPI_READ_CMD BIT(15)
+#define AD7768_SPI_REG_MASK GENMASK(14, 8)
+#define AD7768_SPI_DATA_MASK GENMASK(7, 0)
+
+#define AD7768_MAX_CHANNEL 8
+
+enum ad7768_clock_source {
+ AD7768_CLOCK_SOURCE_MCLK,
+ AD7768_CLOCK_SOURCE_XTAL,
+ AD7768_CLOCK_SOURCE_LVDS,
+};
+
+struct ad7768_precharge_config {
+ bool prebufp_en;
+ bool prebufn_en;
+ bool refbufp;
+ bool refbufn;
+};
+
+struct ad7768_chip_info {
+ const char *name;
+ unsigned int num_channels;
+ const struct regmap_config *regmap_config;
+ const unsigned int *available_datalines;
+ unsigned int num_datalines;
+ const u8 *chan_map;
+ u8 prebuf_split;
+};
+
+struct ad7768_state {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int datalines;
+ enum ad7768_clock_source clock_source;
+ const struct ad7768_chip_info *chip_info;
+ struct iio_backend *back;
+ unsigned int vref_uV[2];
+
+ __be16 d16 __aligned(IIO_DMA_MINALIGN);
+};
+
+static const unsigned int ad7768_available_datalines[] = {
+ 1, 2, 8,
+};
+
+static const unsigned int ad7768_4_available_datalines[] = {
+ 1, 4,
+};
+
+static const u8 ad7768_chan_map[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+};
+
+static const u8 ad7768_4_chan_map[] = {
+ 0, 1, 4, 5,
+};
+
+static const char * const ad7768_supply_names[] = {
+ "avdd2", "iovdd",
+};
+
+static const char * const ad7768_vref_supply_names[][2] = {
+ { "ref1p", "ref1n" },
+ { "ref2p", "ref2n" },
+};
+
+static const char * const ad7768_clock_names[] = {
+ [AD7768_CLOCK_SOURCE_MCLK] = "mclk",
+ [AD7768_CLOCK_SOURCE_XTAL] = "xtal",
+ [AD7768_CLOCK_SOURCE_LVDS] = "lvds",
+};
+
+static u8 ad7768_channel_mask(const struct ad7768_state *st, u8 ch)
+{
+ return BIT(st->chip_info->chan_map[ch]);
+}
+
+static u8 ad7768_all_standby_mask(const struct ad7768_state *st)
+{
+ return GENMASK(st->chip_info->num_channels - 1, 0);
+}
+
+static u8 ad7768_precharge_buf1_mask(const struct ad7768_state *st, u16 val)
+{
+ return val & GENMASK(st->chip_info->prebuf_split - 1, 0);
+}
+
+static u8 ad7768_precharge_buf2_mask(const struct ad7768_state *st, u16 val)
+{
+ return (val >> st->chip_info->prebuf_split) &
+ GENMASK(st->chip_info->prebuf_split - 1, 0);
+}
+
+static int ad7768_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct ad7768_state *st = spi_get_drvdata(context);
+ struct spi_device *spi = context;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d16,
+ .len = sizeof(st->d16),
+ .cs_change = 1,
+ }, {
+ /*
+ * Register responses are delayed by one CS frame. While
+ * receiving the response to this read, the device also
+ * decodes another command on SDI. Repeat the read
+ * command to avoid sending an unspecified dummy
+ * command.
+ */
+ .tx_buf = &st->d16,
+ .rx_buf = &st->d16,
+ .len = sizeof(st->d16),
+ },
+ };
+ u8 *data_val = val_buf;
+ unsigned int reg;
+ int ret;
+
+ reg = *(const u8 *)reg_buf;
+
+ st->d16 = be16_replace_bits(cpu_to_be16(AD7768_SPI_READ_CMD), reg,
+ AD7768_SPI_REG_MASK);
+
+ ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+ if (ret)
+ return ret;
+
+ *data_val = be16_get_bits(st->d16, AD7768_SPI_DATA_MASK);
+
+ return 0;
+}
+
+static int ad7768_regmap_write(void *context, const void *data, size_t count)
+{
+ struct spi_device *spi = context;
+
+ return spi_write(spi, data, count);
+}
+
+static const struct regmap_bus ad7768_regmap_bus = {
+ .read = ad7768_regmap_read,
+ .write = ad7768_regmap_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static bool ad7768_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AD7768_REG_CH_STANDBY ... AD7768_REG_REV_ID:
+ case AD7768_REG_GPIO_CONTROL ... AD7768_REG_REFN_BUF:
+ case AD7768_REG_OFFSET(0) ... AD7768_REG_OFFSET(7) + 2:
+ case AD7768_REG_GAIN(0) ... AD7768_REG_GAIN(7) + 2:
+ case AD7768_REG_PHASE(0) ... AD7768_REG_PHASE(7):
+ case AD7768_REG_DIAGNOSTIC_RX ... AD7768_REG_CHOP_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ad7768_4_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AD7768_REG_CH_STANDBY ... AD7768_REG_REV_ID:
+ case AD7768_REG_GPIO_CONTROL ... AD7768_REG_REFN_BUF:
+ case AD7768_4_REG_OFFSET(0) ... AD7768_4_REG_OFFSET(1) + 2:
+ case AD7768_4_REG_OFFSET(2) ... AD7768_4_REG_OFFSET(3) + 2:
+ case AD7768_4_REG_GAIN(0) ... AD7768_4_REG_GAIN(1) + 2:
+ case AD7768_4_REG_GAIN(2) ... AD7768_4_REG_GAIN(3) + 2:
+ case AD7768_4_REG_PHASE(0) ... AD7768_4_REG_PHASE(1):
+ case AD7768_4_REG_PHASE(2) ... AD7768_4_REG_PHASE(3):
+ case AD7768_REG_DIAGNOSTIC_RX ... AD7768_REG_CHOP_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config ad7768_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AD7768_REG_CHOP_CTRL,
+ /*
+ * Regmap bulk transfers use one starting address, while each register
+ * access requires a separate 16-bit SPI frame and reads use an off-frame
+ * response. Split bulk transfers into individual register accesses.
+ */
+ .use_single_read = true,
+ .use_single_write = true,
+ .readable_reg = ad7768_readable_reg,
+};
+
+static const struct regmap_config ad7768_4_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AD7768_REG_CHOP_CTRL,
+ /*
+ * Regmap bulk transfers use one starting address, while each register
+ * access requires a separate 16-bit SPI frame and reads use an off-frame
+ * response. Split bulk transfers into individual register accesses.
+ */
+ .use_single_read = true,
+ .use_single_write = true,
+ .readable_reg = ad7768_4_readable_reg,
+};
+
+static int ad7768_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int ad7768_sync(struct ad7768_state *st)
+{
+ int ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_DATA_CONTROL,
+ AD7768_DATA_CONTROL_SPI_SYNC);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(st->regmap, AD7768_REG_DATA_CONTROL,
+ AD7768_DATA_CONTROL_SPI_SYNC);
+}
+
+static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned long channel_mask;
+ unsigned long standby_mask;
+ int ret;
+
+ channel_mask = ad7768_all_standby_mask(st);
+ standby_mask = channel_mask & ~*scan_mask;
+
+ /*
+ * Crystal excitation requires channel 4 on AD7768 or channel 2 on
+ * AD7768-4 to remain active.
+ */
+ if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL)
+ __clear_bit(st->chip_info->num_channels / 2, &standby_mask);
+
+ ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY,
+ channel_mask, standby_mask);
+ if (ret)
+ return ret;
+
+ for (unsigned int c = 0; c < st->chip_info->num_channels; c++) {
+ if (test_bit(c, scan_mask))
+ ret = iio_backend_chan_enable(st->back, c);
+ else
+ ret = iio_backend_chan_disable(st->back, c);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct ad7768_chip_info ad7768_chip_info = {
+ .name = "ad7768",
+ .num_channels = 8,
+ .regmap_config = &ad7768_regmap_config,
+ .available_datalines = ad7768_available_datalines,
+ .num_datalines = ARRAY_SIZE(ad7768_available_datalines),
+ .chan_map = ad7768_chan_map,
+ .prebuf_split = 8,
+};
+
+static const struct ad7768_chip_info ad7768_4_chip_info = {
+ .name = "ad7768-4",
+ .num_channels = 4,
+ .regmap_config = &ad7768_4_regmap_config,
+ .available_datalines = ad7768_4_available_datalines,
+ .num_datalines = ARRAY_SIZE(ad7768_4_available_datalines),
+ .chan_map = ad7768_4_chan_map,
+ .prebuf_split = 4,
+};
+
+static int ad7768_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ return pm_runtime_resume_and_get(regmap_get_device(st->regmap));
+}
+
+static int ad7768_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ pm_runtime_put_autosuspend(regmap_get_device(st->regmap));
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
+ .preenable = ad7768_buffer_preenable,
+ .postdisable = ad7768_buffer_postdisable,
+};
+
+static int ad7768_read_scale(struct ad7768_state *st,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2)
+{
+ unsigned int vref_idx;
+
+ vref_idx = chan->channel >= st->chip_info->num_channels / 2;
+ *val = 2 * st->vref_uV[vref_idx] / (MICRO / MILLI);
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+}
+
+static int ad7768_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ return ad7768_read_scale(st, chan, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad7768_info = {
+ .debugfs_reg_access = ad7768_reg_access,
+ .read_raw = ad7768_read_raw,
+ .update_scan_mode = ad7768_update_scan_mode,
+};
+
+static int ad7768_configure_precharge_buffers(struct iio_dev *indio_dev,
+ struct ad7768_precharge_config *precharge_cfg)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ u8 prebuf1_val, prebuf2_val;
+ u16 prebuf_mask = 0;
+ u8 refbufp_val = 0;
+ u8 refbufn_val = 0;
+ int ret;
+
+ for (unsigned int ch = 0; ch < indio_dev->num_channels; ch++) {
+ u8 channel = indio_dev->channels[ch].channel;
+
+ if (precharge_cfg[channel].prebufp_en)
+ prebuf_mask |= AD7768_PREBUF_POS_EN(channel);
+
+ if (precharge_cfg[channel].prebufn_en)
+ prebuf_mask |= AD7768_PREBUF_NEG_EN(channel);
+
+ if (precharge_cfg[channel].refbufp)
+ refbufp_val |= ad7768_channel_mask(st, channel);
+
+ if (precharge_cfg[channel].refbufn)
+ refbufn_val |= ad7768_channel_mask(st, channel);
+ }
+
+ prebuf1_val = ad7768_precharge_buf1_mask(st, ~prebuf_mask);
+ prebuf2_val = ad7768_precharge_buf2_mask(st, ~prebuf_mask);
+
+ ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF1, prebuf1_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF2, prebuf2_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_REFP_BUF, refbufp_val);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, AD7768_REG_REFN_BUF, refbufn_val);
+}
+
+static int ad7768_configure_capture(struct ad7768_state *st)
+{
+ unsigned int dclk_div_reg;
+ unsigned int mode_config;
+ unsigned int dclk_div;
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_POWER_MODE_POWER_MODE_MSK |
+ AD7768_POWER_MODE_MCLK_DIV_MSK,
+ FIELD_PREP(AD7768_POWER_MODE_POWER_MODE_MSK,
+ AD7768_POWER_MODE_POWER_MODE_FAST) |
+ FIELD_PREP(AD7768_POWER_MODE_MCLK_DIV_MSK,
+ AD7768_POWER_MODE_POWER_MODE_FAST));
+ if (ret)
+ return ret;
+
+ /*
+ * Start with the wideband filter and a decimation rate of 64. This
+ * supports every valid data-line configuration at the maximum MCLK.
+ */
+ mode_config = FIELD_PREP(AD7768_CH_MODE_FILTER_TYPE_MSK,
+ AD7768_CH_MODE_FILTER_TYPE_WIDEBAND) |
+ FIELD_PREP(AD7768_CH_MODE_DEC_RATE_MSK,
+ AD7768_CH_MODE_DEC_RATE_64);
+ ret = regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(0),
+ AD7768_CH_MODE_FILTER_TYPE_MSK |
+ AD7768_CH_MODE_DEC_RATE_MSK,
+ mode_config);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_CH_MODE_SEL, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * DCLK(min) is ODR * channels per DOUTx * 32. With fast mode
+ * (fMOD = MCLK / 4) and x64 decimation, this gives:
+ * MCLK / DCLK = 8 * data lines / channels.
+ */
+ dclk_div = 8 * st->datalines / st->chip_info->num_channels;
+ dclk_div_reg = AD7768_INTERFACE_CFG_DCLK_DIV(dclk_div);
+ ret = regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG,
+ AD7768_INTERFACE_CFG_DCLK_DIV_MSK,
+ FIELD_PREP(AD7768_INTERFACE_CFG_DCLK_DIV_MSK,
+ dclk_div_reg));
+ if (ret)
+ return ret;
+
+ return ad7768_sync(st);
+}
+
+static int ad7768_parse_config(struct iio_dev *indio_dev,
+ struct device *dev)
+{
+ struct ad7768_precharge_config precharge_cfg[AD7768_MAX_CHANNEL] = { };
+ struct ad7768_state *st = iio_priv(indio_dev);
+ const unsigned int *available_datalines;
+ bool datalines_valid = false;
+ struct iio_chan_spec *chan;
+ unsigned int num_channels;
+ unsigned long standby_mask;
+ unsigned int len;
+ int chan_idx = 0;
+ int ret;
+
+ num_channels = device_get_named_child_node_count(dev, "channel");
+
+ if (num_channels == 0)
+ return dev_err_probe(dev, -ENOENT, "No channel specified\n");
+
+ if (num_channels > st->chip_info->num_channels)
+ return dev_err_probe(dev, -ENOSPC, "Invalid number of channels\n");
+
+ chan = devm_kcalloc(dev, num_channels, sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ indio_dev->channels = chan;
+ indio_dev->num_channels = num_channels;
+
+ standby_mask = ad7768_all_standby_mask(st);
+
+ /*
+ * Crystal excitation requires channel 4 on AD7768 or channel 2 on
+ * AD7768-4 to remain active.
+ */
+ if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL)
+ __clear_bit(st->chip_info->num_channels / 2, &standby_mask);
+
+ ret = regmap_write(st->regmap, AD7768_REG_CH_STANDBY, standby_mask);
+ if (ret)
+ return ret;
+
+ device_for_each_named_child_node_scoped(dev, child, "channel") {
+ u32 channel;
+
+ ret = fwnode_property_read_u32(child, "reg", &channel);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to parse reg of %pfwP\n",
+ child);
+
+ if (channel >= st->chip_info->num_channels)
+ return dev_err_probe(dev, -ECHRNG,
+ "Invalid channel %u in firmware\n",
+ channel);
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_CH_STANDBY,
+ BIT(channel));
+ if (ret)
+ return ret;
+
+ precharge_cfg[channel].prebufp_en =
+ fwnode_property_read_bool(child,
+ "adi,prechargebuf-pos-enable");
+ precharge_cfg[channel].prebufn_en =
+ fwnode_property_read_bool(child,
+ "adi,prechargebuf-neg-enable");
+ precharge_cfg[channel].refbufp =
+ fwnode_property_read_bool(child,
+ "adi,refbuf-pos-enable");
+ precharge_cfg[channel].refbufn =
+ fwnode_property_read_bool(child,
+ "adi,refbuf-neg-enable");
+
+ chan[chan_idx] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = channel,
+ .scan_index = channel,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ };
+ chan_idx++;
+ }
+
+ ret = ad7768_configure_precharge_buffers(indio_dev, precharge_cfg);
+ if (ret)
+ return ret;
+
+ available_datalines = st->chip_info->available_datalines;
+ len = st->chip_info->num_datalines;
+ if (device_property_present(dev, "adi,data-lines-number")) {
+ ret = device_property_read_u32(dev, "adi,data-lines-number",
+ &st->datalines);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Invalid adi,data-lines-number property\n");
+ } else {
+ st->datalines = available_datalines[len - 1];
+ }
+
+ for (unsigned int i = 0; i < len; i++) {
+ if (available_datalines[i] == st->datalines) {
+ datalines_valid = true;
+ break;
+ }
+ }
+
+ if (!datalines_valid)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid data-lines-number %d for %s\n",
+ st->datalines, st->chip_info->name);
+
+ return ad7768_configure_capture(st);
+}
+
+static int ad7768_reset(struct ad7768_state *st)
+{
+ struct device *dev = regmap_get_device(st->regmap);
+ struct reset_control *reset_ctrl;
+ unsigned long reset_low_us;
+ unsigned long mclk;
+ int ret;
+
+ reset_ctrl = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(reset_ctrl))
+ return PTR_ERR(reset_ctrl);
+
+ if (reset_ctrl) {
+ mclk = clk_get_rate(st->mclk);
+ if (!mclk)
+ return -EINVAL;
+
+ /*
+ * Minimum RESET low pulse width: 2 x tMCLK
+ * (datasheet Table 1).
+ */
+ reset_low_us = DIV_ROUND_UP_ULL(2ULL * USEC_PER_SEC, mclk);
+
+ ret = reset_control_assert(reset_ctrl);
+ if (ret)
+ return ret;
+
+ fsleep(max(1UL, reset_low_us));
+
+ ret = reset_control_deassert(reset_ctrl);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_write(st->regmap, AD7768_REG_DATA_CONTROL,
+ AD7768_DATA_CONTROL_SPI_RESET_1);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_DATA_CONTROL,
+ AD7768_DATA_CONTROL_SPI_RESET_2);
+ if (ret)
+ return ret;
+ }
+
+ /* ADC start-up time after reset: 1.66 ms max (datasheet Table 1) */
+ fsleep(1660);
+
+ return 0;
+}
+
+static void ad7768_disable_clk(void *clk)
+{
+ clk_disable_unprepare(clk);
+}
+
+static int ad7768_configure_xtal_clock(struct ad7768_state *st)
+{
+ int ret;
+
+ ret = regmap_set_bits(st->regmap, AD7768_REG_GPIO_WRITE, BIT(4));
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(st->regmap, AD7768_REG_GPIO_CONTROL,
+ AD7768_GPIO_UGPIO_ENABLE |
+ AD7768_GPIO4_OUTPUT_ENABLE);
+}
+
+static int ad7768_enable_lvds_clock(struct ad7768_state *st)
+{
+ struct device *dev = regmap_get_device(st->regmap);
+ int ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_GPIO_WRITE, BIT(4));
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(st->regmap, AD7768_REG_GPIO_CONTROL,
+ AD7768_GPIO_UGPIO_ENABLE |
+ AD7768_GPIO4_OUTPUT_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_POWER_MODE_LVDS_ENABLE);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(st->mclk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, ad7768_disable_clk, st->mclk);
+}
+
+static int ad7768_get_enable_vref(struct device *dev, unsigned int index)
+{
+ const char * const *supply = ad7768_vref_supply_names[index];
+ int refp_uV;
+ int refn_uV;
+
+ refp_uV = devm_regulator_get_enable_read_voltage(dev, supply[0]);
+ if (refp_uV < 0)
+ return dev_err_probe(dev, refp_uV,
+ "Failed to get %s supply voltage\n", supply[0]);
+
+ refn_uV = devm_regulator_get_enable_read_voltage(dev, supply[1]);
+ if (refn_uV == -ENODEV)
+ refn_uV = 0;
+ else if (refn_uV < 0)
+ return dev_err_probe(dev, refn_uV,
+ "Failed to get %s supply voltage\n", supply[1]);
+
+ if (refp_uV <= refn_uV)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid reference %u voltage\n", index + 1);
+
+ return refp_uV - refn_uV;
+}
+
+static int ad7768_probe(struct spi_device *spi)
+{
+ unsigned int spi_readback, rev_id;
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ad7768_state *st;
+ const char *clock_name;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, st);
+
+ st->chip_info = spi_get_device_match_data(spi);
+ if (!st->chip_info)
+ return dev_err_probe(dev, -ENODATA, "Failed to get match data\n");
+
+ ret = devm_regulator_get_enable_optional(dev, "avss");
+ if (ret == -ENODEV) {
+ /* AVSS may be tied directly to ground instead of a regulator. */
+ } else if (ret) {
+ return dev_err_probe(dev, ret,
+ "Failed to enable AVSS supply\n");
+ }
+
+ ret = devm_regulator_get_enable(dev, "avdd1");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable AVDD1 supply\n");
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(ad7768_supply_names),
+ ad7768_supply_names);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(ad7768_vref_supply_names); i++) {
+ ret = ad7768_get_enable_vref(dev, i);
+ if (ret < 0)
+ return ret;
+
+ st->vref_uV[i] = ret;
+ }
+
+ ret = device_property_match_property_string(dev, "clock-names",
+ ad7768_clock_names,
+ ARRAY_SIZE(ad7768_clock_names));
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Invalid clock source\n");
+
+ st->clock_source = ret;
+ clock_name = ad7768_clock_names[st->clock_source];
+
+ /*
+ * The device must start on its internal clock. Keep the LVDS clock
+ * disabled until GPIO4 is driven low and the LVDS input is enabled in
+ * the power mode register.
+ */
+ switch (st->clock_source) {
+ case AD7768_CLOCK_SOURCE_MCLK:
+ case AD7768_CLOCK_SOURCE_XTAL:
+ st->mclk = devm_clk_get_enabled(dev, clock_name);
+ break;
+ case AD7768_CLOCK_SOURCE_LVDS:
+ st->mclk = devm_clk_get(dev, clock_name);
+ break;
+ }
+
+ if (IS_ERR(st->mclk))
+ return dev_err_probe(dev, PTR_ERR(st->mclk),
+ "Failed to get master clock\n");
+
+ st->regmap = devm_regmap_init(dev, &ad7768_regmap_bus, spi,
+ st->chip_info->regmap_config);
+ if (IS_ERR(st->regmap))
+ return PTR_ERR(st->regmap);
+
+ ret = ad7768_reset(st);
+ if (ret)
+ return ret;
+
+ /* Discard the reset response with a dummy SPI register read. */
+ ret = regmap_read(st->regmap, AD7768_REG_REV_ID, &spi_readback);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, AD7768_REG_REV_ID, &rev_id);
+ if (ret)
+ return ret;
+
+ if (rev_id != AD7768_REV_ID_VAL)
+ dev_info(dev, "Unexpected revision ID 0x%02x\n", rev_id);
+
+ switch (st->clock_source) {
+ case AD7768_CLOCK_SOURCE_MCLK:
+ break;
+ case AD7768_CLOCK_SOURCE_XTAL:
+ ret = ad7768_configure_xtal_clock(st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to configure crystal clock\n");
+ break;
+ case AD7768_CLOCK_SOURCE_LVDS:
+ ret = ad7768_enable_lvds_clock(st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable LVDS clock\n");
+ break;
+ }
+
+ ret = ad7768_parse_config(indio_dev, dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Hardware supports CRC every 4 or 16 samples; the backend supports only
+ * 4-sample CRC.
+ */
+ ret = regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG,
+ AD7768_INTERFACE_CFG_CRC_SELECT_MSK,
+ FIELD_PREP(AD7768_INTERFACE_CFG_CRC_SELECT_MSK,
+ AD7768_INTERFACE_CFG_CRC_SELECT_4));
+ if (ret)
+ return ret;
+
+ indio_dev->name = st->chip_info->name;
+ indio_dev->info = &ad7768_info;
+ indio_dev->setup_ops = &ad7768_buffer_ops;
+
+ st->back = devm_iio_backend_get(dev, NULL);
+ if (IS_ERR(st->back))
+ return PTR_ERR(st->back);
+
+ ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_num_lanes_set(st->back, st->datalines);
+ if (ret)
+ return ret;
+
+ ret = iio_backend_crc_enable(st->back);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_backend_enable(dev, st->back);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 2000);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int ad7768_runtime_suspend(struct device *dev)
+{
+ struct ad7768_state *st = dev_get_drvdata(dev);
+
+ return regmap_set_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_SLEEP_MODE_MSK);
+}
+
+static int ad7768_runtime_resume(struct device *dev)
+{
+ struct ad7768_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_SLEEP_MODE_MSK);
+ if (ret)
+ return ret;
+
+ /*
+ * The datasheet does not specify a wake-up time. Allow 20 ms for the
+ * ADC and digital clocks to restart.
+ */
+ fsleep(20 * USEC_PER_MSEC);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ad7768_pm_ops, ad7768_runtime_suspend,
+ ad7768_runtime_resume, NULL);
+
+static const struct of_device_id ad7768_of_match[] = {
+ { .compatible = "adi,ad7768", .data = &ad7768_chip_info },
+ { .compatible = "adi,ad7768-4", .data = &ad7768_4_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7768_of_match);
+
+static const struct spi_device_id ad7768_spi_id[] = {
+ { "ad7768", (kernel_ulong_t)&ad7768_chip_info },
+ { "ad7768-4", (kernel_ulong_t)&ad7768_4_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7768_spi_id);
+
+static struct spi_driver ad7768_driver = {
+ .probe = ad7768_probe,
+ .driver = {
+ .name = "ad7768",
+ .of_match_table = ad7768_of_match,
+ .pm = pm_ptr(&ad7768_pm_ops),
+ },
+ .id_table = ad7768_spi_id,
+};
+module_spi_driver(ad7768_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7768 ADC driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_BACKEND");
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 06/17] iio: adc: ad7768: Validate master clock rate
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (4 preceding siblings ...)
2026-09-04 14:14 ` [PATCH v6 05/17] iio: adc: Add AD7768 and AD7768-4 core support Janani Sunil
@ 2026-09-04 14:14 ` Janani Sunil
2026-09-04 14:14 ` [PATCH v6 07/17] iio: adc: ad7768: Add power mode helper Janani Sunil
` (10 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Reject master clock rates outside the limits specified by the device.
External CMOS and LVDS clocks support 1.15 MHz to 34 MHz, while a
crystal requires 8 MHz to 34 MHz.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index c80056a9edd8..707f3ae70a95 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -95,6 +95,9 @@
#define AD7768_SPI_REG_MASK GENMASK(14, 8)
#define AD7768_SPI_DATA_MASK GENMASK(7, 0)
+#define AD7768_MIN_MCLK_FREQ_HZ (1150 * HZ_PER_KHZ)
+#define AD7768_MIN_XTAL_FREQ_HZ (8 * HZ_PER_MHZ)
+#define AD7768_MAX_MCLK_FREQ_HZ (34 * HZ_PER_MHZ)
#define AD7768_MAX_CHANNEL 8
enum ad7768_clock_source {
@@ -777,6 +780,23 @@ static int ad7768_get_enable_vref(struct device *dev, unsigned int index)
return refp_uV - refn_uV;
}
+static int ad7768_validate_mclk_rate(struct device *dev,
+ const struct ad7768_state *st)
+{
+ unsigned long min_rate = AD7768_MIN_MCLK_FREQ_HZ;
+ unsigned long rate = clk_get_rate(st->mclk);
+
+ if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL)
+ min_rate = AD7768_MIN_XTAL_FREQ_HZ;
+
+ if (rate < min_rate || rate > AD7768_MAX_MCLK_FREQ_HZ)
+ return dev_err_probe(dev, -EINVAL,
+ "MCLK rate %lu Hz outside %lu-%lu Hz\n",
+ rate, min_rate, AD7768_MAX_MCLK_FREQ_HZ);
+
+ return 0;
+}
+
static int ad7768_probe(struct spi_device *spi)
{
unsigned int spi_readback, rev_id;
@@ -852,6 +872,10 @@ static int ad7768_probe(struct spi_device *spi)
return dev_err_probe(dev, PTR_ERR(st->mclk),
"Failed to get master clock\n");
+ ret = ad7768_validate_mclk_rate(dev, st);
+ if (ret)
+ return ret;
+
st->regmap = devm_regmap_init(dev, &ad7768_regmap_bus, spi,
st->chip_info->regmap_config);
if (IS_ERR(st->regmap))
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 07/17] iio: adc: ad7768: Add power mode helper
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (5 preceding siblings ...)
2026-09-04 14:14 ` [PATCH v6 06/17] iio: adc: ad7768: Validate master clock rate Janani Sunil
@ 2026-09-04 14:14 ` Janani Sunil
2026-09-04 14:25 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 08/17] iio: adc: ad7768: Derive output data rates Janani Sunil
` (9 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:14 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Describe each power mode and its internal master-clock divisor in a
table. Replace the open-coded fast-mode setup with a helper that records
the active mode, preparing for dynamic mode selection.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 52 +++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 45 insertions(+), 7 deletions(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 707f3ae70a95..71db57a2b9dd 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -41,6 +41,8 @@
#define AD7768_REG_POWER_MODE 0x04
#define AD7768_SLEEP_MODE_MSK BIT(7)
#define AD7768_POWER_MODE_POWER_MODE_MSK GENMASK(5, 4)
+#define AD7768_POWER_MODE_POWER_MODE_LOW 0x0
+#define AD7768_POWER_MODE_POWER_MODE_MEDIAN 0x2
#define AD7768_POWER_MODE_POWER_MODE_FAST 0x3
#define AD7768_POWER_MODE_LVDS_ENABLE BIT(3)
#define AD7768_POWER_MODE_MCLK_DIV_MSK GENMASK(1, 0)
@@ -106,6 +108,17 @@ enum ad7768_clock_source {
AD7768_CLOCK_SOURCE_LVDS,
};
+struct ad7768_power_mode_info {
+ unsigned int mode;
+ unsigned int mclk_div;
+};
+
+static const struct ad7768_power_mode_info ad7768_power_modes[] = {
+ { .mode = AD7768_POWER_MODE_POWER_MODE_LOW, .mclk_div = 32 },
+ { .mode = AD7768_POWER_MODE_POWER_MODE_MEDIAN, .mclk_div = 8 },
+ { .mode = AD7768_POWER_MODE_POWER_MODE_FAST, .mclk_div = 4 },
+};
+
struct ad7768_precharge_config {
bool prebufp_en;
bool prebufn_en;
@@ -131,6 +144,7 @@ struct ad7768_state {
const struct ad7768_chip_info *chip_info;
struct iio_backend *back;
unsigned int vref_uV[2];
+ unsigned int power_mode_idx;
__be16 d16 __aligned(IIO_DMA_MINALIGN);
};
@@ -335,6 +349,36 @@ static int ad7768_sync(struct ad7768_state *st)
AD7768_DATA_CONTROL_SPI_SYNC);
}
+static int ad7768_set_power_mode(struct ad7768_state *st,
+ unsigned int mode_idx)
+{
+ const struct ad7768_power_mode_info *mode_info;
+ int ret;
+
+ mode_info = &ad7768_power_modes[mode_idx];
+ ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_POWER_MODE_POWER_MODE_MSK,
+ FIELD_PREP(AD7768_POWER_MODE_POWER_MODE_MSK,
+ mode_info->mode));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_POWER_MODE_MCLK_DIV_MSK,
+ FIELD_PREP(AD7768_POWER_MODE_MCLK_DIV_MSK,
+ mode_info->mode));
+ if (ret)
+ return ret;
+
+ ret = ad7768_sync(st);
+ if (ret)
+ return ret;
+
+ st->power_mode_idx = mode_idx;
+
+ return 0;
+}
+
static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
@@ -494,13 +538,7 @@ static int ad7768_configure_capture(struct ad7768_state *st)
unsigned int dclk_div;
int ret;
- ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE,
- AD7768_POWER_MODE_POWER_MODE_MSK |
- AD7768_POWER_MODE_MCLK_DIV_MSK,
- FIELD_PREP(AD7768_POWER_MODE_POWER_MODE_MSK,
- AD7768_POWER_MODE_POWER_MODE_FAST) |
- FIELD_PREP(AD7768_POWER_MODE_MCLK_DIV_MSK,
- AD7768_POWER_MODE_POWER_MODE_FAST));
+ ret = ad7768_set_power_mode(st, ARRAY_SIZE(ad7768_power_modes) - 1);
if (ret)
return ret;
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 08/17] iio: adc: ad7768: Derive output data rates
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (6 preceding siblings ...)
2026-09-04 14:14 ` [PATCH v6 07/17] iio: adc: ad7768: Add power mode helper Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:15 ` [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles Janani Sunil
` (8 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Derive valid output data rates from the master clock, power mode and
decimation ratio.
Replace the fixed x64 decimation and open-coded DCLK divider in
ad7768_configure_capture() with the derived default output rate and the
new decimation and clock-divider helpers. Select the maximum rate
supported by the configured data interface for the initial capture
configuration.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 149 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 126 insertions(+), 23 deletions(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 71db57a2b9dd..2020156eb6b6 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -12,6 +12,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/log2.h>
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
@@ -34,7 +35,6 @@
#define AD7768_CH_MODE_FILTER_TYPE_MSK BIT(3)
#define AD7768_CH_MODE_FILTER_TYPE_WIDEBAND 0x0
#define AD7768_CH_MODE_DEC_RATE_MSK GENMASK(2, 0)
-#define AD7768_CH_MODE_DEC_RATE_64 0x1
#define AD7768_REG_CH_MODE_SEL 0x03
@@ -97,9 +97,12 @@
#define AD7768_SPI_REG_MASK GENMASK(14, 8)
#define AD7768_SPI_DATA_MASK GENMASK(7, 0)
+#define AD7768_SAMPLE_SIZE 32
+#define AD7768_MAX_DCLK_DIV 8
#define AD7768_MIN_MCLK_FREQ_HZ (1150 * HZ_PER_KHZ)
#define AD7768_MIN_XTAL_FREQ_HZ (8 * HZ_PER_MHZ)
#define AD7768_MAX_MCLK_FREQ_HZ (34 * HZ_PER_MHZ)
+#define AD7768_MAX_FREQ_PER_MODE 6
#define AD7768_MAX_CHANNEL 8
enum ad7768_clock_source {
@@ -126,6 +129,16 @@ struct ad7768_precharge_config {
bool refbufn;
};
+struct ad7768_freq_config {
+ unsigned int freq_hz;
+ unsigned int dec_rate;
+};
+
+struct ad7768_avail_freq {
+ unsigned int n_freqs;
+ struct ad7768_freq_config freq_cfg[AD7768_MAX_FREQ_PER_MODE];
+};
+
struct ad7768_chip_info {
const char *name;
unsigned int num_channels;
@@ -142,6 +155,8 @@ struct ad7768_state {
unsigned int datalines;
enum ad7768_clock_source clock_source;
const struct ad7768_chip_info *chip_info;
+ struct ad7768_avail_freq avail_freq[ARRAY_SIZE(ad7768_power_modes)];
+ unsigned int ch_freq[AD7768_MAX_CHANNEL];
struct iio_backend *back;
unsigned int vref_uV[2];
unsigned int power_mode_idx;
@@ -149,6 +164,10 @@ struct ad7768_state {
__be16 d16 __aligned(IIO_DMA_MINALIGN);
};
+static const unsigned int ad7768_dec_rate[AD7768_MAX_FREQ_PER_MODE] = {
+ 32, 64, 128, 256, 512, 1024,
+};
+
static const unsigned int ad7768_available_datalines[] = {
1, 2, 8,
};
@@ -379,6 +398,68 @@ static int ad7768_set_power_mode(struct ad7768_state *st,
return 0;
}
+static const struct ad7768_freq_config *
+ad7768_find_freq_config(const struct ad7768_state *st,
+ unsigned int mode_idx, unsigned int freq)
+{
+ const struct ad7768_avail_freq *avail_freq = &st->avail_freq[mode_idx];
+
+ for (unsigned int i = 0; i < avail_freq->n_freqs; i++) {
+ if (freq == avail_freq->freq_cfg[i].freq_hz)
+ return &avail_freq->freq_cfg[i];
+ }
+
+ return NULL;
+}
+
+static int ad7768_set_clk_divs(struct ad7768_state *st, unsigned int freq)
+{
+ const struct ad7768_freq_config *freq_cfg;
+ unsigned int mclk, dclk, dclk_div;
+ unsigned int chan_per_doutx;
+ unsigned int dclk_div_reg;
+
+ freq_cfg = ad7768_find_freq_config(st, st->power_mode_idx, freq);
+ if (!freq_cfg)
+ return -EINVAL;
+
+ mclk = clk_get_rate(st->mclk);
+ chan_per_doutx = st->chip_info->num_channels / st->datalines;
+ if (!chan_per_doutx)
+ return -EINVAL;
+
+ dclk = freq_cfg->freq_hz * AD7768_SAMPLE_SIZE * chan_per_doutx;
+ if (dclk > mclk)
+ return -EINVAL;
+
+ dclk_div = DIV_ROUND_CLOSEST(mclk, dclk);
+
+ /* Set the divider to the next-lowest supported power of two. */
+ dclk_div = rounddown_pow_of_two(min(dclk_div, AD7768_MAX_DCLK_DIV));
+
+ dclk_div_reg = AD7768_INTERFACE_CFG_DCLK_DIV(dclk_div);
+
+ return regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG,
+ AD7768_INTERFACE_CFG_DCLK_DIV_MSK,
+ FIELD_PREP(AD7768_INTERFACE_CFG_DCLK_DIV_MSK,
+ dclk_div_reg));
+}
+
+static int ad7768_set_mode_decimation(struct ad7768_state *st,
+ unsigned int freq, unsigned int mode)
+{
+ const struct ad7768_freq_config *freq_cfg;
+
+ freq_cfg = ad7768_find_freq_config(st, st->power_mode_idx, freq);
+ if (!freq_cfg)
+ return -EINVAL;
+
+ return regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(mode),
+ AD7768_CH_MODE_DEC_RATE_MSK,
+ FIELD_PREP(AD7768_CH_MODE_DEC_RATE_MSK,
+ freq_cfg->dec_rate));
+}
+
static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
@@ -531,28 +612,58 @@ static int ad7768_configure_precharge_buffers(struct iio_dev *indio_dev,
return regmap_write(st->regmap, AD7768_REG_REFN_BUF, refbufn_val);
}
+static void ad7768_set_available_sampling_freqs(struct ad7768_state *st)
+{
+ unsigned int n_power_modes = ARRAY_SIZE(ad7768_power_modes);
+ unsigned int mclk = clk_get_rate(st->mclk);
+
+ for (unsigned int mode_idx = 0; mode_idx < n_power_modes; mode_idx++) {
+ unsigned int dec = ARRAY_SIZE(ad7768_dec_rate);
+ struct ad7768_avail_freq *avail_freq;
+ unsigned int div;
+
+ avail_freq = &st->avail_freq[mode_idx];
+ div = ad7768_power_modes[mode_idx].mclk_div;
+ while (dec--) {
+ struct ad7768_freq_config *freq_cfg;
+
+ freq_cfg = &avail_freq->freq_cfg[avail_freq->n_freqs++];
+ freq_cfg->dec_rate = dec;
+ freq_cfg->freq_hz = mclk / (ad7768_dec_rate[dec] * div);
+ }
+ }
+
+ /* One DOUT line cannot carry the AD7768 fast-mode x32 output rate. */
+ if (st->datalines == 1 &&
+ st->chip_info->num_channels == AD7768_MAX_CHANNEL)
+ st->avail_freq[n_power_modes - 1].n_freqs--;
+}
+
static int ad7768_configure_capture(struct ad7768_state *st)
{
- unsigned int dclk_div_reg;
+ const struct ad7768_avail_freq *avail_freq;
+ unsigned int default_freq;
unsigned int mode_config;
- unsigned int dclk_div;
int ret;
ret = ad7768_set_power_mode(st, ARRAY_SIZE(ad7768_power_modes) - 1);
if (ret)
return ret;
- /*
- * Start with the wideband filter and a decimation rate of 64. This
- * supports every valid data-line configuration at the maximum MCLK.
- */
+ avail_freq = &st->avail_freq[st->power_mode_idx];
+ default_freq = avail_freq->freq_cfg[avail_freq->n_freqs - 1].freq_hz;
+ for (unsigned int channel = 0;
+ channel < st->chip_info->num_channels; channel++)
+ st->ch_freq[channel] = default_freq;
+
+ ret = ad7768_set_mode_decimation(st, default_freq, 0);
+ if (ret)
+ return ret;
+
mode_config = FIELD_PREP(AD7768_CH_MODE_FILTER_TYPE_MSK,
- AD7768_CH_MODE_FILTER_TYPE_WIDEBAND) |
- FIELD_PREP(AD7768_CH_MODE_DEC_RATE_MSK,
- AD7768_CH_MODE_DEC_RATE_64);
+ AD7768_CH_MODE_FILTER_TYPE_WIDEBAND);
ret = regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(0),
- AD7768_CH_MODE_FILTER_TYPE_MSK |
- AD7768_CH_MODE_DEC_RATE_MSK,
+ AD7768_CH_MODE_FILTER_TYPE_MSK,
mode_config);
if (ret)
return ret;
@@ -561,17 +672,7 @@ static int ad7768_configure_capture(struct ad7768_state *st)
if (ret)
return ret;
- /*
- * DCLK(min) is ODR * channels per DOUTx * 32. With fast mode
- * (fMOD = MCLK / 4) and x64 decimation, this gives:
- * MCLK / DCLK = 8 * data lines / channels.
- */
- dclk_div = 8 * st->datalines / st->chip_info->num_channels;
- dclk_div_reg = AD7768_INTERFACE_CFG_DCLK_DIV(dclk_div);
- ret = regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG,
- AD7768_INTERFACE_CFG_DCLK_DIV_MSK,
- FIELD_PREP(AD7768_INTERFACE_CFG_DCLK_DIV_MSK,
- dclk_div_reg));
+ ret = ad7768_set_clk_divs(st, default_freq);
if (ret)
return ret;
@@ -695,6 +796,8 @@ static int ad7768_parse_config(struct iio_dev *indio_dev,
"Invalid data-lines-number %d for %s\n",
st->datalines, st->chip_info->name);
+ ad7768_set_available_sampling_freqs(st);
+
return ad7768_configure_capture(st);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (7 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 08/17] iio: adc: ad7768: Derive output data rates Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:45 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 10/17] iio: adc: ad7768: Add sampling frequency controls Janani Sunil
` (7 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
At buffered capture setup, select the fastest compatible power mode for
the enabled channel rates. Group channels into the two hardware mode
profiles and program their decimation, mode selection, and data clock.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 162 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 135 insertions(+), 27 deletions(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 2020156eb6b6..868130ebcc8f 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -7,7 +7,9 @@
#include <linux/array_size.h>
#include <linux/bitfield.h>
+#include <linux/bitmap.h>
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -16,6 +18,7 @@
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -33,7 +36,6 @@
#define AD7768_REG_CH_MODE(x) (0x01 + (x))
#define AD7768_CH_MODE_FILTER_TYPE_MSK BIT(3)
-#define AD7768_CH_MODE_FILTER_TYPE_WIDEBAND 0x0
#define AD7768_CH_MODE_DEC_RATE_MSK GENMASK(2, 0)
#define AD7768_REG_CH_MODE_SEL 0x03
@@ -104,6 +106,7 @@
#define AD7768_MAX_MCLK_FREQ_HZ (34 * HZ_PER_MHZ)
#define AD7768_MAX_FREQ_PER_MODE 6
#define AD7768_MAX_CHANNEL 8
+#define AD7768_NUM_CHANNEL_MODES 2
enum ad7768_clock_source {
AD7768_CLOCK_SOURCE_MCLK,
@@ -151,6 +154,8 @@ struct ad7768_chip_info {
struct ad7768_state {
struct regmap *regmap;
+ /* Protects device register access and configuration. */
+ struct mutex lock;
struct clk *mclk;
unsigned int datalines;
enum ad7768_clock_source clock_source;
@@ -204,6 +209,11 @@ static u8 ad7768_channel_mask(const struct ad7768_state *st, u8 ch)
return BIT(st->chip_info->chan_map[ch]);
}
+static u8 ad7768_channel_mode_mask(const struct ad7768_state *st, u8 ch)
+{
+ return BIT(ch) | ad7768_channel_mask(st, ch);
+}
+
static u8 ad7768_all_standby_mask(const struct ad7768_state *st)
{
return GENMASK(st->chip_info->num_channels - 1, 0);
@@ -460,14 +470,70 @@ static int ad7768_set_mode_decimation(struct ad7768_state *st,
freq_cfg->dec_rate));
}
-static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
- const unsigned long *scan_mask)
+static int ad7768_set_lowest_noise_mode(struct ad7768_state *st,
+ const unsigned long *scan_mask)
+{
+ unsigned int mode_idx = ARRAY_SIZE(ad7768_power_modes);
+
+ /*
+ * The output data rate ranges overlap between the power modes. At a
+ * common ODR, the faster mode has lower noise, so prefer the fastest
+ * mode that supports every enabled channel.
+ */
+ while (mode_idx--) {
+ bool supported = true;
+ unsigned int channel;
+
+ for_each_set_bit(channel, scan_mask,
+ st->chip_info->num_channels) {
+ if (!ad7768_find_freq_config(st, mode_idx,
+ st->ch_freq[channel])) {
+ supported = false;
+ break;
+ }
+ }
+
+ if (supported)
+ return ad7768_set_power_mode(st, mode_idx);
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_find_matching_mode(const bool *mode_used,
+ const unsigned int *mode_freq,
+ unsigned int freq)
+{
+ for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ if (!mode_used[mode] || mode_freq[mode] == freq)
+ return mode;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
{
+ unsigned int mode_freq[AD7768_NUM_CHANNEL_MODES] = { };
+ bool mode_used[AD7768_NUM_CHANNEL_MODES] = { };
struct ad7768_state *st = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
unsigned long channel_mask;
unsigned long standby_mask;
+ unsigned int max_freq;
+ unsigned int c;
int ret;
+ guard(mutex)(&st->lock);
+
+ ret = ad7768_set_lowest_noise_mode(st, scan_mask);
+ if (ret == -EINVAL)
+ return dev_err_probe(dev, ret,
+ "No power mode supports all ODRs\n");
+ if (ret)
+ return ret;
+
channel_mask = ad7768_all_standby_mask(st);
standby_mask = channel_mask & ~*scan_mask;
@@ -478,11 +544,66 @@ static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL)
__clear_bit(st->chip_info->num_channels / 2, &standby_mask);
+ for_each_set_bit(c, scan_mask, st->chip_info->num_channels) {
+ unsigned int mask;
+ int mode;
+
+ mode = ad7768_find_matching_mode(mode_used, mode_freq,
+ st->ch_freq[c]);
+ if (mode < 0)
+ return dev_err_probe(dev, -EINVAL,
+ "Over %d channel modes required\n",
+ AD7768_NUM_CHANNEL_MODES);
+
+ mode_freq[mode] = st->ch_freq[c];
+ mode_used[mode] = true;
+
+ mask = ad7768_channel_mode_mask(st, c);
+ ret = regmap_assign_bits(st->regmap, AD7768_REG_CH_MODE_SEL,
+ mask, mode);
+ if (ret)
+ return ret;
+ }
+
ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY,
channel_mask, standby_mask);
if (ret)
return ret;
+ max_freq = 0;
+ for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ if (!mode_used[mode])
+ continue;
+
+ ret = ad7768_set_mode_decimation(st, mode_freq[mode], mode);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_CH_MODE(mode),
+ AD7768_CH_MODE_FILTER_TYPE_MSK);
+ if (ret)
+ return ret;
+
+ max_freq = max(max_freq, mode_freq[mode]);
+ }
+
+ ret = ad7768_set_clk_divs(st, max_freq);
+ if (ret)
+ return ret;
+
+ return ad7768_sync(st);
+}
+
+static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ad7768_apply_channel_modes(indio_dev, scan_mask);
+ if (ret)
+ return ret;
+
for (unsigned int c = 0; c < st->chip_info->num_channels; c++) {
if (test_bit(c, scan_mask))
ret = iio_backend_chan_enable(st->back, c);
@@ -639,11 +760,10 @@ static void ad7768_set_available_sampling_freqs(struct ad7768_state *st)
st->avail_freq[n_power_modes - 1].n_freqs--;
}
-static int ad7768_configure_capture(struct ad7768_state *st)
+static int ad7768_init_sampling_freqs(struct ad7768_state *st)
{
const struct ad7768_avail_freq *avail_freq;
unsigned int default_freq;
- unsigned int mode_config;
int ret;
ret = ad7768_set_power_mode(st, ARRAY_SIZE(ad7768_power_modes) - 1);
@@ -656,27 +776,7 @@ static int ad7768_configure_capture(struct ad7768_state *st)
channel < st->chip_info->num_channels; channel++)
st->ch_freq[channel] = default_freq;
- ret = ad7768_set_mode_decimation(st, default_freq, 0);
- if (ret)
- return ret;
-
- mode_config = FIELD_PREP(AD7768_CH_MODE_FILTER_TYPE_MSK,
- AD7768_CH_MODE_FILTER_TYPE_WIDEBAND);
- ret = regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(0),
- AD7768_CH_MODE_FILTER_TYPE_MSK,
- mode_config);
- if (ret)
- return ret;
-
- ret = regmap_write(st->regmap, AD7768_REG_CH_MODE_SEL, 0);
- if (ret)
- return ret;
-
- ret = ad7768_set_clk_divs(st, default_freq);
- if (ret)
- return ret;
-
- return ad7768_sync(st);
+ return 0;
}
static int ad7768_parse_config(struct iio_dev *indio_dev,
@@ -798,7 +898,11 @@ static int ad7768_parse_config(struct iio_dev *indio_dev,
ad7768_set_available_sampling_freqs(st);
- return ad7768_configure_capture(st);
+ ret = ad7768_init_sampling_freqs(st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set power mode\n");
+
+ return 0;
}
static int ad7768_reset(struct ad7768_state *st)
@@ -954,6 +1058,10 @@ static int ad7768_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
spi_set_drvdata(spi, st);
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
st->chip_info = spi_get_device_match_data(spi);
if (!st->chip_info)
return dev_err_probe(dev, -ENODATA, "Failed to get match data\n");
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 10/17] iio: adc: ad7768: Add sampling frequency controls
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (8 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:15 ` [PATCH v6 11/17] iio: adc: ad7768: Add per-channel filter controls Janani Sunil
` (6 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Expose the requested sampling frequency and available rates for each
channel. Validate requests against the rates supported by the power mode
tables and apply them when buffered capture starts.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 99 insertions(+), 2 deletions(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 868130ebcc8f..d5eaef92cbf2 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -125,6 +125,9 @@ static const struct ad7768_power_mode_info ad7768_power_modes[] = {
{ .mode = AD7768_POWER_MODE_POWER_MODE_FAST, .mclk_div = 4 },
};
+#define AD7768_MAX_FREQS \
+ (AD7768_MAX_FREQ_PER_MODE + ARRAY_SIZE(ad7768_power_modes))
+
struct ad7768_precharge_config {
bool prebufp_en;
bool prebufn_en;
@@ -161,10 +164,12 @@ struct ad7768_state {
enum ad7768_clock_source clock_source;
const struct ad7768_chip_info *chip_info;
struct ad7768_avail_freq avail_freq[ARRAY_SIZE(ad7768_power_modes)];
+ unsigned int n_freqs;
+ int freqs[AD7768_MAX_FREQS];
unsigned int ch_freq[AD7768_MAX_CHANNEL];
+ unsigned int power_mode_idx;
struct iio_backend *back;
unsigned int vref_uV[2];
- unsigned int power_mode_idx;
__be16 d16 __aligned(IIO_DMA_MINALIGN);
};
@@ -500,6 +505,32 @@ static int ad7768_set_lowest_noise_mode(struct ad7768_state *st,
return -EINVAL;
}
+static bool ad7768_freq_supported_in_any_mode(const struct ad7768_state *st,
+ unsigned int freq)
+{
+ for (unsigned int mode_idx = 0;
+ mode_idx < ARRAY_SIZE(ad7768_power_modes); mode_idx++) {
+ if (ad7768_find_freq_config(st, mode_idx, freq))
+ return true;
+ }
+
+ return false;
+}
+
+static int ad7768_set_sampling_freq(struct iio_dev *indio_dev,
+ unsigned int freq, unsigned int channel)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ if (!freq || !ad7768_freq_supported_in_any_mode(st, freq))
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+ st->ch_freq[channel] = freq;
+
+ return 0;
+}
+
static int ad7768_find_matching_mode(const bool *mode_used,
const unsigned int *mode_freq,
unsigned int freq)
@@ -678,14 +709,63 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_SCALE:
return ad7768_read_scale(st, chan, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ guard(mutex)(&st->lock);
+ *val = st->ch_freq[chan->channel];
+
+ return IIO_VAL_INT;
+ }
default:
return -EINVAL;
}
}
+static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long info)
+{
+ if (info == IIO_CHAN_INFO_SAMP_FREQ)
+ return IIO_VAL_INT;
+
+ return -EINVAL;
+}
+
+static int ad7768_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ if (info == IIO_CHAN_INFO_SAMP_FREQ)
+ return ad7768_set_sampling_freq(indio_dev, val, chan->channel);
+
+ return -EINVAL;
+}
+
+static int ad7768_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ if (info != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ *vals = st->freqs;
+ *type = IIO_VAL_INT;
+ *length = st->n_freqs;
+
+ return IIO_AVAIL_LIST;
+}
+
static const struct iio_info ad7768_info = {
.debugfs_reg_access = ad7768_reg_access,
.read_raw = ad7768_read_raw,
+ .write_raw_get_fmt = ad7768_write_raw_get_fmt,
+ .write_raw = ad7768_write_raw,
+ .read_avail = ad7768_read_avail,
.update_scan_mode = ad7768_update_scan_mode,
};
@@ -758,6 +838,20 @@ static void ad7768_set_available_sampling_freqs(struct ad7768_state *st)
if (st->datalines == 1 &&
st->chip_info->num_channels == AD7768_MAX_CHANNEL)
st->avail_freq[n_power_modes - 1].n_freqs--;
+
+ for (unsigned int mode_idx = 0; mode_idx < n_power_modes; mode_idx++) {
+ const struct ad7768_avail_freq *avail_freq;
+
+ avail_freq = &st->avail_freq[mode_idx];
+ for (unsigned int i = 0; i < avail_freq->n_freqs; i++) {
+ unsigned int freq = avail_freq->freq_cfg[i].freq_hz;
+
+ if (st->n_freqs && st->freqs[st->n_freqs - 1] >= freq)
+ continue;
+
+ st->freqs[st->n_freqs++] = freq;
+ }
+ }
}
static int ad7768_init_sampling_freqs(struct ad7768_state *st)
@@ -855,7 +949,10 @@ static int ad7768_parse_config(struct iio_dev *indio_dev,
chan[chan_idx] = (struct iio_chan_spec) {
.type = IIO_VOLTAGE,
- .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
.indexed = 1,
.channel = channel,
.scan_index = channel,
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 11/17] iio: adc: ad7768: Add per-channel filter controls
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (9 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 10/17] iio: adc: ad7768: Add sampling frequency controls Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:15 ` [PATCH v6 12/17] iio: adc: ad7768: Wait for digital filters to settle Janani Sunil
` (5 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Expose wideband and sinc5 filter selection for each channel. Group
enabled channels by their requested sampling frequency and filter pairs
and program each pair into one of the two hardware mode profiles.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 72 ++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 66 insertions(+), 6 deletions(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index d5eaef92cbf2..ac236e5c74c7 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -108,6 +108,11 @@
#define AD7768_MAX_CHANNEL 8
#define AD7768_NUM_CHANNEL_MODES 2
+enum ad7768_filter_type {
+ AD7768_FILTER_TYPE_WIDEBAND,
+ AD7768_FILTER_TYPE_SINC5,
+};
+
enum ad7768_clock_source {
AD7768_CLOCK_SOURCE_MCLK,
AD7768_CLOCK_SOURCE_XTAL,
@@ -168,6 +173,7 @@ struct ad7768_state {
int freqs[AD7768_MAX_FREQS];
unsigned int ch_freq[AD7768_MAX_CHANNEL];
unsigned int power_mode_idx;
+ enum ad7768_filter_type ch_filter[AD7768_MAX_CHANNEL];
struct iio_backend *back;
unsigned int vref_uV[2];
@@ -531,12 +537,59 @@ static int ad7768_set_sampling_freq(struct iio_dev *indio_dev,
return 0;
}
+static int ad7768_set_filter_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ guard(mutex)(&st->lock);
+ st->ch_filter[chan->channel] = mode;
+
+ return 0;
+}
+
+static int ad7768_get_filter_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ guard(mutex)(&st->lock);
+
+ return st->ch_filter[chan->channel];
+}
+
+static const char *const ad7768_filter_types[] = {
+ [AD7768_FILTER_TYPE_WIDEBAND] = "wideband",
+ [AD7768_FILTER_TYPE_SINC5] = "sinc5",
+};
+
+static const struct iio_enum ad7768_filter_types_enum = {
+ .items = ad7768_filter_types,
+ .num_items = ARRAY_SIZE(ad7768_filter_types),
+ .set = ad7768_set_filter_mode,
+ .get = ad7768_get_filter_mode,
+};
+
+static struct iio_chan_spec_ext_info ad7768_ext_info[] = {
+ IIO_ENUM("filter_type", IIO_SEPARATE, &ad7768_filter_types_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ad7768_filter_types_enum),
+ { }
+};
+
static int ad7768_find_matching_mode(const bool *mode_used,
const unsigned int *mode_freq,
- unsigned int freq)
+ const enum ad7768_filter_type *mode_filter,
+ unsigned int freq,
+ enum ad7768_filter_type filter)
{
for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
- if (!mode_used[mode] || mode_freq[mode] == freq)
+ if (!mode_used[mode] ||
+ (mode_freq[mode] == freq && mode_filter[mode] == filter))
return mode;
}
@@ -546,6 +599,7 @@ static int ad7768_find_matching_mode(const bool *mode_used,
static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
+ enum ad7768_filter_type mode_filter[AD7768_NUM_CHANNEL_MODES] = { };
unsigned int mode_freq[AD7768_NUM_CHANNEL_MODES] = { };
bool mode_used[AD7768_NUM_CHANNEL_MODES] = { };
struct ad7768_state *st = iio_priv(indio_dev);
@@ -580,13 +634,15 @@ static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
int mode;
mode = ad7768_find_matching_mode(mode_used, mode_freq,
- st->ch_freq[c]);
+ mode_filter, st->ch_freq[c],
+ st->ch_filter[c]);
if (mode < 0)
return dev_err_probe(dev, -EINVAL,
"Over %d channel modes required\n",
AD7768_NUM_CHANNEL_MODES);
mode_freq[mode] = st->ch_freq[c];
+ mode_filter[mode] = st->ch_filter[c];
mode_used[mode] = true;
mask = ad7768_channel_mode_mask(st, c);
@@ -610,8 +666,9 @@ static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
if (ret)
return ret;
- ret = regmap_clear_bits(st->regmap, AD7768_REG_CH_MODE(mode),
- AD7768_CH_MODE_FILTER_TYPE_MSK);
+ ret = regmap_assign_bits(st->regmap, AD7768_REG_CH_MODE(mode),
+ AD7768_CH_MODE_FILTER_TYPE_MSK,
+ mode_filter[mode]);
if (ret)
return ret;
@@ -867,8 +924,10 @@ static int ad7768_init_sampling_freqs(struct ad7768_state *st)
avail_freq = &st->avail_freq[st->power_mode_idx];
default_freq = avail_freq->freq_cfg[avail_freq->n_freqs - 1].freq_hz;
for (unsigned int channel = 0;
- channel < st->chip_info->num_channels; channel++)
+ channel < st->chip_info->num_channels; channel++) {
st->ch_freq[channel] = default_freq;
+ st->ch_filter[channel] = AD7768_FILTER_TYPE_WIDEBAND;
+ }
return 0;
}
@@ -961,6 +1020,7 @@ static int ad7768_parse_config(struct iio_dev *indio_dev,
.realbits = 24,
.storagebits = 32,
},
+ .ext_info = ad7768_ext_info,
};
chan_idx++;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 12/17] iio: adc: ad7768: Wait for digital filters to settle
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (10 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 11/17] iio: adc: ad7768: Add per-channel filter controls Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:15 ` [PATCH v6 13/17] iio: adc: ad7768: Add calibration controls Janani Sunil
` (4 subsequent siblings)
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
After synchronizing the channel mode configuration, wait for the longest
settling period required by the active profiles. Derive the delay from
the selected output data rate and the datasheet filter-settling limits.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index ac236e5c74c7..f2c762688fb3 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -107,6 +107,8 @@
#define AD7768_MAX_FREQ_PER_MODE 6
#define AD7768_MAX_CHANNEL 8
#define AD7768_NUM_CHANNEL_MODES 2
+#define AD7768_WIDEBAND_SETTLING_SAMPLES 68
+#define AD7768_SINC5_SETTLING_SAMPLES 7
enum ad7768_filter_type {
AD7768_FILTER_TYPE_WIDEBAND,
@@ -596,6 +598,33 @@ static int ad7768_find_matching_mode(const bool *mode_used,
return -EINVAL;
}
+static void ad7768_filter_wait(const unsigned int *mode_freq,
+ const enum ad7768_filter_type *mode_filter,
+ const bool *mode_used)
+{
+ unsigned int t_settle_us = 0;
+
+ for (unsigned int mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ unsigned int t_mode_us;
+ u8 settling_samples;
+
+ if (!mode_used[mode] || !mode_freq[mode])
+ continue;
+
+ if (mode_filter[mode] == AD7768_FILTER_TYPE_SINC5)
+ settling_samples = AD7768_SINC5_SETTLING_SAMPLES;
+ else
+ settling_samples = AD7768_WIDEBAND_SETTLING_SAMPLES;
+
+ t_mode_us = DIV_ROUND_UP(settling_samples * USEC_PER_SEC,
+ mode_freq[mode]);
+ t_settle_us = max(t_settle_us, t_mode_us);
+ }
+
+ if (t_settle_us)
+ fsleep(t_settle_us);
+}
+
static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
@@ -679,7 +708,14 @@ static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
if (ret)
return ret;
- return ad7768_sync(st);
+ ret = ad7768_sync(st);
+ if (ret)
+ return ret;
+
+ /* Apply a filter settling time (datasheet Tables 28 and 29). */
+ ad7768_filter_wait(mode_freq, mode_filter, mode_used);
+
+ return 0;
}
static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 13/17] iio: adc: ad7768: Add calibration controls
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (11 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 12/17] iio: adc: ad7768: Wait for digital filters to settle Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:35 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 14/17] iio: adc: ad7768: Add per-channel conversion delay Janani Sunil
` (3 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Expose the per-channel offset and gain calibration registers through the
IIO calibbias and calibscale attributes.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 116 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 111 insertions(+), 5 deletions(-)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index f2c762688fb3..802666f7abd4 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -27,6 +27,7 @@
#include <linux/spi/spi.h>
#include <linux/time.h>
#include <linux/types.h>
+#include <linux/unaligned.h>
#include <linux/units.h>
#include <linux/iio/backend.h>
@@ -107,6 +108,7 @@
#define AD7768_MAX_FREQ_PER_MODE 6
#define AD7768_MAX_CHANNEL 8
#define AD7768_NUM_CHANNEL_MODES 2
+#define AD7768_CALIB_REG_MSK GENMASK(23, 0)
#define AD7768_WIDEBAND_SETTLING_SAMPLES 68
#define AD7768_SINC5_SETTLING_SAMPLES 7
@@ -232,6 +234,18 @@ static u8 ad7768_all_standby_mask(const struct ad7768_state *st)
return GENMASK(st->chip_info->num_channels - 1, 0);
}
+static unsigned int ad7768_offset_reg(const struct ad7768_state *st,
+ unsigned int ch)
+{
+ return AD7768_REG_OFFSET(st->chip_info->chan_map[ch]);
+}
+
+static unsigned int ad7768_gain_reg(const struct ad7768_state *st,
+ unsigned int ch)
+{
+ return AD7768_REG_GAIN(st->chip_info->chan_map[ch]);
+}
+
static u8 ad7768_precharge_buf1_mask(const struct ad7768_state *st, u16 val)
{
return val & GENMASK(st->chip_info->prebuf_split - 1, 0);
@@ -359,6 +373,59 @@ static const struct regmap_config ad7768_4_regmap_config = {
.readable_reg = ad7768_4_readable_reg,
};
+static unsigned int ad7768_get_calib_reg_base(struct ad7768_state *st,
+ const struct iio_chan_spec *chan,
+ bool is_gain)
+{
+ if (is_gain)
+ return ad7768_gain_reg(st, chan->channel);
+
+ return ad7768_offset_reg(st, chan->channel);
+}
+
+static int ad7768_read_calib_value(struct ad7768_state *st,
+ unsigned int base_reg, unsigned int *val)
+{
+ u8 data[3];
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_bulk_read(st->regmap, base_reg, data, sizeof(data));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_be24(data);
+
+ return 0;
+}
+
+static int ad7768_write_calib_value(struct ad7768_state *st,
+ unsigned int base_reg, unsigned int val)
+{
+ u8 data[3];
+ int ret;
+
+ if (val > AD7768_CALIB_REG_MSK)
+ return -EINVAL;
+
+ put_unaligned_be24(val, data);
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return regmap_bulk_write(st->regmap, base_reg, data, sizeof(data));
+}
+
static int ad7768_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
@@ -798,6 +865,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long info)
{
struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
switch (info) {
case IIO_CHAN_INFO_SCALE:
@@ -808,6 +876,21 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
}
+
+ case IIO_CHAN_INFO_CALIBBIAS:
+ case IIO_CHAN_INFO_CALIBSCALE: {
+ bool is_gain = info == IIO_CHAN_INFO_CALIBSCALE;
+ unsigned int base_reg;
+ unsigned int calib;
+
+ base_reg = ad7768_get_calib_reg_base(st, chan, is_gain);
+ ret = ad7768_read_calib_value(st, base_reg, &calib);
+ if (ret)
+ return ret;
+
+ *val = calib;
+ return IIO_VAL_INT;
+ }
default:
return -EINVAL;
}
@@ -816,24 +899,45 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long info)
{
- if (info == IIO_CHAN_INFO_SAMP_FREQ)
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ case IIO_CHAN_INFO_CALIBBIAS:
+ case IIO_CHAN_INFO_CALIBSCALE:
return IIO_VAL_INT;
-
- return -EINVAL;
+ default:
+ return -EINVAL;
+ }
}
static int ad7768_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned int base_reg;
+
IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
- if (info == IIO_CHAN_INFO_SAMP_FREQ)
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
return ad7768_set_sampling_freq(indio_dev, val, chan->channel);
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val < 0 || val > AD7768_CALIB_REG_MSK)
+ return -EINVAL;
- return -EINVAL;
+ base_reg = ad7768_get_calib_reg_base(st, chan, false);
+ return ad7768_write_calib_value(st, base_reg, val);
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val < 0 || val > AD7768_CALIB_REG_MSK)
+ return -EINVAL;
+
+ base_reg = ad7768_get_calib_reg_base(st, chan, true);
+ return ad7768_write_calib_value(st, base_reg, val);
+ default:
+ return -EINVAL;
+ }
}
static int ad7768_read_avail(struct iio_dev *indio_dev,
@@ -1045,6 +1149,8 @@ static int ad7768_parse_config(struct iio_dev *indio_dev,
chan[chan_idx] = (struct iio_chan_spec) {
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_separate_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ),
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 14/17] iio: adc: ad7768: Add per-channel conversion delay
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (12 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 13/17] iio: adc: ad7768: Add calibration controls Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:33 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 15/17] iio: adc: ad7768: Add VCM regulator support Janani Sunil
` (2 subsequent siblings)
16 siblings, 1 reply; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Expose the per-channel synchronization phase offset through the IIO
conversion-delay attribute.
Derive the delay resolution from MCLK, power mode and decimation rate.
Validate the requested delay and program the corresponding phase
register when applying the active channel configuration.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/ad7768.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 129 insertions(+)
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 802666f7abd4..9c6bebb1857f 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -149,6 +149,12 @@ struct ad7768_freq_config {
unsigned int dec_rate;
};
+struct ad7768_convdelay_params {
+ unsigned int shift;
+ unsigned int max_raw;
+ u64 step_ps;
+};
+
struct ad7768_avail_freq {
unsigned int n_freqs;
struct ad7768_freq_config freq_cfg[AD7768_MAX_FREQ_PER_MODE];
@@ -175,6 +181,7 @@ struct ad7768_state {
struct ad7768_avail_freq avail_freq[ARRAY_SIZE(ad7768_power_modes)];
unsigned int n_freqs;
int freqs[AD7768_MAX_FREQS];
+ u64 ch_convdelay_ps[AD7768_MAX_CHANNEL];
unsigned int ch_freq[AD7768_MAX_CHANNEL];
unsigned int power_mode_idx;
enum ad7768_filter_type ch_filter[AD7768_MAX_CHANNEL];
@@ -246,6 +253,12 @@ static unsigned int ad7768_gain_reg(const struct ad7768_state *st,
return AD7768_REG_GAIN(st->chip_info->chan_map[ch]);
}
+static unsigned int ad7768_phase_reg(const struct ad7768_state *st,
+ unsigned int ch)
+{
+ return AD7768_REG_PHASE(st->chip_info->chan_map[ch]);
+}
+
static u8 ad7768_precharge_buf1_mask(const struct ad7768_state *st, u16 val)
{
return val & GENMASK(st->chip_info->prebuf_split - 1, 0);
@@ -665,6 +678,92 @@ static int ad7768_find_matching_mode(const bool *mode_used,
return -EINVAL;
}
+static int ad7768_get_convdelay_params(struct ad7768_state *st, unsigned int ch,
+ struct ad7768_convdelay_params *params)
+{
+ const struct ad7768_freq_config *freq_cfg;
+ unsigned int dec_rate;
+ unsigned int mclk_div;
+ u64 mclk;
+ u64 mult;
+
+ freq_cfg = ad7768_find_freq_config(st, st->power_mode_idx,
+ st->ch_freq[ch]);
+ if (!freq_cfg)
+ return -EINVAL;
+
+ dec_rate = ad7768_dec_rate[freq_cfg->dec_rate];
+ switch (dec_rate) {
+ case 32:
+ params->shift = 3;
+ params->max_raw = 31;
+ mult = 1;
+ break;
+ case 64:
+ params->shift = 2;
+ params->max_raw = 63;
+ mult = 1;
+ break;
+ case 128:
+ params->shift = 1;
+ params->max_raw = 127;
+ mult = 1;
+ break;
+ case 256:
+ params->shift = 0;
+ params->max_raw = 255;
+ mult = 1;
+ break;
+ case 512:
+ params->shift = 0;
+ params->max_raw = 255;
+ mult = 2;
+ break;
+ case 1024:
+ params->shift = 0;
+ params->max_raw = 255;
+ mult = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mclk = clk_get_rate(st->mclk);
+ if (!mclk)
+ return -EINVAL;
+
+ mclk_div = ad7768_power_modes[st->power_mode_idx].mclk_div;
+ params->step_ps =
+ DIV_ROUND_CLOSEST_ULL(mult * PSEC_PER_SEC * mclk_div, mclk);
+
+ return 0;
+}
+
+static int ad7768_set_channel_convdelay(struct ad7768_state *st,
+ unsigned int ch)
+{
+ u64 delay_ps = st->ch_convdelay_ps[ch];
+ struct ad7768_convdelay_params params;
+ u64 max_delay_ps;
+ u64 raw;
+ int ret;
+
+ ret = ad7768_get_convdelay_params(st, ch, ¶ms);
+ if (ret)
+ return ret;
+
+ max_delay_ps = (u64)params.max_raw * params.step_ps;
+ if (delay_ps > max_delay_ps)
+ return -EINVAL;
+
+ raw = DIV_ROUND_CLOSEST_ULL(delay_ps, params.step_ps);
+ if (raw > params.max_raw)
+ return -EINVAL;
+
+ return regmap_write(st->regmap, ad7768_phase_reg(st, ch),
+ raw << params.shift);
+}
+
static void ad7768_filter_wait(const unsigned int *mode_freq,
const enum ad7768_filter_type *mode_filter,
const bool *mode_used)
@@ -771,6 +870,16 @@ static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
max_freq = max(max_freq, mode_freq[mode]);
}
+ for_each_set_bit(c, scan_mask, st->chip_info->num_channels) {
+ ret = ad7768_set_channel_convdelay(st, c);
+ if (ret == -EINVAL)
+ return dev_err_probe(regmap_get_device(st->regmap), ret,
+ "Invalid conversion delay for channel %u\n",
+ c);
+ if (ret)
+ return ret;
+ }
+
ret = ad7768_set_clk_divs(st, max_freq);
if (ret)
return ret;
@@ -891,6 +1000,13 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
*val = calib;
return IIO_VAL_INT;
}
+ case IIO_CHAN_INFO_CONVDELAY: {
+ guard(mutex)(&st->lock);
+ iio_val_s64_decompose(st->ch_convdelay_ps[chan->channel],
+ val, val2);
+
+ return IIO_VAL_DECIMAL64_PICO;
+ }
default:
return -EINVAL;
}
@@ -904,6 +1020,8 @@ static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBBIAS:
case IIO_CHAN_INFO_CALIBSCALE:
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CONVDELAY:
+ return IIO_VAL_DECIMAL64_PICO;
default:
return -EINVAL;
}
@@ -915,6 +1033,7 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
{
struct ad7768_state *st = iio_priv(indio_dev);
unsigned int base_reg;
+ s64 delay_ps;
IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
if (IIO_DEV_ACQUIRE_FAILED(claim))
@@ -935,6 +1054,15 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
base_reg = ad7768_get_calib_reg_base(st, chan, true);
return ad7768_write_calib_value(st, base_reg, val);
+ case IIO_CHAN_INFO_CONVDELAY: {
+ delay_ps = iio_val_s64_compose(val, val2);
+ if (delay_ps < 0)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+ st->ch_convdelay_ps[chan->channel] = delay_ps;
+ return 0;
+ }
default:
return -EINVAL;
}
@@ -1151,6 +1279,7 @@ static int ad7768_parse_config(struct iio_dev *indio_dev,
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CONVDELAY) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_separate_available =
BIT(IIO_CHAN_INFO_SAMP_FREQ),
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 15/17] iio: adc: ad7768: Add VCM regulator support
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (13 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 14/17] iio: adc: ad7768: Add per-channel conversion delay Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:15 ` [PATCH v6 16/17] iio: adc: ad7768: Register GPIO auxiliary device Janani Sunil
2026-09-04 14:15 ` [PATCH v6 17/17] Documentation: iio: Add AD7768 Documentation Janani Sunil
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Expose the on-chip common mode voltage output through the regulator
framework. Support the three fixed output levels and the
supply-dependent AVDD1/2 setting.
Keep the ADC runtime active while VCM is enabled and release the runtime
PM reference when the output is disabled.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/Kconfig | 2 +-
drivers/iio/adc/ad7768.c | 162 ++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 161 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 5164608fa418..c784bee0fd65 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -434,7 +434,7 @@ config AD7766
config AD7768
tristate "Analog Devices AD7768/AD7768-4 ADC driver"
depends on SPI
- depends on REGULATOR || COMPILE_TEST
+ select REGULATOR
select IIO_BUFFER
select IIO_BACKEND
select REGMAP
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 9c6bebb1857f..4acf89bdad12 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -23,6 +23,7 @@
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
#include <linux/time.h>
@@ -50,6 +51,10 @@
#define AD7768_POWER_MODE_LVDS_ENABLE BIT(3)
#define AD7768_POWER_MODE_MCLK_DIV_MSK GENMASK(1, 0)
+#define AD7768_REG_GENERAL_CONFIG 0x05
+#define AD7768_GEN_CONFIG_VCM_SEL_MSK GENMASK(1, 0)
+#define AD7768_GEN_CONFIG_VCM_PD BIT(4)
+
#define AD7768_REG_DATA_CONTROL 0x06
#define AD7768_DATA_CONTROL_SPI_RESET_1 0x03
#define AD7768_DATA_CONTROL_SPI_RESET_2 0x02
@@ -186,11 +191,157 @@ struct ad7768_state {
unsigned int power_mode_idx;
enum ad7768_filter_type ch_filter[AD7768_MAX_CHANNEL];
struct iio_backend *back;
+ struct regulator_dev *vcm_rdev;
+ unsigned int avdd1_uV;
unsigned int vref_uV[2];
__be16 d16 __aligned(IIO_DMA_MINALIGN);
};
+static const unsigned int ad7768_vcm_voltage_table[] = {
+ 0, 1650000, 2500000, 2140000,
+};
+
+static int ad7768_vcm_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+
+ if (selector >= ARRAY_SIZE(ad7768_vcm_voltage_table))
+ return -EINVAL;
+
+ if (!selector)
+ return DIV_ROUND_CLOSEST(st->avdd1_uV, 2);
+
+ return ad7768_vcm_voltage_table[selector];
+}
+
+static int ad7768_vcm_enable(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(regmap_get_device(st->regmap));
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_PD);
+ if (ret)
+ pm_runtime_put_autosuspend(regmap_get_device(st->regmap));
+
+ return ret;
+}
+
+static int ad7768_vcm_disable(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = regmap_set_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_PD);
+ if (ret)
+ return ret;
+
+ pm_runtime_put_autosuspend(regmap_get_device(st->regmap));
+
+ return 0;
+}
+
+static int ad7768_vcm_is_enabled(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = regmap_test_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_PD);
+ if (ret < 0)
+ return ret;
+
+ return !ret;
+}
+
+static int ad7768_vcm_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_SEL_MSK,
+ FIELD_PREP(AD7768_GEN_CONFIG_VCM_SEL_MSK,
+ selector));
+}
+
+static int ad7768_vcm_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(regmap_get_device(st->regmap), pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, AD7768_REG_GENERAL_CONFIG, &val);
+ if (ret)
+ return ret;
+
+ return FIELD_GET(AD7768_GEN_CONFIG_VCM_SEL_MSK, val);
+}
+
+static const struct regulator_ops ad7768_vcm_ops = {
+ .enable = ad7768_vcm_enable,
+ .disable = ad7768_vcm_disable,
+ .is_enabled = ad7768_vcm_is_enabled,
+ .list_voltage = ad7768_vcm_list_voltage,
+ .set_voltage_sel = ad7768_vcm_set_voltage_sel,
+ .get_voltage_sel = ad7768_vcm_get_voltage_sel,
+};
+
+static const struct regulator_desc ad7768_vcm_desc = {
+ .name = "vcm",
+ .of_match = "vcm-output",
+ .regulators_node = "regulators",
+ .n_voltages = ARRAY_SIZE(ad7768_vcm_voltage_table),
+ .ops = &ad7768_vcm_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+};
+
+static int ad7768_register_vcm_regulator(struct device *dev,
+ struct ad7768_state *st)
+{
+ struct regulator_config config = {
+ .dev = dev,
+ .driver_data = st,
+ };
+ int ret;
+
+ /*
+ * Start with VCM disabled so each enabled state is paired with the
+ * runtime PM reference acquired by ad7768_vcm_enable().
+ */
+ ret = regmap_set_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_PD);
+ if (ret)
+ return ret;
+
+ st->vcm_rdev = devm_regulator_register(dev, &ad7768_vcm_desc, &config);
+ return PTR_ERR_OR_ZERO(st->vcm_rdev);
+}
+
static const unsigned int ad7768_dec_rate[AD7768_MAX_FREQ_PER_MODE] = {
32, 64, 128, 256, 512, 1024,
};
@@ -1502,11 +1653,13 @@ static int ad7768_probe(struct spi_device *spi)
"Failed to enable AVSS supply\n");
}
- ret = devm_regulator_get_enable(dev, "avdd1");
- if (ret)
+ ret = devm_regulator_get_enable_read_voltage(dev, "avdd1");
+ if (ret < 0)
return dev_err_probe(dev, ret,
"Failed to enable AVDD1 supply\n");
+ st->avdd1_uV = ret;
+
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(ad7768_supply_names),
ad7768_supply_names);
@@ -1636,6 +1789,11 @@ static int ad7768_probe(struct spi_device *spi)
if (ret)
return ret;
+ ret = ad7768_register_vcm_regulator(dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to register VCM regulator\n");
+
return devm_iio_device_register(dev, indio_dev);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 16/17] iio: adc: ad7768: Register GPIO auxiliary device
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (14 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 15/17] iio: adc: ad7768: Add VCM regulator support Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
2026-09-04 14:15 ` [PATCH v6 17/17] Documentation: iio: Add AD7768 Documentation Janani Sunil
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Register an auxiliary device when the AD7768 is described as a GPIO
controller. This allows the GPIO driver to share the parent regmap and
runtime power-management state.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/iio/adc/Kconfig | 1 +
drivers/iio/adc/ad7768.c | 29 +++++++++++++++++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index c784bee0fd65..dea25315ed20 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -435,6 +435,7 @@ config AD7768
tristate "Analog Devices AD7768/AD7768-4 ADC driver"
depends on SPI
select REGULATOR
+ select AUXILIARY_BUS
select IIO_BUFFER
select IIO_BACKEND
select REGMAP
diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
index 4acf89bdad12..ecb7e8554d6d 100644
--- a/drivers/iio/adc/ad7768.c
+++ b/drivers/iio/adc/ad7768.c
@@ -6,6 +6,7 @@
*/
#include <linux/array_size.h>
+#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
@@ -1245,6 +1246,30 @@ static const struct iio_info ad7768_info = {
.update_scan_mode = ad7768_update_scan_mode,
};
+static int ad7768_gpio_adev_init(struct ad7768_state *st)
+{
+ struct device *dev = regmap_get_device(st->regmap);
+ struct spi_device *spi = to_spi_device(dev);
+ struct auxiliary_device *adev;
+ int id;
+
+ if (!device_property_read_bool(dev, "gpio-controller"))
+ return 0;
+
+ /*
+ * Use the SPI bus number and chip select to derive a stable per-device
+ * ID.
+ */
+ id = (spi->controller->bus_num << 8) | spi_get_chipselect(spi, 0);
+ adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, "gpio",
+ NULL, id);
+ if (!adev)
+ return dev_err_probe(dev, -ENODEV,
+ "Failed to create GPIO auxiliary device\n");
+
+ return 0;
+}
+
static int ad7768_configure_precharge_buffers(struct iio_dev *indio_dev,
struct ad7768_precharge_config *precharge_cfg)
{
@@ -1794,6 +1819,10 @@ static int ad7768_probe(struct spi_device *spi)
return dev_err_probe(dev, ret,
"Failed to register VCM regulator\n");
+ ret = ad7768_gpio_adev_init(st);
+ if (ret)
+ return ret;
+
return devm_iio_device_register(dev, indio_dev);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v6 17/17] Documentation: iio: Add AD7768 Documentation
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (15 preceding siblings ...)
2026-09-04 14:15 ` [PATCH v6 16/17] iio: adc: ad7768: Register GPIO auxiliary device Janani Sunil
@ 2026-09-04 14:15 ` Janani Sunil
16 siblings, 0 replies; 26+ messages in thread
From: Janani Sunil @ 2026-09-04 14:15 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil,
Jonathan Cameron
Add driver documentation for AD7768.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
Documentation/iio/ad7768.rst | 273 +++++++++++++++++++++++++++++++++++++++++++
Documentation/iio/index.rst | 1 +
MAINTAINERS | 1 +
3 files changed, 275 insertions(+)
diff --git a/Documentation/iio/ad7768.rst b/Documentation/iio/ad7768.rst
new file mode 100644
index 000000000000..49aeff27b00a
--- /dev/null
+++ b/Documentation/iio/ad7768.rst
@@ -0,0 +1,273 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+=============
+AD7768 driver
+=============
+
+ADC driver for Analog Devices Inc. AD7768 and AD7768-4 devices. The module name
+is ``ad7768``.
+
+Supported devices
+=================
+
+The following chips are supported by this driver:
+
+* `AD7768 <https://www.analog.com/en/products/ad7768.html>`_ -
+ 8-channel, 24-bit simultaneous sampling ADC
+* `AD7768-4 <https://www.analog.com/en/products/ad7768-4.html>`_ -
+ 4-channel, 24-bit simultaneous sampling ADC
+
+Supported features
+==================
+
+Clock sources
+-------------
+
+The master clock source is selected using the ``clock-names`` device tree
+property:
+
+* ``mclk`` selects a CMOS clock on XTAL2/MCLK. CLK_SEL must be low and XTAL1
+ must be connected to DGND.
+* ``xtal`` selects a crystal connected between XTAL1 and XTAL2. CLK_SEL must
+ be high. The driver drives FILTER/GPIO4 high and keeps channel 4 on the
+ AD7768 or channel 2 on the AD7768-4 enabled to maintain crystal excitation.
+* ``lvds`` selects a differential clock on XTAL1 and XTAL2. CLK_SEL must be
+ high. The driver drives FILTER/GPIO4 low before enabling the LVDS input.
+
+GPIO4 is reserved when ``xtal`` or ``lvds`` is selected.
+
+Power modes
+-----------
+
+The AD7768 family supports three power and performance modes:
+
+* **Low power mode** - Optimized for lowest power consumption
+* **Median mode** - Balanced power and performance
+* **Fast mode** - Highest performance with maximum sampling rates
+
+The driver initializes the device in fast mode and uses the maximum fast-mode
+output data rate as the default sampling frequency.
+
+When buffered capture starts, the driver selects the lowest-noise mode that can
+produce the requested output data rates for all enabled channels. Where output
+data rates overlap, fast mode is preferred over median mode, and median mode is
+preferred over low power mode. This prioritizes the lower RMS noise and higher
+dynamic range offered by a faster mode at the same output data rate.
+
+Data output configuration
+-------------------------
+
+The devices support flexible serial data output configurations:
+
+AD7768 data lines
+^^^^^^^^^^^^^^^^^
+
+* 1 data line (DOUT0) - Standard single-lane output
+* 2 data lines (DOUT0, DOUT1) - Dual-lane output for higher throughput
+* 8 data lines (DOUT0-DOUT7) - Maximum throughput, one line per channel
+
+AD7768-4 data lines
+^^^^^^^^^^^^^^^^^^^
+
+* 1 data line (DOUT0) - Standard single-lane output
+* 4 data lines (DOUT0-DOUT3) - Maximum throughput, one line per channel
+
+The number of data lines can be configured via the ``adi,data-lines-number``
+device tree property. If omitted, the driver uses the maximum supported by the
+selected variant: eight lines for AD7768 and four lines for AD7768-4.
+
+Channel configuration
+---------------------
+
+Each channel can be individually configured with:
+
+Channel modes
+^^^^^^^^^^^^^
+
+* **Mode A** - First set of filter and decimation settings
+* **Mode B** - Second set of filter and decimation settings
+
+The hardware provides two mode profiles (A and B), each holding one
+(frequency, filter) combination. When buffered capture is started,
+enabled channels are grouped by their configured (frequency, filter)
+pair. Up to two distinct groups are supported; the driver automatically
+assigns each group to a mode slot and programs the hardware accordingly.
+
+Precharge and reference buffers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Per-channel buffer control for optimal signal integrity:
+
+* Positive input precharge buffer (``adi,prechargebuf-pos-enable``)
+* Negative input precharge buffer (``adi,prechargebuf-neg-enable``)
+* Positive reference buffer (``adi,refbuf-pos-enable``)
+* Negative reference buffer (``adi,refbuf-neg-enable``)
+
+Common mode voltage
+-------------------
+
+The VCM pin provides a buffered common-mode voltage output used to bias
+the analog inputs. The driver exposes this as a standard voltage regulator
+provider under a ``regulators`` subnode in the device tree. Supported
+output voltage levels are:
+
+* (AVDD1 - AVSS) / 2 - Mid-supply (hardware default), reported as half the
+ voltage provided by ``avdd1-supply``
+* 1,650,000 µV - 1.65V
+* 2,500,000 µV - 2.5V
+* 2,140,000 µV - 2.14V
+
+The regulator can be enabled and disabled at runtime using the standard
+regulator framework interfaces.
+
+The VCM circuitry is associated with channel 0. When VCM is used externally,
+``channel@0`` must be present in the device tree and channel 0 must remain
+enabled in the active scan mask. Placing channel 0 in standby disables the VCM
+output.
+
+Filter types
+------------
+
+Two digital filter types are available:
+
+* **Wideband** - Optimized for wide bandwidth applications
+* **Sinc5** - Fifth-order sinc filter for high rejection of out-of-band noise
+
+IIO backend support
+-------------------
+
+The driver integrates with IIO backends (e.g., AXI ADC) for high-speed data
+capture and DMA operations. Features include:
+
+* Automatic channel enable/disable based on scan mask
+* CRC on data interface. CRC replaces the header every 4th output sample.
+* High-throughput buffered data acquisition
+
+GPIO controller
+---------------
+
+The AD7768 includes a 5-pin GPIO controller for auxiliary digital I/O
+operations. The GPIO pins can be configured as inputs or outputs.
+
+GPIO4 is reserved when a crystal or LVDS master clock is used. GPIO0 through
+GPIO3 remain available in these configurations.
+
+Device attributes
+=================
+
+The following IIO attributes are available for each enabled channel:
+
+Voltage scale
+-------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - Attribute
+ - Description
+ * - ``in_voltage<N>_scale``
+ - Scale in millivolts per least significant bit for signed 24-bit data,
+ calculated as twice the differential reference voltage used by channel
+ N divided by 2\ :sup:`24`.
+
+Sampling frequency
+------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - Attribute
+ - Description
+ * - ``in_voltage<N>_sampling_frequency``
+ - Requested sampling frequency in Hz for channel N. Enabled channels are
+ grouped into up to two profiles at capture time.
+ * - ``in_voltage<N>_sampling_frequency_available``
+ - Available sampling frequencies in Hz for channel N across all power
+ modes, based on the master clock frequency. Buffer setup fails if no
+ single power mode supports the frequencies requested by all enabled
+ channels.
+
+Filter configuration
+---------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - Attribute
+ - Description
+ * - ``in_voltage<N>_filter_type``
+ - Requested filter type for channel N: "wideband" or "sinc5". It is
+ grouped with sampling frequency at capture time.
+ * - ``in_voltage<N>_filter_type_available``
+ - Available filter types for channel N: "wideband sinc5".
+
+Per-channel calibration
+-----------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - Attribute
+ - Description
+ * - ``in_voltage<N>_calibbias``
+ - Raw unsigned 24-bit channel offset register value.
+ * - ``in_voltage<N>_calibscale``
+ - Raw unsigned 24-bit channel gain register value.
+ * - ``in_voltage<N>_convdelay``
+ - Per-channel conversion delay. The driver exposes the sync phase offset
+ value in seconds with picosecond precision. Resolution and valid range
+ depend on the decimation ratio in use (see datasheet
+ Table 32).
+
+Device buffers
+==============
+
+This driver supports IIO buffered data acquisition through IIO backends.
+When used with compatible backends like the AXI ADC, it provides:
+
+* High-speed simultaneous sampling across all enabled channels
+* Hardware-driven data capture
+* DMA-based data transfer for minimal CPU overhead
+* CRC error detection
+
+See :doc:`iio_devbuf` for more information about IIO device buffers.
+
+Example usage
+=============
+
+.. code-block:: bash
+
+ # Read current sampling frequency for channel 0
+ cat /sys/bus/iio/devices/iio:device0/in_voltage0_sampling_frequency
+
+ # Update sampling frequency for channel 0
+ echo 8000 > /sys/bus/iio/devices/iio:device0/in_voltage0_sampling_frequency
+
+ # Read current filter type for channel 0
+ cat /sys/bus/iio/devices/iio:device0/in_voltage0_filter_type
+
+ # List available filter types for channel 0
+ cat /sys/bus/iio/devices/iio:device0/in_voltage0_filter_type_available
+
+ # Update filter type for channel 0 to wideband
+ echo wideband > /sys/bus/iio/devices/iio:device0/in_voltage0_filter_type
+
+ # Buffer setup fails if enabled channels request more than two distinct
+ # (sampling frequency, filter type) combinations.
+
+ # Read calibration scale for channel 0
+ cat /sys/bus/iio/devices/iio:device0/in_voltage0_calibscale
+
+ # Read conversion delay for channel 0
+ cat /sys/bus/iio/devices/iio:device0/in_voltage0_convdelay
+
+
+Unimplemented features
+======================
+
+* CRC message every 16 samples (CRC_SEL configuration) - currently only
+ supports CRC every 4 samples
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index b02b879b053a..73c58cec7620 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -29,6 +29,7 @@ Industrial I/O Kernel Drivers
ad7380
ad7606
ad7625
+ ad7768
ad7944
ade9000
adf41513
diff --git a/MAINTAINERS b/MAINTAINERS
index de91903b0b45..93eab996c529 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1663,6 +1663,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
+F: Documentation/iio/ad7768.rst
F: drivers/iio/adc/ad7768.c
ANALOG DEVICES INC AD7780 DRIVER
--
2.43.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768
2026-09-04 14:14 ` [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768 Janani Sunil
@ 2026-09-04 14:21 ` sashiko-bot
2026-09-04 17:08 ` Conor Dooley
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-04 14:21 UTC (permalink / raw)
To: Janani Sunil; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The binding defines properties for a GPIO controller but fails to include `gpio-controller` and `#gpio-cells` in the `required` list.
--
commit bc42d987ad118f0b1686ed33657b36a45a4a5392
Author: Janani Sunil <janani.sunil@analog.com>
dt-bindings: iio: adc: Add AD7768
Devicetree Bindings for AD7768-4 (4 channel) and AD7768 (8 channel)
simultaneous sampling ADCs.
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
> new file mode 100644
> index 0000000000000..d8e489fa1f6ff
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
[ ... ]
> + sync-in-gpios:
> + maxItems: 1
> + description:
> + GPIO connected to the SYNC_IN pin. Receives the synchronization pulse
> + generated on SYNC_OUT in response to a START pulse. Must be synchronous
> + to the MCLK clock domain. In multi-device or daisy-chained systems,
> + two successive synchronization pulses must be applied to guarantee all
> + ADCs are synchronized.
> +
> + gpio-controller: true
> +
> + '#gpio-cells':
> + const: 2
> +
> + adi,data-lines-number:
[ ... ]
> +required:
> + - compatible
> + - reg
> + - clocks
> + - clock-names
> + - avdd1-supply
> + - avdd2-supply
> + - iovdd-supply
> + - ref1p-supply
> + - ref2p-supply
> + - io-backends
[Severity: Medium]
The binding defines gpio-controller and #gpio-cells earlier in the schema,
indicating the hardware has GPIO provider capabilities. Should these
properties be added to this required list?
When hardware acts as a provider, the subsystem guidelines require the
corresponding standard properties to be explicitly listed as required.
Without this, incomplete device tree nodes that omit the GPIO properties
will silently pass schema validation, which might lead to runtime failures
if dependent devices attempt to use the GPIO functionality.
> +
> +allOf:
> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
> + - if:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com?part=2
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v6 04/17] iio: adc: adi-axi-adc: Add support for CRC
2026-09-04 14:14 ` [PATCH v6 04/17] iio: adc: adi-axi-adc: " Janani Sunil
@ 2026-09-04 14:24 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-04 14:24 UTC (permalink / raw)
To: Janani Sunil; +Cc: robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] CRC operations are not hooked up for all AXI ADC IP core variants
--
commit 432feaef6b44e5ab8e03ad8059b2636d78ecf5d5
Author: Janani Sunil <janani.sunil@analog.com>
iio: adc: adi-axi-adc: Add support for CRC
Add support for enabling and disabling Cyclic Redundancy Check (CRC)
processing in the AXI ADC backend. CRC provides data integrity verification
for high-speed ADC data streams, ensuring reliable data transfer between
the ADC frontend and backend processing systems.
> diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
> index fc7cc92b35f0a..8b7c45d51dbe8 100644
> --- a/drivers/iio/adc/adi-axi-adc.c
> +++ b/drivers/iio/adc/adi-axi-adc.c
[ ... ]
> @@ -47,6 +47,9 @@
> #define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
>
> #define ADI_AXI_ADC_REG_CNTRL_3 0x004c
> +/* Bit 8 is common to all AXI ADC cores; bits 7-0 are per-HDL-project. */
> +#define ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK BIT(8)
> +
> #define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2)
> #define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0)
> #define AXI_AD485X_PACKET_FORMAT_20BIT 0x0
[ ... ]
> @@ -644,6 +663,8 @@ static const struct iio_backend_ops adi_axi_adc_ops = {
> .num_lanes_set = axi_adc_num_lanes_set,
> .debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
> .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
> + .crc_enable = axi_adc_crc_enable,
> + .crc_disable = axi_adc_crc_disable,
> };
[Severity: Medium]
If the CRC enable bit is common to all AXI ADC cores as the comment above
suggests, should these callbacks also be added to adi_ad485x_ops and
adi_ad408x_ops in the same file?
For example:
drivers/iio/adc/adi-axi-adc.c:adi_ad485x_ops {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
...
}
If a frontend driver paired with an axi-ad485x or axi-ad408x backend attempts
to call iio_backend_crc_enable(), the call will fail with -EOPNOTSUPP because
the function pointers in those backend ops structures are NULL.
If the feature is intentionally unsupported on those variants, the code comment
claiming it is "common to all" seems misleading.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com?part=4
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v6 07/17] iio: adc: ad7768: Add power mode helper
2026-09-04 14:14 ` [PATCH v6 07/17] iio: adc: ad7768: Add power mode helper Janani Sunil
@ 2026-09-04 14:25 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-04 14:25 UTC (permalink / raw)
To: Janani Sunil; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Splitting the update of AD7768_REG_POWER_MODE into two separate regmap_update_bits calls creates a transient hardware state where the power mode and clock divider are mismatched, and adds redundant SPI bus overhead.
--
commit 9e0a5b0981ecb80ffeddcff0bde57100dda96fba
Author: Janani Sunil <janani.sunil@analog.com>
iio: adc: ad7768: Add power mode helper
Describe each power mode and its internal master-clock divisor in a
table. Replace the open-coded fast-mode setup with a helper that records
the active mode, preparing for dynamic mode selection.
> diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
> index 707f3ae70a95a..71db57a2b9dd6 100644
> --- a/drivers/iio/adc/ad7768.c
> +++ b/drivers/iio/adc/ad7768.c
[ ... ]
> @@ -335,6 +349,36 @@ static int ad7768_sync(struct ad7768_state *st)
> AD7768_DATA_CONTROL_SPI_SYNC);
> }
>
> +static int ad7768_set_power_mode(struct ad7768_state *st,
> + unsigned int mode_idx)
> +{
> + const struct ad7768_power_mode_info *mode_info;
> + int ret;
> +
> + mode_info = &ad7768_power_modes[mode_idx];
> + ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE,
> + AD7768_POWER_MODE_POWER_MODE_MSK,
> + FIELD_PREP(AD7768_POWER_MODE_POWER_MODE_MSK,
> + mode_info->mode));
> + if (ret)
> + return ret;
> +
> + ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE,
> + AD7768_POWER_MODE_MCLK_DIV_MSK,
> + FIELD_PREP(AD7768_POWER_MODE_MCLK_DIV_MSK,
> + mode_info->mode));
[Severity: Medium]
Does splitting this register update into two consecutive read-modify-write
operations to AD7768_REG_POWER_MODE create a problematic transient hardware
state?
Updating the analog power mode first and the digital clock divider second might
leave them temporarily mismatched. If the analog power mode is updated to a
lower state while the clock divider remains fast, could this violate internal
timing constraints and risk ADC glitches or unpredictable behavior?
Could these two fields be updated in a single regmap_update_bits() call to
avoid both the transient hardware state and the redundant SPI bus overhead?
> + if (ret)
> + return ret;
> +
> + ret = ad7768_sync(st);
> + if (ret)
> + return ret;
> +
> + st->power_mode_idx = mode_idx;
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com?part=7
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v6 05/17] iio: adc: Add AD7768 and AD7768-4 core support
2026-09-04 14:14 ` [PATCH v6 05/17] iio: adc: Add AD7768 and AD7768-4 core support Janani Sunil
@ 2026-09-04 14:31 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-04 14:31 UTC (permalink / raw)
To: Janani Sunil; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Overlapping active DMA mappings for the same memory buffer (`st->d16`) across concurrent SPI transfers violates the DMA API.
- [High] Incorrect channel bit mapping for CH_STANDBY and CH_MODE_SEL on AD7768-4 breaks logical-to-physical translation.
- [Medium] Hardware left powered on after device removal due to cancelled async suspend.
- [Medium] Incorrect polarity for reference buffer configuration (powers down instead of enabling).
- [Medium] Swapped POS and NEG bit assignments for precharge buffers.
- [Medium] Unbalanced IIO backend channel enablement on error path in `ad7768_update_scan_mode`.
- [Medium] Missing PM runtime idle notification at the end of probe leaves the device permanently active.
--
commit cad3d6d13491f167da3c31d541a31ac850d8d3b5
Author: Janani Sunil <janani.sunil@analog.com>
iio: adc: Add AD7768 and AD7768-4 core support
Add core support for the AD7768 and AD7768-4 simultaneous sampling ADCs.
Configure supplies, clock and reset, use a custom regmap bus for the SPI
protocol, and parse the enabled channels and input buffer settings from
devicetree.
> diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
> new file mode 100644
> index 0000000000000..c80056a9edd8c
> --- /dev/null
> +++ b/drivers/iio/adc/ad7768.c
> @@ -0,0 +1,1000 @@
> +// SPDX-License-Identifier: GPL-2.0
[ ... ]
> +#define AD7768_REG_PRECHARGE_BUF1 0x11
> +#define AD7768_REG_PRECHARGE_BUF2 0x12
> +#define AD7768_PREBUF_POS_EN(ch) BIT((ch) * 2)
> +#define AD7768_PREBUF_NEG_EN(ch) BIT(((ch) * 2) + 1)
[Severity: Medium]
Are these bit assignments for the precharge buffers swapped? According to the
AD7768 datasheet, the positive (POS) precharge buffers are assigned to odd bits,
and the negative (NEG) precharge buffers are assigned to even bits.
> +
> +#define AD7768_REG_REFP_BUF 0x13
> +#define AD7768_REG_REFN_BUF 0x14
[ ... ]
> +static u8 ad7768_all_standby_mask(const struct ad7768_state *st)
> +{
> + return GENMASK(st->chip_info->num_channels - 1, 0);
> +}
[Severity: High]
Does this generate the correct physical mask for the AD7768-4? The AD7768-4
maps its 4 logical channels to physical channels 0, 1, 4, and 5. This returns
GENMASK(3, 0) which is 0x0F, writing 0 to physical bits 4, 5, 6, and 7,
leaving them fully active.
[ ... ]
> +static int ad7768_regmap_read(void *context, const void *reg_buf,
> + size_t reg_size, void *val_buf, size_t val_size)
> +{
> + struct ad7768_state *st = spi_get_drvdata(context);
> + struct spi_device *spi = context;
> + struct spi_transfer t[] = {
> + {
> + .tx_buf = &st->d16,
> + .len = sizeof(st->d16),
> + .cs_change = 1,
> + }, {
> + /*
> + * Register responses are delayed by one CS frame. While
> + * receiving the response to this read, the device also
> + * decodes another command on SDI. Repeat the read
> + * command to avoid sending an unspecified dummy
> + * command.
> + */
> + .tx_buf = &st->d16,
> + .rx_buf = &st->d16,
> + .len = sizeof(st->d16),
> + },
> + };
[Severity: High]
Can this violate the DMA API? Both transfers use the same memory address
(&st->d16) for their buffers. When the SPI subsystem maps all transfers for
DMA concurrently, attempting to map the same memory region twice concurrently
(DMA_TO_DEVICE and DMA_BIDIRECTIONAL) can cause cache coherency issues on
non-coherent architectures.
[ ... ]
> +static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct ad7768_state *st = iio_priv(indio_dev);
> + unsigned long channel_mask;
> + unsigned long standby_mask;
> + int ret;
> +
> + channel_mask = ad7768_all_standby_mask(st);
> + standby_mask = channel_mask & ~*scan_mask;
> +
> + /*
> + * Crystal excitation requires channel 4 on AD7768 or channel 2 on
> + * AD7768-4 to remain active.
> + */
> + if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL)
> + __clear_bit(st->chip_info->num_channels / 2, &standby_mask);
> +
> + ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY,
> + channel_mask, standby_mask);
> + if (ret)
> + return ret;
> +
> + for (unsigned int c = 0; c < st->chip_info->num_channels; c++) {
> + if (test_bit(c, scan_mask))
> + ret = iio_backend_chan_enable(st->back, c);
> + else
> + ret = iio_backend_chan_disable(st->back, c);
> + if (ret)
> + return ret;
> + }
[Severity: Medium]
Will this leave the backend hardware out of sync if an error occurs? If one
operation fails, it returns the error immediately without unwinding the
previously modified channels.
> +
> + return 0;
> +}
[ ... ]
> +static int ad7768_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct ad7768_state *st = iio_priv(indio_dev);
> +
> + pm_runtime_put_autosuspend(regmap_get_device(st->regmap));
> + return 0;
> +}
[Severity: Medium]
Could this leave the hardware powered on after device removal?
If the driver is unbound while the IIO buffer is active,
devm_iio_device_register unwinds first, calling iio_device_unregister(),
which disables the buffer and triggers ad7768_buffer_postdisable().
Immediately after, devm_pm_runtime_set_active_enabled unwinds, calling
pm_runtime_disable() which cancels the pending autosuspend work. The
driver should ensure synchronous suspend if the buffer is active during
removal.
[ ... ]
> +static int ad7768_configure_precharge_buffers(struct iio_dev *indio_dev,
> + struct ad7768_precharge_config *precharge_cfg)
> +{
> + struct ad7768_state *st = iio_priv(indio_dev);
> + u8 prebuf1_val, prebuf2_val;
> + u16 prebuf_mask = 0;
> + u8 refbufp_val = 0;
> + u8 refbufn_val = 0;
> + int ret;
> +
> + for (unsigned int ch = 0; ch < indio_dev->num_channels; ch++) {
> + u8 channel = indio_dev->channels[ch].channel;
> +
> + if (precharge_cfg[channel].prebufp_en)
> + prebuf_mask |= AD7768_PREBUF_POS_EN(channel);
> +
> + if (precharge_cfg[channel].prebufn_en)
> + prebuf_mask |= AD7768_PREBUF_NEG_EN(channel);
> +
> + if (precharge_cfg[channel].refbufp)
> + refbufp_val |= ad7768_channel_mask(st, channel);
> +
> + if (precharge_cfg[channel].refbufn)
> + refbufn_val |= ad7768_channel_mask(st, channel);
> + }
[Severity: Medium]
Does this apply the incorrect polarity for the reference buffer configuration?
When adi,refbuf-pos-enable or adi,refbuf-neg-enable are provided in the device
tree, the driver sets the corresponding bit to 1. According to the AD7768
datasheet, setting a bit to 1 in the REFP_BUF and REFN_BUF registers powers down
the buffer.
[ ... ]
> + device_for_each_named_child_node_scoped(dev, child, "channel") {
> + u32 channel;
> +
> + ret = fwnode_property_read_u32(child, "reg", &channel);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Failed to parse reg of %pfwP\n",
> + child);
> +
> + if (channel >= st->chip_info->num_channels)
> + return dev_err_probe(dev, -ECHRNG,
> + "Invalid channel %u in firmware\n",
> + channel);
> +
> + ret = regmap_clear_bits(st->regmap, AD7768_REG_CH_STANDBY,
> + BIT(channel));
[Severity: High]
Does this use logical indices instead of physical bits for the AD7768-4? The
AD7768-4 uses physical channels 0, 1, 4, and 5. Using BIT(channel) will update
physical bits 0-3 instead of the mapped hardware bits.
> + if (ret)
> + return ret;
> +
[ ... ]
> + pm_runtime_set_autosuspend_delay(dev, 2000);
> + pm_runtime_use_autosuspend(dev);
> + ret = devm_pm_runtime_set_active_enabled(dev);
> + if (ret)
> + return ret;
> +
> + return devm_iio_device_register(dev, indio_dev);
> +}
[Severity: Medium]
Does this leave the device permanently active? This configures autosuspend and
enables runtime PM, which transitions the device to RPM_ACTIVE with a usage
count of 0, but fails to call pm_request_idle(dev). As a result, the autosuspend
timer is never triggered, leaving the device fully powered indefinitely until
a user explicitly accesses it.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com?part=5
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v6 14/17] iio: adc: ad7768: Add per-channel conversion delay
2026-09-04 14:15 ` [PATCH v6 14/17] iio: adc: ad7768: Add per-channel conversion delay Janani Sunil
@ 2026-09-04 14:33 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-04 14:33 UTC (permalink / raw)
To: Janani Sunil; +Cc: devicetree, conor+dt, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Passing 64-bit typed variables as divisors to DIV_ROUND_CLOSEST_ULL, which explicitly requires a 32-bit divisor.
--
commit 741d5031c79e2b3ec4d6ed7618901a8c2dcc88cd
Author: Janani Sunil <janani.sunil@analog.com>
iio: adc: ad7768: Add per-channel conversion delay
This commit exposes the per-channel synchronization phase offset through the
IIO conversion-delay attribute. It derives the delay resolution from MCLK,
power mode and decimation rate, validates the requested delay, and programs
the corresponding phase register.
> diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
> index 802666f7abd45..9c6bebb1857fd 100644
> --- a/drivers/iio/adc/ad7768.c
> +++ b/drivers/iio/adc/ad7768.c
[ ... ]
> @@ -665,6 +678,92 @@ static int ad7768_find_matching_mode(const bool *mode_used,
> return -EINVAL;
> }
>
> +static int ad7768_get_convdelay_params(struct ad7768_state *st, unsigned int ch,
> + struct ad7768_convdelay_params *params)
> +{
> + const struct ad7768_freq_config *freq_cfg;
> + unsigned int dec_rate;
> + unsigned int mclk_div;
> + u64 mclk;
> + u64 mult;
[ ... ]
> + mclk = clk_get_rate(st->mclk);
> + if (!mclk)
> + return -EINVAL;
> +
> + mclk_div = ad7768_power_modes[st->power_mode_idx].mclk_div;
> + params->step_ps =
> + DIV_ROUND_CLOSEST_ULL(mult * PSEC_PER_SEC * mclk_div, mclk);
[Severity: Low]
Is it safe to pass the 64-bit variable mclk as the divisor here?
The DIV_ROUND_CLOSEST_ULL() macro explicitly requires a 32-bit divisor.
Internally, it invokes do_div(), which casts the divisor to a 32-bit
integer.
While this might not cause a functional issue here because mclk is bounded
and fits safely within 32 bits, it violates the API contract and can
trigger static analysis warnings due to the implicit downcasting of the
u64 type.
> +
> + return 0;
> +}
> +
> +static int ad7768_set_channel_convdelay(struct ad7768_state *st,
> + unsigned int ch)
> +{
> + u64 delay_ps = st->ch_convdelay_ps[ch];
> + struct ad7768_convdelay_params params;
> + u64 max_delay_ps;
> + u64 raw;
> + int ret;
> +
> + ret = ad7768_get_convdelay_params(st, ch, ¶ms);
> + if (ret)
> + return ret;
> +
> + max_delay_ps = (u64)params.max_raw * params.step_ps;
> + if (delay_ps > max_delay_ps)
> + return -EINVAL;
> +
> + raw = DIV_ROUND_CLOSEST_ULL(delay_ps, params.step_ps);
[Severity: Low]
Does this have the same type safety issue as above?
The params.step_ps struct member is declared as a u64, but is passed
to DIV_ROUND_CLOSEST_ULL() which requires a 32-bit divisor.
> + if (raw > params.max_raw)
> + return -EINVAL;
> +
> + return regmap_write(st->regmap, ad7768_phase_reg(st, ch),
> + raw << params.shift);
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com?part=14
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v6 13/17] iio: adc: ad7768: Add calibration controls
2026-09-04 14:15 ` [PATCH v6 13/17] iio: adc: ad7768: Add calibration controls Janani Sunil
@ 2026-09-04 14:35 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-04 14:35 UTC (permalink / raw)
To: Janani Sunil; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The IIO CALIBBIAS attribute incorrectly handles negative offsets, breaking two's complement offset calibration and violating the IIO ABI.
--
commit c96b4e9aa2c8a4f4230f53b26b0c87ecec23865f
Author: Janani Sunil <janani.sunil@analog.com>
iio: adc: ad7768: Add calibration controls
This commit exposes the per-channel offset and gain calibration registers
through the IIO calibbias and calibscale attributes.
> diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
> index f2c762688fb39..802666f7abd45 100644
> --- a/drivers/iio/adc/ad7768.c
> +++ b/drivers/iio/adc/ad7768.c
[ ... ]
> @@ -798,6 +865,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
> int *val, int *val2, long info)
> {
> struct ad7768_state *st = iio_priv(indio_dev);
[ ... ]
> + case IIO_CHAN_INFO_CALIBBIAS:
> + case IIO_CHAN_INFO_CALIBSCALE: {
> + bool is_gain = info == IIO_CHAN_INFO_CALIBSCALE;
> + unsigned int base_reg;
> + unsigned int calib;
> +
> + base_reg = ad7768_get_calib_reg_base(st, chan, is_gain);
> + ret = ad7768_read_calib_value(st, base_reg, &calib);
> + if (ret)
> + return ret;
> +
> + *val = calib;
[Severity: High]
Does this assignment correctly handle negative two's complement offsets?
Since calib is an unsigned integer populated from a 24-bit register, reading
a negative offset directly assigns it without sign extension. Will this cause
userspace to see massive positive integers (e.g., 0xFFFFFF) instead of the
actual negative offset, violating the IIO ABI?
> + return IIO_VAL_INT;
> + }
> default:
> return -EINVAL;
> }
[ ... ]
> @@ -816,24 +899,45 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
> static int ad7768_write_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan,
> int val, int val2, long info)
> {
[ ... ]
> - if (info == IIO_CHAN_INFO_SAMP_FREQ)
> + switch (info) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> return ad7768_set_sampling_freq(indio_dev, val, chan->channel);
> + case IIO_CHAN_INFO_CALIBBIAS:
> + if (val < 0 || val > AD7768_CALIB_REG_MSK)
> + return -EINVAL;
[Severity: High]
Is it expected to explicitly block negative values for the calibration bias?
By returning -EINVAL when val < 0, this rejects valid negative integer
offsets that users or auto-calibration scripts might attempt to write. Should
negative values be converted to their 24-bit two's complement representation
instead of being rejected?
>
> - return -EINVAL;
> + base_reg = ad7768_get_calib_reg_base(st, chan, false);
> + return ad7768_write_calib_value(st, base_reg, val);
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com?part=13
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles
2026-09-04 14:15 ` [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles Janani Sunil
@ 2026-09-04 14:45 ` sashiko-bot
0 siblings, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-04 14:45 UTC (permalink / raw)
To: Janani Sunil; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] ad7768_channel_mode_mask() ambiguously combines logical and physical channel bitmasks, potentially writing to reserved or unmapped hardware bits.
--
commit d77a0cc3ec50f3f6a3dbf446c22f27d8fae9209d
Author: Janani Sunil <janani.sunil@analog.com>
iio: adc: ad7768: Configure channel sampling profiles
At buffered capture setup, select the fastest compatible power mode for
the enabled channel rates. Group channels into the two hardware mode
profiles and program their decimation, mode selection, and data clock.
> diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c
> index 2020156eb6b61..868130ebcc8f1 100644
> --- a/drivers/iio/adc/ad7768.c
> +++ b/drivers/iio/adc/ad7768.c
[ ... ]
> @@ -204,6 +209,11 @@ static u8 ad7768_channel_mask(const struct ad7768_state *st, u8 ch)
> return BIT(st->chip_info->chan_map[ch]);
> }
>
> +static u8 ad7768_channel_mode_mask(const struct ad7768_state *st, u8 ch)
> +{
> + return BIT(ch) | ad7768_channel_mask(st, ch);
[Severity: Medium]
Does this bitwise OR inadvertently combine the logical channel index and the
physical channel mapping?
When this is applied to the AD7768_REG_CH_MODE_SEL register, it could result
in writing to reserved or unmapped hardware bits, which might lead to
unpredictable hardware state.
> +}
> +
> static u8 ad7768_all_standby_mask(const struct ad7768_state *st)
> {
> return GENMASK(st->chip_info->num_channels - 1, 0);
[ ... ]
> @@ -478,11 +544,66 @@ static int ad7768_update_scan_mode(struct iio_dev *indio_dev,
> if (st->clock_source == AD7768_CLOCK_SOURCE_XTAL)
> __clear_bit(st->chip_info->num_channels / 2, &standby_mask);
>
> + for_each_set_bit(c, scan_mask, st->chip_info->num_channels) {
> + unsigned int mask;
> + int mode;
> +
> + mode = ad7768_find_matching_mode(mode_used, mode_freq,
> + st->ch_freq[c]);
> + if (mode < 0)
> + return dev_err_probe(dev, -EINVAL,
> + "Over %d channel modes required\n",
> + AD7768_NUM_CHANNEL_MODES);
> +
> + mode_freq[mode] = st->ch_freq[c];
> + mode_used[mode] = true;
> +
> + mask = ad7768_channel_mode_mask(st, c);
> + ret = regmap_assign_bits(st->regmap, AD7768_REG_CH_MODE_SEL,
> + mask, mode);
[Severity: Medium]
This applies the multi-bit mask to the hardware configuration register.
Could this configure unintended channels or interfere with device compatibility?
> + if (ret)
> + return ret;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260904-ad7768-driver-v6-0-e4378f946bfb@analog.com?part=9
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768
2026-09-04 14:14 ` [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768 Janani Sunil
2026-09-04 14:21 ` sashiko-bot
@ 2026-09-04 17:08 ` Conor Dooley
1 sibling, 0 replies; 26+ messages in thread
From: Conor Dooley @ 2026-09-04 17:08 UTC (permalink / raw)
To: Janani Sunil
Cc: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, Randy Dunlap, linux,
linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König
[-- Attachment #1: Type: text/plain, Size: 444 bytes --]
On Fri, Sep 04, 2026 at 04:14:54PM +0200, Janani Sunil wrote:
> Devicetree Bindings for AD7768-4 (4 channel) and AD7768 (8 channel)
> simultaneous sampling ADCs.
>
> Signed-off-by: Janani Sunil <janani.sunil@analog.com>
One more binding that the nuances of are probably beyond me, but David
and Jonathan appear okay with judging from previous iterations.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2026-09-04 17:09 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04 14:14 [PATCH v6 00/17] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
2026-09-04 14:14 ` [PATCH v6 01/17] iio: adc: adi-axi-adc: Initialize state mutex Janani Sunil
2026-09-04 14:14 ` [PATCH v6 02/17] dt-bindings: iio: adc: Add AD7768 Janani Sunil
2026-09-04 14:21 ` sashiko-bot
2026-09-04 17:08 ` Conor Dooley
2026-09-04 14:14 ` [PATCH v6 03/17] iio: backend: Add support for CRC Janani Sunil
2026-09-04 14:14 ` [PATCH v6 04/17] iio: adc: adi-axi-adc: " Janani Sunil
2026-09-04 14:24 ` sashiko-bot
2026-09-04 14:14 ` [PATCH v6 05/17] iio: adc: Add AD7768 and AD7768-4 core support Janani Sunil
2026-09-04 14:31 ` sashiko-bot
2026-09-04 14:14 ` [PATCH v6 06/17] iio: adc: ad7768: Validate master clock rate Janani Sunil
2026-09-04 14:14 ` [PATCH v6 07/17] iio: adc: ad7768: Add power mode helper Janani Sunil
2026-09-04 14:25 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 08/17] iio: adc: ad7768: Derive output data rates Janani Sunil
2026-09-04 14:15 ` [PATCH v6 09/17] iio: adc: ad7768: Configure channel sampling profiles Janani Sunil
2026-09-04 14:45 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 10/17] iio: adc: ad7768: Add sampling frequency controls Janani Sunil
2026-09-04 14:15 ` [PATCH v6 11/17] iio: adc: ad7768: Add per-channel filter controls Janani Sunil
2026-09-04 14:15 ` [PATCH v6 12/17] iio: adc: ad7768: Wait for digital filters to settle Janani Sunil
2026-09-04 14:15 ` [PATCH v6 13/17] iio: adc: ad7768: Add calibration controls Janani Sunil
2026-09-04 14:35 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 14/17] iio: adc: ad7768: Add per-channel conversion delay Janani Sunil
2026-09-04 14:33 ` sashiko-bot
2026-09-04 14:15 ` [PATCH v6 15/17] iio: adc: ad7768: Add VCM regulator support Janani Sunil
2026-09-04 14:15 ` [PATCH v6 16/17] iio: adc: ad7768: Register GPIO auxiliary device Janani Sunil
2026-09-04 14:15 ` [PATCH v6 17/17] Documentation: iio: Add AD7768 Documentation Janani Sunil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox