* [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support
@ 2026-07-09 8:50 Janani Sunil
2026-07-09 8:50 ` [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 Janani Sunil
` (5 more replies)
0 siblings, 6 replies; 27+ messages in thread
From: Janani Sunil @ 2026-07-09 8:50 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
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Janani Sunil
Add support for Analog Devices AD7768 and AD7768-4, 24-bit simultaneous
sampling Sigma-Delta ADCs with configurable power and performance modes.
Datasheet:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768.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.
- Configurable power modes (fast, median, low power) with associated
decimation rate and output data rate tables.
- Per-channel offset, gain, and phase calibration 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.
- GPIO auxiliary driver to expose the 5 on-chip GPIO pins.
**Patch Summary:**
1. **dt-bindings**: Binding documentation for AD7768 and AD7768-4,
covering SPI interface, clock, power supplies, reference voltage,
reset GPIO, serial data lane count, and on-chip GPIO controller.
2. **iio: backend**: Add CRC enable/disable backend operation for data
integrity verification over the high-speed serial interface.
3. **iio: adi-axi-adc**: Implement the new CRC backend operation.
4. **driver**: Core IIO driver for AD7768/AD7768-4 with regmap, power
mode, calibration, and IIO backend support. Registers an auxiliary
device to expose the GPIO functionality.
5. **gpio**: Auxiliary bus GPIO driver for the AD7768's 5 general-purpose
I/O pins, reusing the parent's regmap and runtime PM.
6. **docs**: 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: 5 general-purpose I/O pins exposed via an auxiliary bus
driver, sharing the parent's regmap and runtime PM.
To: Nuno Sá <nuno.sa@analog.com>
To: Michael Hennerich <Michael.Hennerich@analog.com>
To: Jonathan Cameron <jic23@kernel.org>
To: David Lechner <dlechner@baylibre.com>
To: Andy Shevchenko <andy@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Olivier Moysan <olivier.moysan@foss.st.com>
To: Philipp Zabel <p.zabel@pengutronix.de>
To: Linus Walleij <linusw@kernel.org>
To: Bartosz Golaszewski <brgl@kernel.org>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
Cc: linux@analog.com
Cc: linux-iio@vger.kernel.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-gpio@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Cc: jananisunil.dev@gmail.com
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
Janani Sunil (6):
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 IIO Driver support
gpio: ad7768: Add AD7768 GPIO auxiliary driver
Documentation: iio: Add AD7768 Documentation
.../devicetree/bindings/iio/adc/adi,ad7768.yaml | 285 +++++
Documentation/iio/ad7768.rst | 207 ++++
Documentation/iio/index.rst | 1 +
MAINTAINERS | 10 +
drivers/gpio/Kconfig | 10 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ad7768.c | 208 ++++
drivers/iio/adc/Kconfig | 16 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ad7768.c | 1291 ++++++++++++++++++++
drivers/iio/adc/adi-axi-adc.c | 20 +
drivers/iio/industrialio-backend.c | 33 +
include/linux/iio/backend.h | 4 +
13 files changed, 2087 insertions(+)
---
base-commit: b75697ef53e435d894833dad2a8c2fa882cb9b19
change-id: 20260708-ad7768-driver-10cd15296c20
Best regards,
--
Janani Sunil <janani.sunil@analog.com>
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 2026-07-09 8:50 [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil @ 2026-07-09 8:50 ` Janani Sunil 2026-07-09 8:59 ` sashiko-bot ` (2 more replies) 2026-07-09 8:50 ` [PATCH 2/6] iio: backend: Add support for CRC Janani Sunil ` (4 subsequent siblings) 5 siblings, 3 replies; 27+ messages in thread From: Janani Sunil @ 2026-07-09 8:50 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 Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev, Janani Sunil Devicetree Bindings for AD7768-4 (4 channel) and AD7768 (8 channel) simultaneous sampling ADC Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- .../devicetree/bindings/iio/adc/adi,ad7768.yaml | 285 +++++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 292 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..b74fe6aef01c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml @@ -0,0 +1,285 @@ +# 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 a 8-channel, 24-bit simultaneous sampling ADC with configurable + power and performance modes. The AD7768-4 is a 4-channel version. + + Datasheet at: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768.pdf + +properties: + compatible: + enum: + - adi,ad7768 + - adi,ad7768-4 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: Master clock (MCLK) + + avdd-supply: + description: Analog power supply AVDD1 (4.5V to 5.5V) + + avss-supply: + description: Analog ground/negative supply AVSS (0V to -2.75V) + + dvdd-supply: + description: Analog power supply AVDD2 (2.0V to 5.5V) + + iovdd-supply: + description: Digital I/O power supply (1.8V or 2.25V to 3.6V) + + vref-supply: + description: ADC reference voltage supply + + reset-gpios: + maxItems: 1 + description: GPIO connected to the active-low RESET pin + + 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 data output lines used for serial interface. + AD7768 supports 1, 2, or 8 lines. AD7768-4 supports 1 or 4 lines. + + adi,common-mode-output: + $ref: /schemas/types.yaml#/definitions/string + enum: + - avdd-avss-half + - 1.65V + - 2.5V + - 2.14V + description: + Common mode voltage output selection. + + adi,vcm-power-down: + type: boolean + description: Power down the common mode output buffer + + adi,power-mode: + $ref: /schemas/types.yaml#/definitions/string + enum: + - low + - median + - fast + description: + Power mode selection. + + 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,ch-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 1 + description: | + Channel mode selection. The AD7768 supports two independent + configuration profiles (Mode A and Mode B) for filter and + decimation settings. Each channel can be assigned to either mode: + 0 - Channel uses Mode A filter and decimation settings + 1 - Channel uses Mode B filter and decimation settings + + adi,prebuf-pos-en: + type: boolean + description: Enable positive input precharge buffer + + adi,prebuf-neg-en: + type: boolean + description: Enable negative input precharge buffer + + adi,refbuf-pos-en: + type: boolean + description: Enable positive reference buffer + + adi,refbuf-neg-en: + type: boolean + description: Enable negative reference buffer + + required: + - reg + - adi,ch-mode + + additionalProperties: false + +required: + - compatible + - reg + - clocks + - avdd-supply + - avss-supply + - dvdd-supply + - iovdd-supply + - vref-supply + - adi,data-lines-number + - adi,common-mode-output + - io-backends + +dependencies: + gpio-controller: + - '#gpio-cells' + '#gpio-cells': + - gpio-controller + +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>; + + avdd-supply = <&avdd_regulator>; + avss-supply = <&avss_regulator>; + dvdd-supply = <&dvdd_regulator>; + iovdd-supply = <&iovdd_regulator>; + vref-supply = <&vref_regulator>; + reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>; + + clocks = <&ad7768_mclk>; + + adi,data-lines-number = <8>; + adi,common-mode-output = "2.5V"; + adi,vcm-power-down; + adi,power-mode = "median"; + gpio-controller; + #gpio-cells = <2>; + + io-backends = <&iio_backend>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,ch-mode = <0>; + adi,prebuf-pos-en; + adi,refbuf-pos-en; + }; + + channel@1 { + reg = <1>; + adi,ch-mode = <1>; + adi,prebuf-pos-en; + adi,prebuf-neg-en; + adi,refbuf-pos-en; + adi,refbuf-neg-en; + }; + + channel@7 { + reg = <7>; + adi,ch-mode = <1>; + adi,prebuf-pos-en; + adi,refbuf-pos-en; + }; + }; + }; + + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@1 { + compatible = "adi,ad7768-4"; + reg = <1>; + spi-max-frequency = <1000000>; + + avdd-supply = <&avdd_regulator>; + avss-supply = <&avss_regulator>; + dvdd-supply = <&dvdd_regulator>; + iovdd-supply = <&iovdd_regulator>; + vref-supply = <&vref_regulator>; + + clocks = <&ad7768_mclk>; + + adi,data-lines-number = <4>; + adi,common-mode-output = "2.14V"; + adi,power-mode = "fast"; + + io-backends = <&iio_backend>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,ch-mode = <0>; + adi,prebuf-pos-en; + adi,refbuf-pos-en; + }; + + channel@3 { + reg = <3>; + adi,ch-mode = <1>; + adi,prebuf-pos-en; + adi,refbuf-pos-en; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 2b1ec46c5919..d667a0411f28 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1633,6 +1633,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] 27+ messages in thread
* Re: [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 2026-07-09 8:50 ` [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 Janani Sunil @ 2026-07-09 8:59 ` sashiko-bot 2026-07-09 15:43 ` David Lechner 2026-07-10 1:39 ` Jonathan Cameron 2 siblings, 0 replies; 27+ messages in thread From: sashiko-bot @ 2026-07-09 8:59 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] Hardware Variant Required Properties Violation: Missing GPIO provider properties in the required list -- --- Patch [1]: [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 --- commit b348e8650b6dd32f354ad1a32b48736f30d333db 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 ADC > 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..b74fe6aef01c6 > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml [ ... ] > +required: > + - compatible > + - reg > + - clocks > + - avdd-supply > + - avss-supply > + - dvdd-supply > + - iovdd-supply > + - vref-supply > + - adi,data-lines-number > + - adi,common-mode-output > + - io-backends [Severity: Medium] Since this hardware provides GPIO capabilities, should gpio-controller and #gpio-cells be included in this required list? Omitting these allows incomplete device tree nodes to silently pass schema validation. [ ... ] > + - | > + spi { > + #address-cells = <1>; > + #size-cells = <0>; > + > + adc@1 { > + compatible = "adi,ad7768-4"; > + reg = <1>; > + spi-max-frequency = <1000000>; > + > + avdd-supply = <&avdd_regulator>; > + avss-supply = <&avss_regulator>; > + dvdd-supply = <&dvdd_regulator>; > + iovdd-supply = <&iovdd_regulator>; > + vref-supply = <&vref_regulator>; > + > + clocks = <&ad7768_mclk>; > + > + adi,data-lines-number = <4>; > + adi,common-mode-output = "2.14V"; > + adi,power-mode = "fast"; > + > + io-backends = <&iio_backend>; [Severity: Medium] If the GPIO properties are added to the required list, they will also need to be included in this example block to pass dt_binding_check validation. Could gpio-controller and #gpio-cells = <2>; be added here? > + > + #address-cells = <1>; > + #size-cells = <0>; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com?part=1 ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 2026-07-09 8:50 ` [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 Janani Sunil 2026-07-09 8:59 ` sashiko-bot @ 2026-07-09 15:43 ` David Lechner 2026-07-10 0:33 ` Jonathan Cameron 2026-07-10 1:39 ` Jonathan Cameron 2 siblings, 1 reply; 27+ messages in thread From: David Lechner @ 2026-07-09 15:43 UTC (permalink / raw) To: Janani Sunil, Nuno Sá, Michael Hennerich, Jonathan Cameron, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On 7/9/26 3:50 AM, Janani Sunil wrote: > Devicetree Bindings for AD7768-4 (4 channel) and AD7768 (8 channel) > simultaneous sampling ADC > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > .../devicetree/bindings/iio/adc/adi,ad7768.yaml | 285 +++++++++++++++++++++ > MAINTAINERS | 7 + > 2 files changed, 292 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..b74fe6aef01c > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml > @@ -0,0 +1,285 @@ > +# 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 a 8-channel, 24-bit simultaneous sampling ADC with configurable > + power and performance modes. The AD7768-4 is a 4-channel version. Probably worth mentioning that this binding is for SPI mode, not for pin control mode. And that the io-backend is for the data output interface, so consumes the DOUTx, DCLK, DRDY. Maybe also START? And SYNC_{IN,OUT}? > + > + Datasheet at: > + https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768.pdf A link to the example HDL project for the io-backend would be helpful too. > + > +properties: > + compatible: > + enum: > + - adi,ad7768 > + - adi,ad7768-4 > + > + reg: > + maxItems: 1 > + > + clocks: > + maxItems: 1 > + description: Master clock (MCLK) Could also be XTAL{1,2}. > + > + avdd-supply: > + description: Analog power supply AVDD1 (4.5V to 5.5V) > + > + avss-supply: > + description: Analog ground/negative supply AVSS (0V to -2.75V) > + What about AV{DD,SS}{1,2}{A,B} supplies? > + dvdd-supply: > + description: Analog power supply AVDD2 (2.0V to 5.5V) > + > + iovdd-supply: > + description: Digital I/O power supply (1.8V or 2.25V to 3.6V) > + > + vref-supply: > + description: ADC reference voltage supply There is more that one reference voltage input. so ref1-supply, ref2-supply. vref is the name of an internal signal, so doesn't make sense here. > + > + reset-gpios: > + maxItems: 1 > + description: GPIO connected to the active-low RESET pin > + > + 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 data output lines used for serial interface. This could be made a bit more clear that this is a secondary data output interface, not the SPI lines. > + AD7768 supports 1, 2, or 8 lines. AD7768-4 supports 1 or 4 lines. > + > + adi,common-mode-output: > + $ref: /schemas/types.yaml#/definitions/string > + enum: > + - avdd-avss-half > + - 1.65V > + - 2.5V > + - 2.14V > + description: > + Common mode voltage output selection. Why not using standard regulator provider bindings for this? > + > + adi,vcm-power-down: > + type: boolean > + description: Power down the common mode output buffer Is the buffer separate from the output? In that case I would expect buffer to be in the property name, otherwise this should just be part of the enum options above (and the default one at that). > + > + adi,power-mode: > + $ref: /schemas/types.yaml#/definitions/string > + enum: > + - low > + - median > + - fast > + description: > + Power mode selection. Unless there are pins that control this, it seems like it should be left up to the driver to decide how to set this. In this case, it looks like the power mode also influences sample rate which is normally something controlled at runtime. > + > + 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,ch-mode: > + $ref: /schemas/types.yaml#/definitions/uint32 > + minimum: 0 > + maximum: 1 > + description: | > + Channel mode selection. The AD7768 supports two independent > + configuration profiles (Mode A and Mode B) for filter and > + decimation settings. Each channel can be assigned to either mode: > + 0 - Channel uses Mode A filter and decimation settings > + 1 - Channel uses Mode B filter and decimation settings This could probably be handled in the driver. E.g. allow per-channel settings that just get stored in a data structure. Then when starting a buffered read check that all enabled channels have at most 2 different groups of settings. Then do all of the required register config at that time. > + > + adi,prebuf-pos-en: > + type: boolean > + description: Enable positive input precharge buffer > + > + adi,prebuf-neg-en: > + type: boolean > + description: Enable negative input precharge buffer > + > + adi,refbuf-pos-en: > + type: boolean > + description: Enable positive reference buffer > + > + adi,refbuf-neg-en: > + type: boolean > + description: Enable negative reference buffer > + > + required: > + - reg > + - adi,ch-mode > + > + additionalProperties: false > + > +required: > + - compatible > + - reg > + - clocks > + - avdd-supply > + - avss-supply > + - dvdd-supply > + - iovdd-supply > + - vref-supply > + - adi,data-lines-number Suggest making the highest number of lines the default instead of making this property required. > + - adi,common-mode-output Should probably default to disabled, unless it is always used? Changing it to a regulator provider makes this not relevant. > + - io-backends > + ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 2026-07-09 15:43 ` David Lechner @ 2026-07-10 0:33 ` Jonathan Cameron 2026-07-11 14:40 ` David Lechner 0 siblings, 1 reply; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 0:33 UTC (permalink / raw) To: David Lechner Cc: Janani Sunil, Nuno Sá, Michael Hennerich, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev > > + adi,common-mode-output: > > + $ref: /schemas/types.yaml#/definitions/string > > + enum: > > + - avdd-avss-half > > + - 1.65V > > + - 2.5V > > + - 2.14V > > + description: > > + Common mode voltage output selection. > > Why not using standard regulator provider bindings for this? Interesting question. If that was done there would need to be a consumer which means explicit modelling of any analog circuit. We do that in a few cases but so far (and yup this is a driver thing in a dt-binding) I don't think we have any way to consumer data when a backend is involved. > > > + > > + adi,vcm-power-down: > > + type: boolean > > + description: Power down the common mode output buffer > > Is the buffer separate from the output? In that case I would expect > buffer to be in the property name, otherwise this should just be > part of the enum options above (and the default one at that). > ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 2026-07-10 0:33 ` Jonathan Cameron @ 2026-07-11 14:40 ` David Lechner 0 siblings, 0 replies; 27+ messages in thread From: David Lechner @ 2026-07-11 14:40 UTC (permalink / raw) To: Jonathan Cameron Cc: Janani Sunil, Nuno Sá, Michael Hennerich, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On 7/9/26 7:33 PM, Jonathan Cameron wrote: >>> + adi,common-mode-output: >>> + $ref: /schemas/types.yaml#/definitions/string >>> + enum: >>> + - avdd-avss-half >>> + - 1.65V >>> + - 2.5V >>> + - 2.14V >>> + description: >>> + Common mode voltage output selection. >> >> Why not using standard regulator provider bindings for this? > > Interesting question. If that was done there would need to be > a consumer which means explicit modelling of any analog circuit. > We do that in a few cases but so far (and yup this is a driver thing > in a dt-binding) I don't think we have any way to consumer data when > a backend is involved. There is also the regulator-always-on property, so strictly speaking, a consumer is not required. > >> >>> + >>> + adi,vcm-power-down: >>> + type: boolean >>> + description: Power down the common mode output buffer >> >> Is the buffer separate from the output? In that case I would expect >> buffer to be in the property name, otherwise this should just be >> part of the enum options above (and the default one at that). >> ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 2026-07-09 8:50 ` [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 Janani Sunil 2026-07-09 8:59 ` sashiko-bot 2026-07-09 15:43 ` David Lechner @ 2026-07-10 1:39 ` Jonathan Cameron 2 siblings, 0 replies; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 1:39 UTC (permalink / raw) To: Janani Sunil Cc: Nuno Sá, Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev > + > + adi,prebuf-pos-en: Spell out precharge and probably enable as well so adi,prechargebuf-pos-enable > + type: boolean > + description: Enable positive input precharge buffer > + > + adi,prebuf-neg-en: > + type: boolean > + description: Enable negative input precharge buffer > + > + adi,refbuf-pos-en: > + type: boolean > + description: Enable positive reference buffer > + > + adi,refbuf-neg-en: > + type: boolean > + description: Enable negative reference buffer ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 2/6] iio: backend: Add support for CRC 2026-07-09 8:50 [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil 2026-07-09 8:50 ` [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 Janani Sunil @ 2026-07-09 8:50 ` Janani Sunil 2026-07-09 9:03 ` sashiko-bot 2026-07-10 0:36 ` Jonathan Cameron 2026-07-09 8:50 ` [PATCH 3/6] iio: adc: adi-axi-adc: " Janani Sunil ` (3 subsequent siblings) 5 siblings, 2 replies; 27+ messages in thread From: Janani Sunil @ 2026-07-09 8:50 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 Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev, Janani Sunil Add a backend operation to enable or disable Cyclic Redundancy Check processing for data integrity verification. When enabled, the backend will generate, verify, or process CRC information for data samples transmitted over the interface, allowing the host to detect corrupted samples. Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- drivers/iio/industrialio-backend.c | 33 +++++++++++++++++++++++++++++++++ include/linux/iio/backend.h | 4 ++++ 2 files changed, 37 insertions(+) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index f7a4be8ec320..379a426931cd 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 the CRC generation. + * @back: Backend device + * + * Enable Cyclic Redundancy Check processing for data integrity + * verification. When enabled, the backend will generate, verify, or process + * CRC information for data samples transmitted over the interface. + * + * 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 the CRC generation. + * @back: Backend device + * + * Disable Cyclic Redundancy Check processing. When disabled, the backend + * will stop generating, verifying, or processing CRC information for data samples. + * + * 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..197cf1ad5e68 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -189,6 +189,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 +239,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] 27+ messages in thread
* Re: [PATCH 2/6] iio: backend: Add support for CRC 2026-07-09 8:50 ` [PATCH 2/6] iio: backend: Add support for CRC Janani Sunil @ 2026-07-09 9:03 ` sashiko-bot 2026-07-10 0:36 ` Jonathan Cameron 1 sibling, 0 replies; 27+ messages in thread From: sashiko-bot @ 2026-07-09 9:03 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: - [Low] The newly added `crc_enable` and `crc_disable` function pointers in `struct iio_backend_ops` are missing their corresponding descriptions in the kernel-doc block. -- --- Patch [2]: [PATCH 2/6] iio: backend: Add support for CRC --- commit 98a340052f70f8ab707e22920844ec1a36237e6a Author: Janani Sunil <janani.sunil@analog.com> iio: backend: Add support for CRC This commit adds backend operations to enable or disable Cyclic Redundancy Check (CRC) processing. This allows the backend to handle data integrity verification for samples transmitted over the interface. [ ... ] > diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h > index 3f95ed1fdf9eb..197cf1ad5e68e 100644 > --- a/include/linux/iio/backend.h > +++ b/include/linux/iio/backend.h > @@ -189,6 +189,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); [Severity: Low] Are the kernel-doc descriptions for crc_enable and crc_disable missing from the documentation block above struct iio_backend_ops? > int (*ddr_enable)(struct iio_backend *back); > int (*ddr_disable)(struct iio_backend *back); > int (*data_stream_enable)(struct iio_backend *back); -- Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com?part=2 ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 2/6] iio: backend: Add support for CRC 2026-07-09 8:50 ` [PATCH 2/6] iio: backend: Add support for CRC Janani Sunil 2026-07-09 9:03 ` sashiko-bot @ 2026-07-10 0:36 ` Jonathan Cameron 1 sibling, 0 replies; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 0:36 UTC (permalink / raw) To: Janani Sunil Cc: Nuno Sá, Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On Thu, 9 Jul 2026 10:50:13 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > Add a backend operation to enable or disable Cyclic Redundancy Check > processing for data integrity verification. When enabled, the backend > will generate, verify, or process CRC information for data samples > transmitted over the interface, allowing the host to detect corrupted > samples. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > drivers/iio/industrialio-backend.c | 33 +++++++++++++++++++++++++++++++++ > include/linux/iio/backend.h | 4 ++++ > 2 files changed, 37 insertions(+) > > diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c > index f7a4be8ec320..379a426931cd 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 the CRC generation. > + * @back: Backend device > + * > + * Enable Cyclic Redundancy Check processing for data integrity > + * verification. When enabled, the backend will generate, verify, or process Verification should be on previous line - under 80 chars. > + * CRC information for data samples transmitted over the interface. > + * > + * 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 the CRC generation. > + * @back: Backend device > + * > + * Disable Cyclic Redundancy Check processing. When disabled, the backend > + * will stop generating, verifying, or processing CRC information for data samples. Odd line wrap. Keep to a consistent <= 80 chars unless there is a reason to do otherwise. > + * > + * RETURNS: > + * 0 on success, negative error number on failure. > + */ ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 3/6] iio: adc: adi-axi-adc: Add support for CRC 2026-07-09 8:50 [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil 2026-07-09 8:50 ` [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 Janani Sunil 2026-07-09 8:50 ` [PATCH 2/6] iio: backend: Add support for CRC Janani Sunil @ 2026-07-09 8:50 ` Janani Sunil 2026-07-09 9:14 ` sashiko-bot ` (3 more replies) 2026-07-09 8:50 ` [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support Janani Sunil ` (2 subsequent siblings) 5 siblings, 4 replies; 27+ messages in thread From: Janani Sunil @ 2026-07-09 8:50 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 Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev, Janani Sunil 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. Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- drivers/iio/adc/adi-axi-adc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 26b9c75bd4d8..8af42d26aac0 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -47,6 +47,8 @@ #define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1) #define ADI_AXI_ADC_REG_CNTRL_3 0x004c +#define ADI_AXI_ADC_REG_CNTRL3_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 @@ -567,6 +569,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_REG_CNTRL3_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_REG_CNTRL3_CRC_EN_MASK); +} + static const struct regmap_config axi_adc_regmap_config = { .val_bits = 32, .reg_bits = 32, @@ -615,6 +633,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] 27+ messages in thread
* Re: [PATCH 3/6] iio: adc: adi-axi-adc: Add support for CRC 2026-07-09 8:50 ` [PATCH 3/6] iio: adc: adi-axi-adc: " Janani Sunil @ 2026-07-09 9:14 ` sashiko-bot 2026-07-09 15:54 ` David Lechner ` (2 subsequent siblings) 3 siblings, 0 replies; 27+ messages in thread From: sashiko-bot @ 2026-07-09 9:14 UTC (permalink / raw) To: Janani Sunil; +Cc: devicetree, conor+dt, robh Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: New issues: - [Medium] Incomplete implementation: missing `crc_enable` and `crc_disable` callbacks in AD485X and AD408X specific operations structures. Pre-existing issues: - [High] The mutex `st->lock` is never initialized in the driver's probe function. -- --- Patch [3]: [PATCH 3/6] iio: adc: adi-axi-adc: Add support for CRC --- commit c99754ea278319045472336d04b62842c05a3731 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 26b9c75bd4d8e..8af42d26aac08 100644 > --- a/drivers/iio/adc/adi-axi-adc.c > +++ b/drivers/iio/adc/adi-axi-adc.c [ ... ] > @@ -615,6 +633,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] Did this patch introduce a regression where AD485X and AD408X backend operations are missing the new CRC callbacks? If a frontend driver for AD485X or AD408X tries to enable CRC via iio_backend_crc_enable(), it will return -EOPNOTSUPP because the callbacks are missing in their respective structures: drivers/iio/adc/adi-axi-adc.c: static const struct iio_backend_ops adi_ad485x_ops = { .enable = axi_adc_enable, ... .debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status), /* crc_enable and crc_disable are NULL */ }; Should adi_ad485x_ops and adi_ad408x_ops also be updated? [Severity: High] This isn't a bug introduced by this patch, but I noticed the mutex st->lock is never initialized during probe: drivers/iio/adc/adi-axi-adc.c:adi_axi_adc_probe() { ... st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); if (!st) return -ENOMEM; ... } Because devm_kzalloc only zero-initializes the memory, the internal wait_list for st->lock is left uninitialized. When concurrent access occurs and hits the mutex slow path, won't this cause a kernel panic due to a NULL pointer dereference? Should a call to devm_mutex_init() be added here? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com?part=3 ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 3/6] iio: adc: adi-axi-adc: Add support for CRC 2026-07-09 8:50 ` [PATCH 3/6] iio: adc: adi-axi-adc: " Janani Sunil 2026-07-09 9:14 ` sashiko-bot @ 2026-07-09 15:54 ` David Lechner 2026-07-10 0:39 ` Jonathan Cameron 2026-07-10 0:46 ` Jonathan Cameron 3 siblings, 0 replies; 27+ messages in thread From: David Lechner @ 2026-07-09 15:54 UTC (permalink / raw) To: Janani Sunil, Nuno Sá, Michael Hennerich, Jonathan Cameron, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On 7/9/26 3:50 AM, Janani Sunil wrote: > 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. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > drivers/iio/adc/adi-axi-adc.c | 20 ++++++++++++++++++++ > 1 file changed, 20 insertions(+) > > diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c > index 26b9c75bd4d8..8af42d26aac0 100644 > --- a/drivers/iio/adc/adi-axi-adc.c > +++ b/drivers/iio/adc/adi-axi-adc.c > @@ -47,6 +47,8 @@ > #define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1) > > #define ADI_AXI_ADC_REG_CNTRL_3 0x004c > +#define ADI_AXI_ADC_REG_CNTRL3_CRC_EN_MASK BIT(8) ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK to match the existing naming pattern. Also, add a comment that bit 8 is shared by all AXI ADC cores while bits 7-0 are custom per HDL project. (That is why they have names like AD485X instead of AXI_ADC). > + > #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 > @@ -567,6 +569,22 @@ static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg, > return regmap_write(st->regmap, reg, writeval); > } > ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 3/6] iio: adc: adi-axi-adc: Add support for CRC 2026-07-09 8:50 ` [PATCH 3/6] iio: adc: adi-axi-adc: " Janani Sunil 2026-07-09 9:14 ` sashiko-bot 2026-07-09 15:54 ` David Lechner @ 2026-07-10 0:39 ` Jonathan Cameron 2026-07-10 0:46 ` Jonathan Cameron 3 siblings, 0 replies; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 0:39 UTC (permalink / raw) To: Janani Sunil Cc: Nuno Sá, Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On Thu, 9 Jul 2026 10:50:14 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > 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. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > drivers/iio/adc/adi-axi-adc.c | 20 ++++++++++++++++++++ > 1 file changed, 20 insertions(+) > static const struct regmap_config axi_adc_regmap_config = { > .val_bits = 32, > .reg_bits = 32, > @@ -615,6 +633,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), Sashiko caught that this needs kernel-doc. https://sashiko.dev/#/patchset/20260709-ad7768-driver-v1-0-44e1194fd96a%40analog.com > + .crc_enable = axi_adc_crc_enable, > + .crc_disable = axi_adc_crc_disable, > }; > > static const struct iio_backend_info adi_axi_adc_generic = { > ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 3/6] iio: adc: adi-axi-adc: Add support for CRC 2026-07-09 8:50 ` [PATCH 3/6] iio: adc: adi-axi-adc: " Janani Sunil ` (2 preceding siblings ...) 2026-07-10 0:39 ` Jonathan Cameron @ 2026-07-10 0:46 ` Jonathan Cameron 3 siblings, 0 replies; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 0:46 UTC (permalink / raw) To: Janani Sunil Cc: Nuno Sá, Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On Thu, 9 Jul 2026 10:50:14 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > 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. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> The 'other things' I found bit from Sashiko is interesting. Far as I can tell it is right and [devm_]mutex_init() is missing for lock in struct adi_axi_adc_state(). Nuno, looks like it was from: 7ecb8ee5c93b ("iio: adc: adi-axi-adc: support digital interface calibration") Which indeed adds the lock with a mutex_init() Jonathan > static const struct iio_backend_info adi_axi_adc_generic = { > ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support 2026-07-09 8:50 [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil ` (2 preceding siblings ...) 2026-07-09 8:50 ` [PATCH 3/6] iio: adc: adi-axi-adc: " Janani Sunil @ 2026-07-09 8:50 ` Janani Sunil 2026-07-09 9:27 ` sashiko-bot ` (2 more replies) 2026-07-09 8:50 ` [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil 2026-07-09 8:50 ` [PATCH 6/6] Documentation: iio: Add AD7768 Documentation Janani Sunil 5 siblings, 3 replies; 27+ messages in thread From: Janani Sunil @ 2026-07-09 8:50 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 Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev, Janani Sunil Add support for AD7768 4/8 channel,simultaneous sampling Sigma-Delta ADC, supporting configurable power modes, decimation filters and data output lines Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 16 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad7768.c | 1291 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1309 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d667a0411f28..dc94e7803a7c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1639,6 +1639,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 d1b198cb8a80..1325cdbedea4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -429,6 +429,22 @@ 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 AUXILIARY_BUS + 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 0f90b75577ff..3e896c8aa7c3 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..f76a7081090e --- /dev/null +++ b/drivers/iio/adc/ad7768.c @@ -0,0 +1,1291 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7768 ADC driver + * + * Copyright 2018-2026 Analog Devices Inc. + */ + +#include <linux/array_size.h> +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/math.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.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/types.h> + +#include <linux/iio/backend.h> +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +#define AD7768_AUX_DEV_GPIO_NAME "gpio" +#define AD7768_REG_GPIO_CONTROL 0x0E + +/* AD7768 registers definition */ +#define AD7768_REG_CH_STANDBY 0x00 +#define AD7768_REG_CH_MODE(x) (0x01 + (x)) +#define AD7768_REG_CH_MODE_SEL 0x03 +#define AD7768_REG_POWER_MODE 0x04 +#define AD7768_REG_GENERAL_CONFIG 0x05 +#define AD7768_REG_DATA_CONTROL 0x06 +#define AD7768_REG_INTERFACE_CFG 0x07 +#define AD7768_REG_REV_ID 0x0A +#define AD7768_REG_PRECHARGE_BUF1 0x11 +#define AD7768_REG_PRECHARGE_BUF2 0x12 +#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_DIAGNOSTIC_MUX_CTRL 0x57 +#define AD7768_REG_MODULATOR_DELAY_CTRL 0x58 +#define AD7768_REG_CHOP_CTRL 0x59 + +/* AD7768_REG_CH_MODE */ +#define AD7768_CH_MODE_FILTER_TYPE_MSK BIT(3) +#define AD7768_CH_MODE_FILTER_TYPE_MODE(x) (((x) & 0x1) << 3) +#define AD7768_CH_MODE_GET_FILTER_TYPE(x) (((x) >> 3) & 0x1) +#define AD7768_CH_MODE_DEC_RATE_MSK GENMASK(2, 0) +#define AD7768_CH_MODE_DEC_RATE_MODE(x) (((x) & 0x7) << 0) + +/* AD7768_REG_CH_MODE_SEL */ +#define AD7768_CH_MODE_READ_MSK GENMASK(7, 0) +#define AD7768_CH_MODE_READ_MODE(x) ((x) & 0xFF) +#define AD7768_CH_MODE_READ_GET(x) ((x) & 0xFF) +#define AD7768_CH_MODE_SEL_8CH(ch) BIT(ch) +#define AD7768_CH_MODE_READ_CHN(n) BIT(n) +#define AD7768_GET_CH_MODE_8CH(ch, x) (!!((x) & BIT(ch))) + +/* AD7768_REG_POWER_MODE */ +#define AD7768_SLEEP_MODE_MSK BIT(7) +#define AD7768_POWER_MODE_POWER_MODE_MSK GENMASK(5, 4) +#define AD7768_POWER_MODE_POWER_MODE(x) (((x) & 0x3) << 4) +#define AD7768_POWER_MODE_GET_POWER_MODE(x) (((x) >> 4) & 0x3) +#define AD7768_POWER_MODE_MCLK_DIV_MSK GENMASK(1, 0) +#define AD7768_POWER_MODE_MCLK_DIV_MODE(x) (((x) & 0x3) << 0) + +/* AD7768_REG_DATA_CONTROL */ +#define AD7768_DATA_CONTROL_SPI_RESET_MSK GENMASK(1, 0) +#define AD7768_DATA_CONTROL_SPI_RESET_1 0x03 +#define AD7768_DATA_CONTROL_SPI_RESET_2 0x02 +#define AD7768_DATA_CONTROL_SPI_SYNC_MSK BIT(7) +#define AD7768_DATA_CONTROL_SPI_SYNC BIT(7) +#define AD7768_DATA_CONTROL_SPI_SYNC_CLEAR 0 + +/* AD7768_REG_INTERFACE_CFG */ +#define AD7768_INTERFACE_CFG_DCLK_DIV_MSK GENMASK(1, 0) +#define AD7768_INTERFACE_CFG_DCLK_DIV_MODE(x) (4 - ffs(x)) +#define AD7768_MAX_DCLK_DIV 8 + +#define AD7768_INTERFACE_CFG_CRC_SELECT_MSK GENMASK(3, 2) +/* only 4 samples CRC calculation support exists */ +#define AD7768_INTERFACE_CFG_CRC_SELECT FIELD_PREP(GENMASK(3, 2), 0x01) + +/* AD7768_REG_GENERAL_CONFIG */ +#define AD7768_GEN_CONFIG_VCM_SEL_MSK GENMASK(1, 0) +#define AD7768_GEN_CONFIG_VCM_PD BIT(4) + +/* AD7768_REG_PRECHARGE_BUF1 and 2*/ +#define AD7768_PRECHARGE_BUF1_MSK(val) ((val) & GENMASK(7, 0)) +#define AD7768_PRECHARGE_BUF2_MSK(val) (((val) & GENMASK(15, 8)) >> 8) +#define AD7768_4_PRECHARGE_BUF1_MSK(val) ((val) & GENMASK(3, 0)) +#define AD7768_4_PRECHARGE_BUF2_MSK(val) (((val) & GENMASK(7, 4)) >> 4) +#define AD7768_PREBUF_POS_EN(ch) BIT((ch) * 2) +#define AD7768_PREBUF_NEG_EN(ch) BIT(((ch) * 2) + 1) + +#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_OUTPUT_MODE_TWOS_COMPLEMENT 0x01 +#define AD7768_SAMPLE_SIZE 32 +#define MAX_FREQ_PER_MODE 6 +#define AD7768_MAX_CHANNEL 8 +#define AD7768_NUM_CHANNEL_MODES 2 +#define AD7768_CALIB_REG_MSB_MASK(val) (((val) & 0xFF0000) >> 16) +#define AD7768_CALIB_REG_MID_MASK(val) (((val) & 0x00FF00) >> 8) +#define AD7768_CALIB_REG_LSB_MASK(val) ((val) & 0x0000FF) +#define AD7768_REV_ID_VAL 0x06 + +enum ad7768_filter_type { + AD7768_FILTER_TYPE_WIDEBAND, + AD7768_FILTER_TYPE_SINC5, +}; + +enum ad7768_power_modes { + AD7768_LOW_POWER_MODE, + AD7768_MEDIAN_MODE, + AD7768_FAST_MODE, + AD7768_NUM_POWER_MODES +}; + +struct ad7768_precharge_config { + bool prebufp_en; + bool prebufn_en; + bool refbufp; + bool refbufn; +}; + +struct ad7768_freq_config { + unsigned int freq; + unsigned int dec_rate; +}; + +struct ad7768_avail_freq { + unsigned int n_freqs; + struct ad7768_freq_config freq_cfg[MAX_FREQ_PER_MODE]; +}; + +struct ad7768_chip_info { + const char *name; + unsigned int num_channels; + const struct iio_chan_spec channel[AD7768_MAX_CHANNEL]; + const struct regmap_config *regmap_config; + const unsigned int *available_datalines; + unsigned int num_datalines; +}; + +struct ad7768_state { + struct spi_device *spi; + struct regmap *regmap; + struct mutex lock; /* Protects device register access and configuration */ + struct clk *mclk; + unsigned int datalines; + unsigned int sampling_freq; + enum ad7768_power_modes power_mode; + const struct ad7768_chip_info *chip_info; + struct ad7768_avail_freq avail_freq[AD7768_NUM_POWER_MODES]; + unsigned int chn_mode[AD7768_MAX_CHANNEL]; + unsigned int mode_freq[AD7768_NUM_CHANNEL_MODES]; + unsigned int active_modes; + struct iio_backend *back; + + __be16 d16 __aligned(IIO_DMA_MINALIGN); +}; + +static const char * const ad7768_vcm_sel_str[] = { + "avdd-avss-half", "1.65V", "2.5V", "2.14V" +}; + +static const char * const ad7768_power_mode_str[] = { + "low", "median", "fast" +}; + +static const int ad7768_dec_rate[MAX_FREQ_PER_MODE] = { + 32, 64, 128, 256, 512, 1024 +}; + +static const int ad7768_mclk_div[3] = { + 32, 8, 4 +}; + +static const unsigned int ad7768_available_datalines[] = { + 1, 2, 8 +}; + +static const unsigned int ad7768_4_available_datalines[] = { + 1, 4 +}; + +static const char * const ad7768_supply_names[] = { + "avdd", "avss", "dvdd", "iovdd", "vref" +}; + +static u8 ad7768_map_power_mode_to_regval(u8 x) +{ + return x ? (x + 1) : 0; +} + +static u8 ad7768_4_get_refbuf(u8 ch) +{ + return (ch < 2) ? BIT(ch) : BIT(ch + 2); +} + +static u8 ad7768_ch_mode_sel_4ch(u8 ch) +{ + return (ch < 2) ? BIT(ch) : BIT(ch + 2); +} + +static int ad7768_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct spi_device *spi = context; + struct ad7768_state *st = spi_get_drvdata(spi); + unsigned int reg; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = &st->d16, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = &st->d16, + .rx_buf = &st->d16, + .len = 2, + }, + }; + + reg = *(const u8 *)reg_buf; + + st->d16 = cpu_to_be16(AD7768_SPI_READ_CMD | + FIELD_PREP(AD7768_SPI_REG_MASK, reg)); + + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + *(u8 *)val_buf = FIELD_GET(AD7768_SPI_DATA_MASK, be16_to_cpu(st->d16)); + + return ret; +} + +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: + return true; + 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: + return true; + 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, + .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, + .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) { + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) + return AD7768_REG_GAIN(chan->address); + return AD7768_4_REG_GAIN(chan->address); + } + + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) + return AD7768_REG_OFFSET(chan->address); + return AD7768_4_REG_OFFSET(chan->address); +} + +static int ad7768_read_calib_value(struct ad7768_state *st, + unsigned int base_reg, int *val) +{ + unsigned int msb, mid, lsb; + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, base_reg + 0, &msb); + if (ret) + return ret; + + ret = regmap_read(st->regmap, base_reg + 1, &mid); + if (ret) + return ret; + + ret = regmap_read(st->regmap, base_reg + 2, &lsb); + if (ret) + return ret; + + *val = (msb << 16) | (mid << 8) | lsb; + + return 0; +} + +static int ad7768_write_calib_value(struct ad7768_state *st, + unsigned int base_reg, int val) +{ + int ret; + + guard(mutex)(&st->lock); + + ret = regmap_write(st->regmap, base_reg, AD7768_CALIB_REG_MSB_MASK(val)); + if (ret) + return ret; + + ret = regmap_write(st->regmap, base_reg + 1, + AD7768_CALIB_REG_MID_MASK(val)); + if (ret) + return ret; + + return regmap_write(st->regmap, base_reg + 2, + AD7768_CALIB_REG_LSB_MASK(val)); +} + +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_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, 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_update_bits(st->regmap, AD7768_REG_DATA_CONTROL, + AD7768_DATA_CONTROL_SPI_SYNC_MSK, + AD7768_DATA_CONTROL_SPI_SYNC_CLEAR); + if (ret < 0) + return ret; + + return regmap_update_bits(st->regmap, AD7768_REG_DATA_CONTROL, + AD7768_DATA_CONTROL_SPI_SYNC_MSK, + AD7768_DATA_CONTROL_SPI_SYNC); +} + +static int ad7768_set_clk_divs(struct ad7768_state *st, + unsigned int freq, unsigned int ch) +{ + unsigned int mclk, dclk, dclk_div, i; + struct ad7768_freq_config f_cfg = {}; + unsigned int chan_per_doutx; + + mclk = clk_get_rate(st->mclk); + + chan_per_doutx = st->chip_info->num_channels / st->datalines; + if (!chan_per_doutx) + return -EINVAL; + + for (i = 0; i < st->avail_freq[st->power_mode].n_freqs; i++) { + f_cfg = st->avail_freq[st->power_mode].freq_cfg[i]; + if (freq == f_cfg.freq) + break; + } + + if (i == st->avail_freq[st->power_mode].n_freqs) + return -EINVAL; + + dclk = f_cfg.freq * AD7768_SAMPLE_SIZE * chan_per_doutx; + if (dclk > mclk) + return -EINVAL; + + /* Set dclk_div to the nearest power of 2 less than the original value */ + dclk_div = DIV_ROUND_CLOSEST_ULL(mclk, dclk); + if (dclk_div > AD7768_MAX_DCLK_DIV) + dclk_div = AD7768_MAX_DCLK_DIV; + else if (dclk_div > 0 && hweight32(dclk_div) != 1) + dclk_div = 1 << (fls(dclk_div) - 1); + + return regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG, + AD7768_INTERFACE_CFG_DCLK_DIV_MSK, + AD7768_INTERFACE_CFG_DCLK_DIV_MODE(dclk_div)); +} + +static int ad7768_set_channel_decimation(struct ad7768_state *st, + unsigned int freq, unsigned int ch) +{ + struct ad7768_freq_config f_cfg = {}; + unsigned int i; + + for (i = 0; i < st->avail_freq[st->power_mode].n_freqs; i++) { + f_cfg = st->avail_freq[st->power_mode].freq_cfg[i]; + if (freq == f_cfg.freq) + break; + } + + if (i == st->avail_freq[st->power_mode].n_freqs) + return -EINVAL; + + return regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(st->chn_mode[ch]), + AD7768_CH_MODE_DEC_RATE_MSK, + AD7768_CH_MODE_DEC_RATE_MODE(f_cfg.dec_rate)); +} + +static unsigned int ad7768_get_max_mode_freq(const struct ad7768_state *st, + unsigned int mode, + unsigned int mode_freq) +{ + unsigned int max_freq = 0; + unsigned int i, freq; + + for (i = 0; i < AD7768_NUM_CHANNEL_MODES; i++) { + if (!(st->active_modes & BIT(i))) + continue; + + freq = i == mode ? mode_freq : st->mode_freq[i]; + if (freq > max_freq) + max_freq = freq; + } + + return max_freq; +} + +static int ad7768_set_sampling_freq(struct iio_dev *indio_dev, + unsigned int freq, unsigned int ch) +{ + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int mode = st->chn_mode[ch]; + int ret = 0; + unsigned int max_freq; + + if (!freq) + return -EINVAL; + + guard(mutex)(&st->lock); + + max_freq = ad7768_get_max_mode_freq(st, mode, freq); + + ret = ad7768_set_clk_divs(st, max_freq, ch); + if (ret < 0) + return ret; + + ret = ad7768_set_channel_decimation(st, freq, ch); + if (ret < 0) + return ret; + + ret = ad7768_sync(st); + if (ret) + return ret; + + st->mode_freq[mode] = freq; + return 0; +} + +static int ad7768_set_channel_mode(struct iio_dev *indio_dev, unsigned int ch, + unsigned int mode) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + unsigned int mask; + + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) + mask = AD7768_CH_MODE_SEL_8CH(ch); + else + mask = ad7768_ch_mode_sel_4ch(ch); + + guard(mutex)(&st->lock); + + ret = regmap_update_bits(st->regmap, AD7768_REG_CH_MODE_SEL, mask, + mode ? mask : 0); + if (ret) + return ret; + + st->chn_mode[ch] = mode; + + return 0; +} + +static int ad7768_set_power_mode(struct iio_dev *indio_dev, unsigned int mode) +{ + struct ad7768_state *st = iio_priv(indio_dev); + struct ad7768_avail_freq avail_freq; + int max_mode_freq; + unsigned int regval; + int ret; + + if (mode >= AD7768_NUM_POWER_MODES) + return -EINVAL; + + guard(mutex)(&st->lock); + + st->power_mode = mode; + + regval = ad7768_map_power_mode_to_regval(mode); + ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_POWER_MODE_POWER_MODE_MSK, + AD7768_POWER_MODE_POWER_MODE(regval)); + if (ret < 0) + return ret; + + ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_POWER_MODE_MCLK_DIV_MSK, + AD7768_POWER_MODE_MCLK_DIV_MODE(regval)); + if (ret < 0) + return ret; + + avail_freq = st->avail_freq[mode]; + max_mode_freq = avail_freq.freq_cfg[avail_freq.n_freqs - 1].freq; + + st->sampling_freq = max_mode_freq; + + return ad7768_sync(st); +} + +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); + unsigned int base_reg; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: { + guard(mutex)(&st->lock); + *val = st->mode_freq[st->chn_mode[chan->channel]]; + return IIO_VAL_INT; + } + + case IIO_CHAN_INFO_CALIBBIAS: + base_reg = ad7768_get_calib_reg_base(st, chan, false); + ret = ad7768_read_calib_value(st, base_reg, val); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_CALIBSCALE: + base_reg = ad7768_get_calib_reg_base(st, chan, true); + ret = ad7768_read_calib_value(st, base_reg, val); + if (ret) + return ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PHASE: + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) + base_reg = AD7768_REG_PHASE(chan->address); + else + base_reg = AD7768_4_REG_PHASE(chan->address); + + ret = regmap_read(st->regmap, base_reg, val); + if (ret) + return ret; + + return IIO_VAL_INT; + 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; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad7768_set_sampling_freq(indio_dev, val, chan->channel); + + case IIO_CHAN_INFO_CALIBBIAS: + return ad7768_write_calib_value(st, + ad7768_get_calib_reg_base(st, chan, false), val); + + case IIO_CHAN_INFO_CALIBSCALE: + return ad7768_write_calib_value(st, + ad7768_get_calib_reg_base(st, chan, true), val); + + case IIO_CHAN_INFO_PHASE: + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) + base_reg = AD7768_REG_PHASE(chan->address); + else + base_reg = AD7768_4_REG_PHASE(chan->address); + + return regmap_write(st->regmap, base_reg, val); + + default: + return -EINVAL; + } +} + +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 int c; + int ret; + + for (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), +}; + +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), +}; + +static int ad7768_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + return pm_runtime_resume_and_get(&st->spi->dev); +} + +static int ad7768_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + pm_runtime_mark_last_busy(&st->spi->dev); + pm_runtime_put_autosuspend(&st->spi->dev); + return 0; +} + +static const struct iio_buffer_setup_ops ad7768_buffer_ops = { + .preenable = ad7768_buffer_preenable, + .postdisable = ad7768_buffer_postdisable, +}; + +static const struct iio_info ad7768_info = { + .debugfs_reg_access = ad7768_reg_access, + .read_raw = ad7768_read_raw, + .write_raw = ad7768_write_raw, + .update_scan_mode = ad7768_update_scan_mode, +}; + +static void ad7768_set_available_sampl_freq(struct ad7768_state *st) +{ + unsigned int mode; + unsigned int dec; + unsigned int mclk = clk_get_rate(st->mclk); + struct ad7768_avail_freq *avail_freq; + + for (mode = 0; mode < AD7768_NUM_POWER_MODES; mode++) { + avail_freq = &st->avail_freq[mode]; + for (dec = ARRAY_SIZE(ad7768_dec_rate); dec > 0; dec--) { + struct ad7768_freq_config freq_cfg; + + freq_cfg.dec_rate = dec - 1; + freq_cfg.freq = mclk / (ad7768_dec_rate[dec - 1] * + ad7768_mclk_div[mode]); + avail_freq->freq_cfg[avail_freq->n_freqs++] = freq_cfg; + } + } + + /* The max frequency is not supported in one data line configuration */ + if (st->datalines == 1) + st->avail_freq[AD7768_FAST_MODE].n_freqs--; +} + +static int ad7768_gpio_adev_init(struct ad7768_state *st) +{ + struct device *dev = &st->spi->dev; + struct auxiliary_device *adev; + int id; + + if (!device_property_read_bool(dev, "gpio-controller")) + return 0; + + id = (st->spi->controller->bus_num << 8) | spi_get_chipselect(st->spi, 0); + adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, + AD7768_AUX_DEV_GPIO_NAME, + (void *)st->chip_info->name, id); + if (!adev) + return dev_err_probe(dev, -ENODEV, + "Failed to create GPIO auxiliary device\n"); + + 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); + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + ret = regmap_update_bits(st->regmap, + AD7768_REG_CH_MODE(st->chn_mode[chan->address]), + AD7768_CH_MODE_FILTER_TYPE_MSK, + AD7768_CH_MODE_FILTER_TYPE_MODE(mode)); + if (ret < 0) + return ret; + + return ad7768_sync(st); +} + +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); + unsigned int regval; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = regmap_read(st->regmap, + AD7768_REG_CH_MODE(st->chn_mode[chan->address]), + ®val); + if (ret < 0) + return ret; + + return AD7768_CH_MODE_GET_FILTER_TYPE(regval); +} + +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); + int ret; + u8 ch; + u8 prebuf1_val, prebuf2_val; + u16 prebuf_mask = 0; + u8 refbufp_val = 0; + u8 refbufn_val = 0; + + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) { + for (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 |= BIT(channel); + + if (precharge_cfg[channel].refbufn) + refbufn_val |= BIT(channel); + } + + prebuf1_val = AD7768_PRECHARGE_BUF1_MSK(prebuf_mask); + prebuf2_val = AD7768_PRECHARGE_BUF2_MSK(prebuf_mask); + } else { + for (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_4_get_refbuf(channel); + + if (precharge_cfg[channel].refbufn) + refbufn_val |= ad7768_4_get_refbuf(channel); + } + + prebuf1_val = AD7768_4_PRECHARGE_BUF1_MSK(prebuf_mask); + prebuf2_val = AD7768_4_PRECHARGE_BUF2_MSK(prebuf_mask); + } + + ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF1, prebuf1_val); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF2, prebuf2_val); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, AD7768_REG_REFP_BUF, refbufp_val); + if (ret < 0) + return ret; + + return regmap_write(st->regmap, AD7768_REG_REFN_BUF, refbufn_val); +} + +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 const struct iio_chan_spec ad7768_channel_template = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_PHASE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .indexed = 1, + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + }, + .ext_info = ad7768_ext_info, +}; + +static void ad7768_init_chan(struct iio_chan_spec *chan, unsigned int channel) +{ + *chan = ad7768_channel_template; + chan->address = channel; + chan->channel = channel; + chan->scan_index = channel; +} + +static int ad7768_parse_config(struct iio_dev *indio_dev, + struct device *dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + const unsigned int *available_datalines; + struct ad7768_precharge_config precharge_cfg[AD7768_MAX_CHANNEL] = {}; + struct iio_chan_spec *chan; + unsigned int num_channels; + unsigned int channel; + unsigned int i, len, vcm_sel, vcm_pd, ch_mode, pwr_mode; + int chan_idx = 0; + int ret; + + num_channels = device_get_child_node_count(dev); + + if (!num_channels || num_channels > st->chip_info->num_channels) + return dev_err_probe(dev, -EINVAL, "Invalid number of channels\n"); + + chan = devm_kcalloc(indio_dev->dev.parent, num_channels, + sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + indio_dev->channels = chan; + indio_dev->num_channels = num_channels; + + ret = regmap_write(st->regmap, AD7768_REG_CH_STANDBY, + GENMASK(st->chip_info->num_channels - 1, 0)); + if (ret < 0) + return ret; + + device_for_each_child_node_scoped(dev, child) { + ret = fwnode_property_read_u32(child, "reg", &channel); + if (ret) + return dev_err_probe(dev, ret, + "Failed to parse reg property of %pfwP\n", child); + + if (channel >= st->chip_info->num_channels) + return dev_err_probe(dev, -EINVAL, + "Invalid channel number %d\n", channel); + + ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY, + BIT(channel), 0); + if (ret < 0) + return ret; + + ret = fwnode_property_read_u32(child, "adi,ch-mode", &ch_mode); + if (ret) + return dev_err_probe(dev, ret, + "Failed to parse property adi,ch-mode %pfwP\n", + child); + + if (ch_mode >= AD7768_NUM_CHANNEL_MODES) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,ch-mode %u for channel %u\n", + ch_mode, channel); + + ret = ad7768_set_channel_mode(indio_dev, channel, ch_mode); + if (ret) + return ret; + + st->active_modes |= BIT(ch_mode); + + if (fwnode_property_read_bool(child, "adi,prebuf-pos-en")) + precharge_cfg[channel].prebufp_en = true; + + if (fwnode_property_read_bool(child, "adi,prebuf-neg-en")) + precharge_cfg[channel].prebufn_en = true; + + if (fwnode_property_read_bool(child, "adi,refbuf-pos-en")) + precharge_cfg[channel].refbufp = true; + + if (fwnode_property_read_bool(child, "adi,refbuf-neg-en")) + precharge_cfg[channel].refbufn = true; + + ad7768_init_chan(&chan[chan_idx], channel); + chan_idx++; + } + + ret = ad7768_configure_precharge_buffers(indio_dev, precharge_cfg); + if (ret < 0) + return ret; + + st->datalines = 1; + ret = device_property_read_u32(&st->spi->dev, "adi,data-lines-number", + &st->datalines); + if (ret) + return dev_err_probe(&st->spi->dev, ret, + "Missing \"adi,data-lines-number\" property\n"); + + ad7768_set_available_sampl_freq(st); + + ret = device_property_match_property_string(&st->spi->dev, + "adi,common-mode-output", + ad7768_vcm_sel_str, + ARRAY_SIZE(ad7768_vcm_sel_str)); + if (ret < 0) + return dev_err_probe(&st->spi->dev, ret, + "Missing or invalid \"adi,common-mode-output\" property\n"); + vcm_sel = ret; + + ret = regmap_update_bits(st->regmap, + AD7768_REG_GENERAL_CONFIG, + AD7768_GEN_CONFIG_VCM_SEL_MSK, + vcm_sel); + if (ret < 0) + return ret; + + vcm_pd = device_property_read_bool(&st->spi->dev, "adi,vcm-power-down"); + + ret = regmap_update_bits(st->regmap, + AD7768_REG_GENERAL_CONFIG, + AD7768_GEN_CONFIG_VCM_PD, + vcm_pd ? AD7768_GEN_CONFIG_VCM_PD : 0); + if (ret < 0) + return ret; + + ret = device_property_match_property_string(&st->spi->dev, + "adi,power-mode", + ad7768_power_mode_str, + ARRAY_SIZE(ad7768_power_mode_str)); + if (ret < 0) { + if (ret != -ENODATA) + return dev_err_probe(&st->spi->dev, ret, + "Invalid \"adi,power-mode\" property\n"); + + pwr_mode = AD7768_LOW_POWER_MODE; + } else { + pwr_mode = ret; + } + + ret = ad7768_set_power_mode(indio_dev, pwr_mode); + if (ret) + return dev_err_probe(dev, ret, "Failed to set power mode\n"); + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ad7768_set_sampling_freq(indio_dev, st->sampling_freq, + indio_dev->channels[i].channel); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to set sampling freq for channel %d\n", + indio_dev->channels[i].channel); + } + + available_datalines = st->chip_info->available_datalines; + len = st->chip_info->num_datalines; + + for (i = 0; i < len; i++) { + if (available_datalines[i] == st->datalines) + return 0; + } + + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid data-lines-number %d for %s\n", + st->datalines, st->chip_info->name); +} + +static int ad7768_reset(struct ad7768_state *st) +{ + struct reset_control *reset_ctrl; + int ret; + + reset_ctrl = devm_reset_control_get_optional_exclusive(&st->spi->dev, NULL); + if (IS_ERR(reset_ctrl)) + return PTR_ERR(reset_ctrl); + + if (reset_ctrl) { + ret = reset_control_assert(reset_ctrl); + if (ret) + return ret; + + 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(2000); + + return 0; +} + +static int ad7768_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + unsigned int spi_readback, rev_id; + struct iio_dev *indio_dev; + struct ad7768_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + 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); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad7768_supply_names), + ad7768_supply_names); + if (ret) + return ret; + + st->mclk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); + + 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 = regmap_attach_dev(dev, st->regmap, st->chip_info->regmap_config); + if (ret) + return ret; + + ret = ad7768_reset(st); + if (ret) + return ret; + + /* Dummy SPI register read to discard the Reset response from the chip */ + 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_warn(dev, "Unexpected revision ID 0x%02x\n", rev_id); + + ret = ad7768_parse_config(indio_dev, dev); + if (ret < 0) + return ret; + + ret = regmap_update_bits(st->regmap, AD7768_REG_INTERFACE_CFG, + AD7768_INTERFACE_CFG_CRC_SELECT_MSK, + AD7768_INTERFACE_CFG_CRC_SELECT); + if (ret < 0) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad7768_info; + + 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); + pm_runtime_set_active(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + indio_dev->setup_ops = &ad7768_buffer_ops; + + ret = ad7768_gpio_adev_init(st); + 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_update_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_SLEEP_MODE_MSK, + AD7768_SLEEP_MODE_MSK); +} + +static int ad7768_runtime_resume(struct device *dev) +{ + struct ad7768_state *st = dev_get_drvdata(dev); + int ret; + + ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE, + AD7768_SLEEP_MODE_MSK, 0); + if (ret) + return ret; + + fsleep(20000); + + 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] 27+ messages in thread
* Re: [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support 2026-07-09 8:50 ` [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support Janani Sunil @ 2026-07-09 9:27 ` sashiko-bot 2026-07-10 2:10 ` Jonathan Cameron 2026-07-10 7:41 ` Uwe Kleine-König 2 siblings, 0 replies; 27+ messages in thread From: sashiko-bot @ 2026-07-09 9:27 UTC (permalink / raw) To: Janani Sunil; +Cc: conor+dt, robh, devicetree Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider: - [High] The AD7768_REG_CH_STANDBY register is programmed with an incorrect contiguous bitmask for the AD7768-4 variant, preventing unused hardware channels from entering standby. - [High] Missing or delayed validation for the `adi,data-lines-number` DT property allows a division by zero kernel panic. - [High] Unvalidated master clock (`mclk`) frequency can result in a division by zero crash. - [High] Missing sign-extension when reading the 24-bit CALIBBIAS (offset) and 8-bit PHASE calibration registers causes data corruption. - [High] The SPI read function reuses the same buffer for concurrent `tx_buf` and `rx_buf` across multiple transfers in the same message, violating the DMA API and the device's SPI protocol. - [High] Missing error rollback in `ad7768_update_scan_mode` leaves the IIO backend hardware in a partially enabled state on failure. - [High] Hardware configuration is modified without claiming direct mode, allowing state corruption if modified while the IIO buffer is active. - [Medium] The device will not automatically enter suspend after probe because the autosuspend timer is never started, resulting in a permanent power leak if unused. - [Medium] Hardware reset sequence asserts and immediately deasserts the reset pin without any delay, potentially violating the minimum reset pulse width. -- --- Patch [4]: [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support --- commit 73c0da3f81ae06a5de50f6a53d4da30b75b35a25 Author: Janani Sunil <janani.sunil@analog.com> iio: adc: Add AD7768 IIO Driver support Add support for AD7768 4/8 channel,simultaneous sampling Sigma-Delta ADC, supporting configurable power modes, decimation filters and data output lines > diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c > new file mode 100644 > index 0000000000000..f76a7081090ec > --- /dev/null > +++ b/drivers/iio/adc/ad7768.c [ ... ] > +static int ad7768_regmap_read(void *context, const void *reg_buf, > + size_t reg_size, void *val_buf, size_t val_size) > +{ [ ... ] > + struct spi_transfer t[] = { > + { > + .tx_buf = &st->d16, > + .len = 2, > + .cs_change = 1, > + }, { > + .tx_buf = &st->d16, > + .rx_buf = &st->d16, > + .len = 2, > + }, > + }; [Severity: High] Does this second transfer violate the DMA API by double-mapping the same virtual pointer (&st->d16) used in the first transfer? Also, wouldn't transmitting st->d16 again during the read phase violate standard SPI protocol by sending the read command on the MOSI line instead of leaving it 0x00? Could .tx_buf be left NULL for the second transfer? [ ... ] > +static int ad7768_read_calib_value(struct ad7768_state *st, > + unsigned int base_reg, int *val) > +{ [ ... ] > + *val = (msb << 16) | (mid << 8) | lsb; > + > + return 0; > +} [Severity: High] Since the CALIBBIAS register stores a 24-bit two's complement offset, does this assignment correctly handle negative offsets? It appears the value is ORed together as an unsigned 24-bit value and assigned without sign extension, which might cause negative offsets to be returned to userspace as large positive integers. Should sign_extend32() be used here? [ ... ] > +static int ad7768_set_clk_divs(struct ad7768_state *st, > + unsigned int freq, unsigned int ch) > +{ [ ... ] > + chan_per_doutx = st->chip_info->num_channels / st->datalines; > + if (!chan_per_doutx) > + return -EINVAL; [ ... ] > + dclk = f_cfg.freq * AD7768_SAMPLE_SIZE * chan_per_doutx; > + if (dclk > mclk) > + return -EINVAL; > + > + /* Set dclk_div to the nearest power of 2 less than the original value */ > + dclk_div = DIV_ROUND_CLOSEST_ULL(mclk, dclk); [Severity: High] Is it possible for the configured mclk to be 0? If the clock rate returns 0, dclk will also become 0. Would this trigger a division by zero kernel panic inside the DIV_ROUND_CLOSEST_ULL() macro? [ ... ] > +static int ad7768_read_raw(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + int *val, int *val2, long info) > +{ [ ... ] > + case IIO_CHAN_INFO_PHASE: [ ... ] > + ret = regmap_read(st->regmap, base_reg, val); > + if (ret) > + return ret; > + > + return IIO_VAL_INT; [Severity: High] Similar to the CALIBBIAS register, the PHASE register stores an 8-bit two's complement value. Since regmap_read() zero-extends the 8-bit read, will this corrupt negative phase values? [ ... ] > +static int ad7768_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, int val2, long info) > +{ [ ... ] > + switch (info) { > + case IIO_CHAN_INFO_SAMP_FREQ: > + return ad7768_set_sampling_freq(indio_dev, val, chan->channel); [Severity: High] Is it safe to update the sampling frequency without claiming direct mode? Since ad7768_set_sampling_freq() invokes ad7768_sync() to reset the ADC's digital filters and SPI synchronization, could userspace writes to sysfs attributes corrupt or desynchronize ongoing buffer captures? Should iio_device_claim_direct_mode() be used here? [ ... ] > +static int ad7768_update_scan_mode(struct iio_dev *indio_dev, > + const unsigned long *scan_mask) > +{ [ ... ] > + for (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; > +} [Severity: High] If an error occurs midway through this loop, returning ret immediately leaves previously enabled backend channels active. Does this leak backend resources and leave the hardware streaming indefinitely, potentially breaking future captures? Would it be better to rollback the enabled channels on failure? [ ... ] > +static int ad7768_parse_config(struct iio_dev *indio_dev, > + struct device *dev) > +{ [ ... ] > + ret = regmap_write(st->regmap, AD7768_REG_CH_STANDBY, > + GENMASK(st->chip_info->num_channels - 1, 0)); > + if (ret < 0) > + return ret; > + > + device_for_each_child_node_scoped(dev, child) { [ ... ] > + ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY, > + BIT(channel), 0); [Severity: High] For the AD7768-4 variant, which uses physical channels 0, 1, 4, and 5, will this contiguous mask GENMASK(3, 0) correctly power down the unused channels? It seems this would write 1s to bits 0-3 and 0s (active) to bits 4 and 5, potentially leaving the upper hardware channels permanently active. Additionally, will BIT(channel) correctly clear the bits for the upper hardware channels? If channel is 2 or 3 (representing the 3rd and 4th channels), this would attempt to wake up non-existent physical channels while leaving bits 4 and 5 active. [ ... ] > + st->datalines = 1; > + ret = device_property_read_u32(&st->spi->dev, "adi,data-lines-number", > + &st->datalines); > + if (ret) > + return dev_err_probe(&st->spi->dev, ret, > + "Missing \"adi,data-lines-number\" property\n"); > + > + ad7768_set_available_sampl_freq(st); [ ... ] > + for (i = 0; i < indio_dev->num_channels; i++) { > + ret = ad7768_set_sampling_freq(indio_dev, st->sampling_freq, > + indio_dev->channels[i].channel); [Severity: High] Could st->datalines be 0 if the user provides adi,data-lines-number = <0> in the device tree? Because validation of this property happens at the end of the function, calling ad7768_set_sampling_freq() here delegates to ad7768_set_clk_divs(), which performs st->chip_info->num_channels / st->datalines. Would this trigger a division by zero kernel panic? [ ... ] > +static int ad7768_set_filter_mode(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + unsigned int mode) > +{ [ ... ] > + ret = regmap_update_bits(st->regmap, > + AD7768_REG_CH_MODE(st->chn_mode[chan->address]), > + AD7768_CH_MODE_FILTER_TYPE_MSK, > + AD7768_CH_MODE_FILTER_TYPE_MODE(mode)); > + if (ret < 0) > + return ret; > + > + return ad7768_sync(st); > +} [Severity: High] Similar to ad7768_write_raw(), is it safe to update the filter mode and sync the hardware without claiming direct mode using iio_device_claim_direct_mode()? [ ... ] > +static int ad7768_reset(struct ad7768_state *st) > +{ [ ... ] > + if (reset_ctrl) { > + ret = reset_control_assert(reset_ctrl); > + if (ret) > + return ret; > + > + ret = reset_control_deassert(reset_ctrl); [Severity: Medium] Is a delay required between asserting and deasserting the reset line? Executing reset_control_deassert() immediately after reset_control_assert() might result in an extremely brief reset pulse. Could this fail to reset the hardware properly on fast platforms? [ ... ] > +static int ad7768_probe(struct spi_device *spi) > +{ [ ... ] > + pm_runtime_set_autosuspend_delay(dev, 2000); > + pm_runtime_use_autosuspend(dev); > + pm_runtime_set_active(dev); > + > + ret = devm_pm_runtime_enable(dev); [Severity: Medium] Does this properly start the autosuspend timer after probe? Because the usage counter is 0 and no idle check is manually queued, the core never starts the autosuspend timer, leaving the ADC fully powered on indefinitely until userspace triggers a pm_runtime_get()/put() cycle. Should pm_runtime_mark_last_busy() and pm_runtime_put_autosuspend() be called before exiting probe? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com?part=4 ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support 2026-07-09 8:50 ` [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support Janani Sunil 2026-07-09 9:27 ` sashiko-bot @ 2026-07-10 2:10 ` Jonathan Cameron 2026-07-10 7:41 ` Uwe Kleine-König 2 siblings, 0 replies; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 2:10 UTC (permalink / raw) To: Janani Sunil Cc: Nuno Sá, Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On Thu, 9 Jul 2026 10:50:15 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > Add support for AD7768 4/8 channel,simultaneous sampling Sigma-Delta > ADC, supporting configurable power modes, decimation filters and data > output lines > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> Hi Janani. Various things inline. > --- > MAINTAINERS | 1 + > drivers/iio/adc/Kconfig | 16 + > drivers/iio/adc/Makefile | 1 + > drivers/iio/adc/ad7768.c | 1291 ++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 1309 insertions(+) > diff --git a/drivers/iio/adc/ad7768.c b/drivers/iio/adc/ad7768.c > new file mode 100644 > index 000000000000..f76a7081090e > --- /dev/null > +++ b/drivers/iio/adc/ad7768.c > @@ -0,0 +1,1291 @@ ... > + > +#define AD7768_AUX_DEV_GPIO_NAME "gpio" Can this just go inline? > +#define AD7768_REG_GPIO_CONTROL 0x0E > +/* AD7768_REG_POWER_MODE */ > +#define AD7768_SLEEP_MODE_MSK BIT(7) > +#define AD7768_POWER_MODE_POWER_MODE_MSK GENMASK(5, 4) > +#define AD7768_POWER_MODE_POWER_MODE(x) (((x) & 0x3) << 4) > +#define AD7768_POWER_MODE_GET_POWER_MODE(x) (((x) >> 4) & 0x3) > +#define AD7768_POWER_MODE_MCLK_DIV_MSK GENMASK(1, 0) > +#define AD7768_POWER_MODE_MCLK_DIV_MODE(x) (((x) & 0x3) << 0) FIELD_GET() inline. This define doesn't give us anything wrt to readability over that. > + > +/* AD7768_REG_DATA_CONTROL */ > +#define AD7768_DATA_CONTROL_SPI_RESET_MSK GENMASK(1, 0) > +#define AD7768_DATA_CONTROL_SPI_RESET_1 0x03 > +#define AD7768_DATA_CONTROL_SPI_RESET_2 0x02 > +#define AD7768_DATA_CONTROL_SPI_SYNC_MSK BIT(7) > +#define AD7768_DATA_CONTROL_SPI_SYNC BIT(7) No need for define, use FIELD_PREP(AD7768_DATA_CONTROL_SPI_SYNC_MASK, 1) inline > +#define AD7768_DATA_CONTROL_SPI_SYNC_CLEAR 0 Not obvious why a define is needed for this. > + > +/* AD7768_REG_INTERFACE_CFG */ > +#define AD7768_INTERFACE_CFG_DCLK_DIV_MSK GENMASK(1, 0) > +#define AD7768_INTERFACE_CFG_DCLK_DIV_MODE(x) (4 - ffs(x)) > +#define AD7768_MAX_DCLK_DIV 8 > + > +#define AD7768_INTERFACE_CFG_CRC_SELECT_MSK GENMASK(3, 2) > +/* only 4 samples CRC calculation support exists */ Exists where? In the backend, or if the hardware. Good to be more specific. > +#define AD7768_INTERFACE_CFG_CRC_SELECT FIELD_PREP(GENMASK(3, 2), 0x01) > +/* AD7768_REG_PRECHARGE_BUF1 and 2*/ > +#define AD7768_PRECHARGE_BUF1_MSK(val) ((val) & GENMASK(7, 0)) > +#define AD7768_PRECHARGE_BUF2_MSK(val) (((val) & GENMASK(15, 8)) >> 8) > +#define AD7768_4_PRECHARGE_BUF1_MSK(val) ((val) & GENMASK(3, 0)) > +#define AD7768_4_PRECHARGE_BUF2_MSK(val) (((val) & GENMASK(7, 4)) >> 4) > +#define AD7768_CALIB_REG_MSB_MASK(val) (((val) & 0xFF0000) >> 16) That seems unlikely to be the mask. It's the value. So just define the mask and let FIELD_GET() deal with the shift. > +#define AD7768_CALIB_REG_MID_MASK(val) (((val) & 0x00FF00) >> 8) > +#define AD7768_CALIB_REG_LSB_MASK(val) ((val) & 0x0000FF) > +#define AD7768_REV_ID_VAL 0x06 ... > + > +static const unsigned int ad7768_4_available_datalines[] = { > + 1, 4 > +}; > + > +static const char * const ad7768_supply_names[] = { > + "avdd", "avss", "dvdd", "iovdd", "vref" > +}; Add trailing commas to these. They aren't terminated in any way so in theory at least we might get more elements on the next line. > + > +static int ad7768_regmap_read(void *context, const void *reg_buf, > + size_t reg_size, void *val_buf, size_t val_size) > +{ > + struct spi_device *spi = context; > + struct ad7768_state *st = spi_get_drvdata(spi); > + unsigned int reg; > + int ret; > + struct spi_transfer t[] = { > + { > + .tx_buf = &st->d16, > + .len = 2, > + .cs_change = 1, > + }, { > + .tx_buf = &st->d16, Sashiko moaned about this. Add a comment on why tx_buf is set to the same thing. > + .rx_buf = &st->d16, > + .len = 2, > + }, > + }; > + > + reg = *(const u8 *)reg_buf; > + > + st->d16 = cpu_to_be16(AD7768_SPI_READ_CMD | > + FIELD_PREP(AD7768_SPI_REG_MASK, reg)); > + > + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); > + if (ret < 0) > + return ret; > + > + *(u8 *)val_buf = FIELD_GET(AD7768_SPI_DATA_MASK, be16_to_cpu(st->d16)); I'd prefer seeing a local pointer variable for the correct size u8 *data_val = val_buf; ... > + > + return ret; > +} > + > +static int ad7768_read_calib_value(struct ad7768_state *st, > + unsigned int base_reg, int *val) > +{ > + unsigned int msb, mid, lsb; > + int ret; > + > + guard(mutex)(&st->lock); > + > + ret = regmap_read(st->regmap, base_reg + 0, &msb); > + if (ret) > + return ret; > + > + ret = regmap_read(st->regmap, base_reg + 1, &mid); > + if (ret) > + return ret; > + > + ret = regmap_read(st->regmap, base_reg + 2, &lsb); > + if (ret) > + return ret; > + > + *val = (msb << 16) | (mid << 8) | lsb; No bulk read? > + > + return 0; > +} > +static unsigned int ad7768_get_max_mode_freq(const struct ad7768_state *st, > + unsigned int mode, > + unsigned int mode_freq) > +{ > + unsigned int max_freq = 0; > + unsigned int i, freq; > + > + for (i = 0; i < AD7768_NUM_CHANNEL_MODES; i++) { > + if (!(st->active_modes & BIT(i))) > + continue; > + > + freq = i == mode ? mode_freq : st->mode_freq[i]; > + if (freq > max_freq) > + max_freq = freq; max_freq = max(max_freq, freq); > + } > + > + return max_freq; > +} > + > +static int ad7768_set_sampling_freq(struct iio_dev *indio_dev, > + unsigned int freq, unsigned int ch) > +{ > + struct ad7768_state *st = iio_priv(indio_dev); > + unsigned int mode = st->chn_mode[ch]; > + int ret = 0; Always set before use so don't init. > + unsigned int max_freq; > + > + if (!freq) > + return -EINVAL; > + > + guard(mutex)(&st->lock); > + > + max_freq = ad7768_get_max_mode_freq(st, mode, freq); > + > + ret = ad7768_set_clk_divs(st, max_freq, ch); > + if (ret < 0) > + return ret; > + > + ret = ad7768_set_channel_decimation(st, freq, ch); > + if (ret < 0) > + return ret; > + > + ret = ad7768_sync(st); > + if (ret) > + return ret; > + > + st->mode_freq[mode] = freq; blank line before simple returns like this one. > + return 0; > +} > + > +static int ad7768_read_raw(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + int *val, int *val2, long info) > +{ ... > + case IIO_CHAN_INFO_PHASE: > + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) > + base_reg = AD7768_REG_PHASE(chan->address); > + else > + base_reg = AD7768_4_REG_PHASE(chan->address); > + > + ret = regmap_read(st->regmap, base_reg, val); As below. Check units. > + if (ret) > + return ret; > + > + return IIO_VAL_INT; > + 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; > + int ret; > + > + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, pm); > + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); > + if (ret) > + return ret; > + > + switch (info) { > + case IIO_CHAN_INFO_SAMP_FREQ: > + return ad7768_set_sampling_freq(indio_dev, val, chan->channel); > + > + case IIO_CHAN_INFO_CALIBBIAS: > + return ad7768_write_calib_value(st, > + ad7768_get_calib_reg_base(st, chan, false), val); Probably use a local variable for the reg base as you do for phase so you can align easily after the ( > + > + case IIO_CHAN_INFO_CALIBSCALE: > + return ad7768_write_calib_value(st, > + ad7768_get_calib_reg_base(st, chan, true), val); > + > + case IIO_CHAN_INFO_PHASE: > + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) > + base_reg = AD7768_REG_PHASE(chan->address); > + else > + base_reg = AD7768_4_REG_PHASE(chan->address); > + > + return regmap_write(st->regmap, base_reg, val); Is val in the units the ABI expects? Seems unlikely given that's radians. > + > + default: > + return -EINVAL; > + } > +} > + > +static int ad7768_buffer_postdisable(struct iio_dev *indio_dev) > +{ > + struct ad7768_state *st = iio_priv(indio_dev); > + > + pm_runtime_mark_last_busy(&st->spi->dev); Look up what the next call does. > + pm_runtime_put_autosuspend(&st->spi->dev); > + return 0; > +} > + > +static void ad7768_set_available_sampl_freq(struct ad7768_state *st) > +{ > + unsigned int mode; > + unsigned int dec; > + unsigned int mclk = clk_get_rate(st->mclk); > + struct ad7768_avail_freq *avail_freq; > + > + for (mode = 0; mode < AD7768_NUM_POWER_MODES; mode++) { for (unsigned int mode = 0; > + avail_freq = &st->avail_freq[mode]; > + for (dec = ARRAY_SIZE(ad7768_dec_rate); dec > 0; dec--) { for (unsigned int dec = ... > + struct ad7768_freq_config freq_cfg; > + > + freq_cfg.dec_rate = dec - 1; > + freq_cfg.freq = mclk / (ad7768_dec_rate[dec - 1] * > + ad7768_mclk_div[mode]); > + avail_freq->freq_cfg[avail_freq->n_freqs++] = freq_cfg; > + } > + } > + > + /* The max frequency is not supported in one data line configuration */ > + if (st->datalines == 1) > + st->avail_freq[AD7768_FAST_MODE].n_freqs--; > +} > + > +static int ad7768_gpio_adev_init(struct ad7768_state *st) > +{ > + struct device *dev = &st->spi->dev; > + struct auxiliary_device *adev; > + int id; > + > + if (!device_property_read_bool(dev, "gpio-controller")) > + return 0; > + > + id = (st->spi->controller->bus_num << 8) | spi_get_chipselect(st->spi, 0); Why that particular ID? Add some comment. > + adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, > + AD7768_AUX_DEV_GPIO_NAME, > + (void *)st->chip_info->name, id); Why is the cast to void * needed? Suggests perhaps the type is wrong for name. > + 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) > +{ > + struct ad7768_state *st = iio_priv(indio_dev); > + int ret; > + u8 ch; > + u8 prebuf1_val, prebuf2_val; Prefer reverse xmas tree where the ordering isn't really enforced by anything else. > + u16 prebuf_mask = 0; > + u8 refbufp_val = 0; > + u8 refbufn_val = 0; > + > + if (st->chip_info->num_channels == AD7768_MAX_CHANNEL) { This match is odd. Encode what you need in chip_info, don't use the number of channels as a place holder for which chip it is. Maybe via a callback, or maybe via data. I haven't messed around with it to see how bad the data version is (masks etc) > + for (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 |= BIT(channel); > + > + if (precharge_cfg[channel].refbufn) > + refbufn_val |= BIT(channel); > + } > + > + prebuf1_val = AD7768_PRECHARGE_BUF1_MSK(prebuf_mask); > + prebuf2_val = AD7768_PRECHARGE_BUF2_MSK(prebuf_mask); > + } else { > + for (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_4_get_refbuf(channel); > + > + if (precharge_cfg[channel].refbufn) > + refbufn_val |= ad7768_4_get_refbuf(channel); > + } > + > + prebuf1_val = AD7768_4_PRECHARGE_BUF1_MSK(prebuf_mask); > + prebuf2_val = AD7768_4_PRECHARGE_BUF2_MSK(prebuf_mask); > + } > + > + ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF1, prebuf1_val); Mentioned below, but if (ret) is more appropriate for regmap calls because... > + if (ret < 0) > + return ret; > + > + ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF2, prebuf2_val); > + if (ret < 0) > + return ret; > + > + ret = regmap_write(st->regmap, AD7768_REG_REFP_BUF, refbufp_val); > + if (ret < 0) > + return ret; > + If ret did go positive ever the following would treat it differently to every other call. > + return regmap_write(st->regmap, AD7768_REG_REFN_BUF, refbufn_val); > +} > + > +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), > + { }, No trailing comma on a terminating entry like that one. > +}; > + > +static const struct iio_chan_spec ad7768_channel_template = { > + .type = IIO_VOLTAGE, > + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS) | > + BIT(IIO_CHAN_INFO_CALIBSCALE) | > + BIT(IIO_CHAN_INFO_PHASE) | > + BIT(IIO_CHAN_INFO_SAMP_FREQ), > + .indexed = 1, > + .scan_type = { > + .sign = 's', > + .realbits = 24, > + .storagebits = 32, > + }, > + .ext_info = ad7768_ext_info, > +}; > + > +static void ad7768_init_chan(struct iio_chan_spec *chan, unsigned int channel) > +{ > + *chan = ad7768_channel_template; I'd replace where this is called with chan[chan_idx] = (struct iio_chan_spec) { .type = IIO_VOLTAGE, etc. Generally ends up easier to read than a template and modify. > + chan->address = channel; > + chan->channel = channel; > + chan->scan_index = channel; > +} > + > +static int ad7768_parse_config(struct iio_dev *indio_dev, > + struct device *dev) > +{ > + struct ad7768_state *st = iio_priv(indio_dev); > + const unsigned int *available_datalines; > + struct ad7768_precharge_config precharge_cfg[AD7768_MAX_CHANNEL] = {}; Trivial but { }; would be my preference for style of that. > + struct iio_chan_spec *chan; > + unsigned int num_channels; > + unsigned int channel; > + unsigned int i, len, vcm_sel, vcm_pd, ch_mode, pwr_mode; What you combine and what you don't combine here seems a bit random. Maybe take another look and see if there is a more logical combination. (or maybe this makes sense after a later patch, in which case ignore this comment). > + int chan_idx = 0; > + int ret; > + > + num_channels = device_get_child_node_count(dev); > + Given following is a check on the value returned, no blank line here would keep that association more obvious. > + if (!num_channels || num_channels > st->chip_info->num_channels) > + return dev_err_probe(dev, -EINVAL, "Invalid number of channels\n"); > + > + chan = devm_kcalloc(indio_dev->dev.parent, num_channels, > + sizeof(*chan), GFP_KERNEL); > + if (!chan) > + return -ENOMEM; > + > + indio_dev->channels = chan; > + indio_dev->num_channels = num_channels; > + > + ret = regmap_write(st->regmap, AD7768_REG_CH_STANDBY, > + GENMASK(st->chip_info->num_channels - 1, 0)); > + if (ret < 0) > + return ret; regmap is always 0 or negative, so these can be if (ret) > + > + device_for_each_child_node_scoped(dev, child) { > + ret = fwnode_property_read_u32(child, "reg", &channel); > + if (ret) > + return dev_err_probe(dev, ret, > + "Failed to parse reg property of %pfwP\n", child); > + > + if (channel >= st->chip_info->num_channels) > + return dev_err_probe(dev, -EINVAL, > + "Invalid channel number %d\n", channel); maybe put "from firmware" in that error message. > + > + ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY, > + BIT(channel), 0); > + if (ret < 0) > + return ret; > + > + ret = fwnode_property_read_u32(child, "adi,ch-mode", &ch_mode); David covered this when reviewing the binding. There is quite a bit of precedence of smaller sets of config registers than channels. Usually we just make it a userspace thing and fail if too many configs are requested. > + if (ret) > + return dev_err_probe(dev, ret, > + "Failed to parse property adi,ch-mode %pfwP\n", > + child); > + > + ret = regmap_update_bits(st->regmap, > + AD7768_REG_GENERAL_CONFIG, > + AD7768_GEN_CONFIG_VCM_PD, > + vcm_pd ? AD7768_GEN_CONFIG_VCM_PD : 0); regmap_assign_bits() > + if (ret < 0) > + return ret; > + > + ret = device_property_match_property_string(&st->spi->dev, > + "adi,power-mode", > + ad7768_power_mode_str, > + ARRAY_SIZE(ad7768_power_mode_str)); > + if (ret < 0) { > + if (ret != -ENODATA) Check for device property existing before reading it. Avoids need for caring about the specific return value. if (device_property_present()) { ret = device_property_match... ... } else { pwr_mode = AD7768_LOW_POWER_MODE; } > + return dev_err_probe(&st->spi->dev, ret, > + "Invalid \"adi,power-mode\" property\n"); > + > + pwr_mode = AD7768_LOW_POWER_MODE; > + } else { > + pwr_mode = ret; > + } > + > + ret = ad7768_set_power_mode(indio_dev, pwr_mode); > + if (ret) > + return dev_err_probe(dev, ret, "Failed to set power mode\n"); > + > + for (i = 0; i < indio_dev->num_channels; i++) { > + ret = ad7768_set_sampling_freq(indio_dev, st->sampling_freq, > + indio_dev->channels[i].channel); > + if (ret < 0) > + return dev_err_probe(dev, ret, > + "Failed to set sampling freq for channel %d\n", > + indio_dev->channels[i].channel); > + } > + > + available_datalines = st->chip_info->available_datalines; > + len = st->chip_info->num_datalines; > + > + for (i = 0; i < len; i++) { > + if (available_datalines[i] == st->datalines) > + return 0; I'd be tempted to not do something so specific to it being the last bit of the function and keep the error case as clearly out of line. if (...) break; } if (i == len) return dev_err_probe(); return 0; > + } > + > + return dev_err_probe(&st->spi->dev, -EINVAL, use dev > + "Invalid data-lines-number %d for %s\n", > + st->datalines, st->chip_info->name); > +} > + > +static int ad7768_reset(struct ad7768_state *st) > +{ > + struct reset_control *reset_ctrl; > + int ret; > + > + reset_ctrl = devm_reset_control_get_optional_exclusive(&st->spi->dev, NULL); > + if (IS_ERR(reset_ctrl)) > + return PTR_ERR(reset_ctrl); > + > + if (reset_ctrl) { > + ret = reset_control_assert(reset_ctrl); > + if (ret) > + return ret; Probably do need a delay of some type here. See if you can find anything in the data sheet on what it should be (sashiko) > + > + 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(2000); > + > + return 0; > +} > + > +static int ad7768_probe(struct spi_device *spi) > +{ ... > + ret = devm_iio_backend_enable(dev, st->back); > + if (ret) > + return ret; > + > + pm_runtime_set_autosuspend_delay(dev, 2000); > + pm_runtime_use_autosuspend(dev); > + pm_runtime_set_active(dev); > + > + ret = devm_pm_runtime_enable(dev); I'm fairly sure the sashiko comment here is incorrect but do check it. > + if (ret) > + return ret; > + > + indio_dev->setup_ops = &ad7768_buffer_ops; > + > + ret = ad7768_gpio_adev_init(st); > + 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_update_bits(st->regmap, AD7768_REG_POWER_MODE, > + AD7768_SLEEP_MODE_MSK, > + AD7768_SLEEP_MODE_MSK); regmap_set_bits() > +} > + > +static int ad7768_runtime_resume(struct device *dev) > +{ > + struct ad7768_state *st = dev_get_drvdata(dev); > + int ret; > + > + ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE, > + AD7768_SLEEP_MODE_MSK, 0); regmap_clear_bits() > + if (ret) > + return ret; ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support 2026-07-09 8:50 ` [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support Janani Sunil 2026-07-09 9:27 ` sashiko-bot 2026-07-10 2:10 ` Jonathan Cameron @ 2026-07-10 7:41 ` Uwe Kleine-König 2 siblings, 0 replies; 27+ messages in thread From: Uwe Kleine-König @ 2026-07-10 7:41 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, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev [-- Attachment #1: Type: text/plain, Size: 232 bytes --] On Thu, Jul 09, 2026 at 10:50:15AM +0200, Janani Sunil wrote: > +#include <linux/mod_devicetable.h> Please don't add new users for this header file. Only use those <linux/device-id/*.h> that you actually need (if any). Thanks Uwe [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver 2026-07-09 8:50 [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil ` (3 preceding siblings ...) 2026-07-09 8:50 ` [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support Janani Sunil @ 2026-07-09 8:50 ` Janani Sunil 2026-07-09 9:38 ` sashiko-bot ` (3 more replies) 2026-07-09 8:50 ` [PATCH 6/6] Documentation: iio: Add AD7768 Documentation Janani Sunil 5 siblings, 4 replies; 27+ messages in thread From: Janani Sunil @ 2026-07-09 8:50 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 Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev, Janani Sunil The AD7768/AD7768-4 ADC exposes 5 general-purpose I/O pins that can be independently configured as inputs or outputs. Add an auxiliary bus driver to expose these pins as a GPIO chip, registered by the parent IIO driver. The driver uses the parent's regmap for register access and delegates runtime power management to the parent device. Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 10 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-ad7768.c | 208 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index dc94e7803a7c..3de7ebcc4ee7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1639,6 +1639,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/gpio/gpio-ad7768.c F: drivers/iio/adc/ad7768.c ANALOG DEVICES INC AD7780 DRIVER diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 28cf6d2e83c2..12ebbdb16982 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1796,6 +1796,16 @@ endmenu menu "Auxiliary Bus GPIO drivers" depends on AUXILIARY_BUS +config GPIO_AD7768 + tristate "Analog Devices AD7768 GPIO support" + depends on AD7768 && GPIOLIB + help + Say yes here to expose the AD7768 utility pins as GPIOs when the + device tree node is marked as a GPIO controller. + + To compile this driver as a module, choose M here: the module will be + called gpio-ad7768. + config GPIO_LTC4283 tristate "Analog Devices LTC4283 GPIO support" depends on SENSORS_LTC4283 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 4d0e900402fc..d2921963efc7 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o +obj-$(CONFIG_GPIO_AD7768) += gpio-ad7768.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o diff --git a/drivers/gpio/gpio-ad7768.c b/drivers/gpio/gpio-ad7768.c new file mode 100644 index 000000000000..c2f01b1abd7c --- /dev/null +++ b/drivers/gpio/gpio-ad7768.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7768 GPIO auxiliary driver + * + * Copyright 2026 Analog Devices Inc. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#define AD7768_REG_GPIO_CONTROL 0x0E +#define AD7768_REG_GPIO_WRITE 0x0F +#define AD7768_REG_GPIO_READ 0x10 + +#define AD7768_GPIO_UGPIO_ENABLE BIT(7) +#define AD7768_GPIO_INPUT 0x00 +#define AD7768_GPIO_OUTPUT(x) BIT(x) + +#define AD7768_NUM_GPIOS 5 + +struct ad7768_gpio_state { + struct device *parent; + struct regmap *regmap; + struct mutex lock; /* protects regmap accesses */ + struct gpio_chip gc; +}; + +static int ad7768_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct ad7768_gpio_state *st = gpiochip_get_data(chip); + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); + int ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + + if (ret) + return ret; + + return regmap_update_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + BIT(offset), AD7768_GPIO_INPUT); +} + +static int ad7768_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct ad7768_gpio_state *st = gpiochip_get_data(chip); + unsigned int val; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val); + if (ret) + return ret; + + return !!(val & BIT(offset)) ? GPIO_LINE_DIRECTION_OUT : + GPIO_LINE_DIRECTION_IN; +} + +static int ad7768_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct ad7768_gpio_state *st = gpiochip_get_data(chip); + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + ret = regmap_update_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + BIT(offset), AD7768_GPIO_OUTPUT(offset)); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, AD7768_REG_GPIO_WRITE, + BIT(offset), value << offset); +} + +static int ad7768_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ad7768_gpio_state *st = gpiochip_get_data(chip); + unsigned int val; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val); + if (ret) + return ret; + + if (val & BIT(offset)) + ret = regmap_read(st->regmap, AD7768_REG_GPIO_WRITE, &val); + else + ret = regmap_read(st->regmap, AD7768_REG_GPIO_READ, &val); + if (ret) + return ret; + + return !!(val & BIT(offset)); +} + +static int ad7768_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct ad7768_gpio_state *st = gpiochip_get_data(chip); + unsigned int val; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + ret = regmap_read(st->regmap, AD7768_REG_GPIO_CONTROL, &val); + if (ret) + return ret; + + if (!(val & BIT(offset))) + return 0; + + return regmap_update_bits(st->regmap, AD7768_REG_GPIO_WRITE, + BIT(offset), value << offset); +} + +static int ad7768_gpio_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + const char *label = dev_get_platdata(dev); + struct ad7768_gpio_state *st; + struct gpio_chip *gc; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->parent = dev->parent; + st->regmap = dev_get_regmap(dev->parent, NULL); + + ret = devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(st->parent); + if (ret < 0) + return ret; + + ret = regmap_update_bits(st->regmap, AD7768_REG_GPIO_CONTROL, + AD7768_GPIO_UGPIO_ENABLE, + AD7768_GPIO_UGPIO_ENABLE); + + pm_runtime_mark_last_busy(st->parent); + pm_runtime_put_autosuspend(st->parent); + + if (ret < 0) + return ret; + + gc = &st->gc; + gc->label = label; + gc->base = -1; + gc->ngpio = AD7768_NUM_GPIOS; + gc->parent = dev; + gc->owner = THIS_MODULE; + gc->can_sleep = true; + gc->get_direction = ad7768_gpio_get_direction; + gc->direction_input = ad7768_gpio_direction_input; + gc->direction_output = ad7768_gpio_direction_output; + gc->get = ad7768_gpio_get; + gc->set = ad7768_gpio_set; + + return devm_gpiochip_add_data(dev, &st->gc, st); +} + +static const struct auxiliary_device_id ad7768_gpio_ids[] = { + { .name = "ad7768.gpio" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, ad7768_gpio_ids); + +static struct auxiliary_driver ad7768_gpio_driver = { + .probe = ad7768_gpio_probe, + .id_table = ad7768_gpio_ids, +}; +module_auxiliary_driver(ad7768_gpio_driver); + +MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7768 GPIO auxiliary driver"); +MODULE_LICENSE("GPL"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver 2026-07-09 8:50 ` [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil @ 2026-07-09 9:38 ` sashiko-bot 2026-07-09 11:05 ` Andy Shevchenko ` (2 subsequent siblings) 3 siblings, 0 replies; 27+ messages in thread From: sashiko-bot @ 2026-07-09 9:38 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] Configuring GPIO direction to output before setting the intended output value causes a transient hardware glitch. -- --- Patch [5]: [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver --- commit 0fd38c3af385be1ae0dc21601c951d0e8f32ade5 Author: Janani Sunil <janani.sunil@analog.com> gpio: ad7768: Add AD7768 GPIO auxiliary driver This commit adds an auxiliary bus driver to expose 5 general-purpose I/O pins on the AD7768/AD7768-4 ADC as a GPIO controller. The driver handles pin direction and value configuration by sharing a regmap with the parent IIO device. > diff --git a/drivers/gpio/gpio-ad7768.c b/drivers/gpio/gpio-ad7768.c > new file mode 100644 > index 0000000000000..c2f01b1abd7c9 > --- /dev/null > +++ b/drivers/gpio/gpio-ad7768.c [ ... ] > +static int ad7768_gpio_direction_output(struct gpio_chip *chip, > + unsigned int offset, int value) > +{ > + struct ad7768_gpio_state *st = gpiochip_get_data(chip); > + int ret; > + > + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); > + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); > + if (ret) > + return ret; > + > + guard(mutex)(&st->lock); > + > + ret = regmap_update_bits(st->regmap, AD7768_REG_GPIO_CONTROL, > + BIT(offset), AD7768_GPIO_OUTPUT(offset)); > + if (ret) > + return ret; > + > + return regmap_update_bits(st->regmap, AD7768_REG_GPIO_WRITE, > + BIT(offset), value << offset); [Severity: Medium] Will this sequence cause a transient hardware glitch on the GPIO pin? By configuring the pin direction to output in AD7768_REG_GPIO_CONTROL before setting the intended output value in AD7768_REG_GPIO_WRITE, the hardware could briefly drive the line with whatever stale value is currently held in the write register. Would it be safer to write the intended value to AD7768_REG_GPIO_WRITE first, and only then switch the pin direction to output? > +} -- Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com?part=5 ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver 2026-07-09 8:50 ` [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil 2026-07-09 9:38 ` sashiko-bot @ 2026-07-09 11:05 ` Andy Shevchenko 2026-07-10 2:14 ` Jonathan Cameron 2026-07-10 20:06 ` Linus Walleij 3 siblings, 0 replies; 27+ messages in thread From: Andy Shevchenko @ 2026-07-09 11:05 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, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On Thu, Jul 09, 2026 at 10:50:16AM +0200, Janani Sunil wrote: > The AD7768/AD7768-4 ADC exposes 5 general-purpose I/O pins that can be > independently configured as inputs or outputs. Add an auxiliary bus driver > to expose these pins as a GPIO chip, registered by the parent IIO driver. > > The driver uses the parent's regmap for register access and delegates > runtime power management to the parent device. ... > +config GPIO_AD7768 > + tristate "Analog Devices AD7768 GPIO support" > + depends on AD7768 && GPIOLIB Make depend on GPIOLIB on a separate line (it helps a lot when grepping for the users of GPIOLIB). > + help > + Say yes here to expose the AD7768 utility pins as GPIOs when the > + device tree node is marked as a GPIO controller. > + > + To compile this driver as a module, choose M here: the module will be > + called gpio-ad7768. > config GPIO_LTC4283 Shouldn't Kconfig entry be aligned by order with Makefile ordering? ... > +#include <linux/auxiliary_bus.h> + bits.h > +#include <linux/cleanup.h> > +#include <linux/device.h> > +#include <linux/err.h> > +#include <linux/gpio/driver.h> > +#include <linux/module.h> > +#include <linux/mutex.h> > +#include <linux/pm_runtime.h> > +#include <linux/regmap.h> Please, IWYU! I believe AD knows this very well and again same mistake from AD! ... > +struct ad7768_gpio_state { > + struct device *parent; > + struct regmap *regmap; As far as I can see these two are dups. One may be derived from the other. Try both and check (with probably bloat-o-meter) which one is better. > + struct mutex lock; /* protects regmap accesses */ This is not fully correct comment. This protects GPIO IO which may require more than one regmap call in a row. > + struct gpio_chip gc; > +}; ... > +static int ad7768_gpio_direction_input(struct gpio_chip *chip, > + unsigned int offset) > +{ > + struct ad7768_gpio_state *st = gpiochip_get_data(chip); > + > + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); > + int ret = PM_RUNTIME_ACQUIRE_ERR(&pm); No, declare it usual way. > + And drop this blank line. > + if (ret) > + return ret; > + > + return regmap_update_bits(st->regmap, AD7768_REG_GPIO_CONTROL, > + BIT(offset), AD7768_GPIO_INPUT); > +} ... So, I briefly looked at the implementation and I don't understand why gpio-regmap can't be used. Do you need PM runtime there? It can be done for all (if absent). ... > +static int ad7768_gpio_probe(struct auxiliary_device *adev, > + const struct auxiliary_device_id *id) > +{ > + struct device *dev = &adev->dev; > + const char *label = dev_get_platdata(dev); > + struct ad7768_gpio_state *st; > + struct gpio_chip *gc; > + int ret; > + > + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); > + if (!st) > + return -ENOMEM; > + > + st->parent = dev->parent; > + st->regmap = dev_get_regmap(dev->parent, NULL); > + > + ret = devm_mutex_init(dev, &st->lock); > + if (ret) > + return ret; > + > + ret = pm_runtime_resume_and_get(st->parent); > + if (ret < 0) > + return ret; > + > + ret = regmap_update_bits(st->regmap, AD7768_REG_GPIO_CONTROL, > + AD7768_GPIO_UGPIO_ENABLE, > + AD7768_GPIO_UGPIO_ENABLE); > + > + pm_runtime_mark_last_busy(st->parent); For a few releases this is a dup call. > + pm_runtime_put_autosuspend(st->parent); > + > + if (ret < 0) > + return ret; > + > + gc = &st->gc; > + gc->label = label; > + gc->base = -1; > + gc->ngpio = AD7768_NUM_GPIOS; > + gc->parent = dev; > + gc->owner = THIS_MODULE; > + gc->can_sleep = true; > + gc->get_direction = ad7768_gpio_get_direction; > + gc->direction_input = ad7768_gpio_direction_input; > + gc->direction_output = ad7768_gpio_direction_output; > + gc->get = ad7768_gpio_get; > + gc->set = ad7768_gpio_set; > + > + return devm_gpiochip_add_data(dev, &st->gc, st); > +} -- With Best Regards, Andy Shevchenko ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver 2026-07-09 8:50 ` [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil 2026-07-09 9:38 ` sashiko-bot 2026-07-09 11:05 ` Andy Shevchenko @ 2026-07-10 2:14 ` Jonathan Cameron 2026-07-10 20:06 ` Linus Walleij 3 siblings, 0 replies; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 2:14 UTC (permalink / raw) To: Janani Sunil Cc: Nuno Sá, Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On Thu, 9 Jul 2026 10:50:16 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > The AD7768/AD7768-4 ADC exposes 5 general-purpose I/O pins that can be > independently configured as inputs or outputs. Add an auxiliary bus driver > to expose these pins as a GPIO chip, registered by the parent IIO driver. > > The driver uses the parent's regmap for register access and delegates > runtime power management to the parent device. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> A few things inline. > diff --git a/drivers/gpio/gpio-ad7768.c b/drivers/gpio/gpio-ad7768.c > new file mode 100644 > index 000000000000..c2f01b1abd7c > --- /dev/null > +++ b/drivers/gpio/gpio-ad7768.c ... > +static int ad7768_gpio_probe(struct auxiliary_device *adev, > + const struct auxiliary_device_id *id) > +{ > + struct device *dev = &adev->dev; > + const char *label = dev_get_platdata(dev); > + struct ad7768_gpio_state *st; > + struct gpio_chip *gc; > + int ret; > + > + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); > + if (!st) > + return -ENOMEM; > + > + st->parent = dev->parent; > + st->regmap = dev_get_regmap(dev->parent, NULL); > + > + ret = devm_mutex_init(dev, &st->lock); > + if (ret) > + return ret; > + > + ret = pm_runtime_resume_and_get(st->parent); I'd use the ACQUIRE stuff you used else where and not worry about maybe keeping power on a tiny bit long. > + if (ret < 0) > + return ret; > + > + ret = regmap_update_bits(st->regmap, AD7768_REG_GPIO_CONTROL, > + AD7768_GPIO_UGPIO_ENABLE, > + AD7768_GPIO_UGPIO_ENABLE); set_bits() > + > + pm_runtime_mark_last_busy(st->parent); As before this is wrong. > + pm_runtime_put_autosuspend(st->parent); > + > + if (ret < 0) > + return ret; > + > + gc = &st->gc; > + gc->label = label; > + gc->base = -1; > + gc->ngpio = AD7768_NUM_GPIOS; > + gc->parent = dev; > + gc->owner = THIS_MODULE; > + gc->can_sleep = true; > + gc->get_direction = ad7768_gpio_get_direction; > + gc->direction_input = ad7768_gpio_direction_input; > + gc->direction_output = ad7768_gpio_direction_output; > + gc->get = ad7768_gpio_get; > + gc->set = ad7768_gpio_set; > + > + return devm_gpiochip_add_data(dev, &st->gc, st); use gc given you have it. > +} > ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver 2026-07-09 8:50 ` [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil ` (2 preceding siblings ...) 2026-07-10 2:14 ` Jonathan Cameron @ 2026-07-10 20:06 ` Linus Walleij 3 siblings, 0 replies; 27+ messages in thread From: Linus Walleij @ 2026-07-10 20:06 UTC (permalink / raw) To: Janani Sunil, Michael Walle Cc: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev Hi Janani, thanks for your patch! On Thu, Jul 9, 2026 at 10:51 AM Janani Sunil <janani.sunil@analog.com> wrote: > The AD7768/AD7768-4 ADC exposes 5 general-purpose I/O pins that can be > independently configured as inputs or outputs. Add an auxiliary bus driver > to expose these pins as a GPIO chip, registered by the parent IIO driver. > > The driver uses the parent's regmap for register access and delegates > runtime power management to the parent device. > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> (...) When I look at these functions I see they are very close to something the GPIO_REGMAP helpers should be able to do. Bascically all that stops you from doing this is: > +static int ad7768_gpio_direction_input(struct gpio_chip *chip, > + unsigned int offset) > +{ > + struct ad7768_gpio_state *st = gpiochip_get_data(chip); > + > + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); > + int ret = PM_RUNTIME_ACQUIRE_ERR(&pm); These two things. > +static int ad7768_gpio_get_direction(struct gpio_chip *chip, > + unsigned int offset) > +{ > + struct ad7768_gpio_state *st = gpiochip_get_data(chip); > + unsigned int val; > + int ret; > + > + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(st->parent, pm); > + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); > + if (ret) > + return ret; Here again, etc. Can't we just add a struct device *pm_dev; to struct gpio_regmap_config, pass st->parent there at init, modify drivers/gpio/gpio-regmap.c adding: static int gpio_regmap_pm(struct gpio_regmap *gpio) { PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(gpio->pm_device, pm); ret = PM_RUNTIME_ACQUIRE_ERR(&pm); if (ret) return ret; return 0; } And call this in each of the standard callbacks so gpio-regmap.c can handle PM as well for free? The above can be an ifdef stub if runtime PM is not enabled. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 6/6] Documentation: iio: Add AD7768 Documentation 2026-07-09 8:50 [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil ` (4 preceding siblings ...) 2026-07-09 8:50 ` [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil @ 2026-07-09 8:50 ` Janani Sunil 2026-07-09 9:41 ` sashiko-bot 2026-07-10 2:16 ` Jonathan Cameron 5 siblings, 2 replies; 27+ messages in thread From: Janani Sunil @ 2026-07-09 8:50 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 Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev, Janani Sunil Add driver documentation for AD7768 Signed-off-by: Janani Sunil <janani.sunil@analog.com> --- Documentation/iio/ad7768.rst | 207 +++++++++++++++++++++++++++++++++++++++++++ Documentation/iio/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 209 insertions(+) diff --git a/Documentation/iio/ad7768.rst b/Documentation/iio/ad7768.rst new file mode 100644 index 000000000000..438e33d4f375 --- /dev/null +++ b/Documentation/iio/ad7768.rst @@ -0,0 +1,207 @@ +.. 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 +================== + +Power modes +----------- + +The AD7768 family supports three configurable 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 power mode affects the available sampling frequencies and power consumption. +Set via the ``adi,power-mode`` device tree property. + +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 is configured via the ``adi,data-lines-number`` +device tree property. + +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 + +Each channel can be assigned to either Mode A or Mode B via the +``adi,ch-mode`` device tree property. + +Channels assigned to the same mode share the same runtime filter and +decimation configuration. The per-channel IIO attributes for sampling +frequency and filter type are aliases to the channel's assigned mode +profile rather than independent per-channel settings. + +Precharge and reference buffers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Per-channel buffer control for optimal signal integrity: + +* Positive input precharge buffer (``adi,prebuf-pos-en``) +* Negative input precharge buffer (``adi,prebuf-neg-en``) +* Positive reference buffer (``adi,refbuf-pos-en``) +* Negative reference buffer (``adi,refbuf-neg-en``) + +Common mode voltage +------------------- + +Configurable common mode output voltage levels: + +* (AVDD - AVSS) / 2V +* 1.65V +* 2.5V +* 2.14V + +Set via the ``adi,common-mode-output`` device tree property. +The common mode buffer can be powered down using ``adi,vcm-power-down``. + +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. + +Device attributes +================= + +The following IIO attributes are available for each enabled channel: + +Sampling frequency +------------------ + ++---------------------------------------+--------------------------------------------------------------+ +| Attribute | Description | ++=======================================+==============================================================+ +| ``in_voltage<N>_sampling_frequency`` | Current sampling frequency in Hz for channel N. Channels | +| | assigned to the same mode profile read back the same value. | ++---------------------------------------+--------------------------------------------------------------+ + +Filter configuration +--------------------- + ++---------------------------------------+--------------------------------------------------------------+ +| Attribute | Description | ++=======================================+==============================================================+ +| ``in_voltage<N>_filter_type`` | Current filter type for channel N: "wideband" or "sinc5". | +| | Channels assigned to the same mode profile share this value. | ++---------------------------------------+--------------------------------------------------------------+ +| ``in_voltage<N>_filter_type_ | Available filter types for channel N: "wideband sinc5". | +| available`` | | ++---------------------------------------+--------------------------------------------------------------+ + +Per-channel calibration +----------------------- + ++---------------------------------------+--------------------------------------------------------------+ +| Attribute | Description | ++=======================================+==============================================================+ +| ``in_voltage<N>_calibbias`` | Channel offset calibration value | ++---------------------------------------+--------------------------------------------------------------+ +| ``in_voltage<N>_calibscale`` | Channel gain calibration value | ++---------------------------------------+--------------------------------------------------------------+ +| ``in_voltage<N>_phase`` | Channel phase calibration value. | ++---------------------------------------+--------------------------------------------------------------+ + +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-triggered 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 + + # Updating one channel also updates all channels assigned to the same + # mode profile (Mode A or Mode B) selected by adi,ch-mode in devicetree + + # Read calibration scale for channel 0 + cat /sys/bus/iio/devices/iio:device0/in_voltage0_calibscale + + # Read phase calibration for channel 0 + cat /sys/bus/iio/devices/iio:device0/in_voltage0_phase + + +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 3de7ebcc4ee7..b93c77d3a4c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1639,6 +1639,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/gpio/gpio-ad7768.c F: drivers/iio/adc/ad7768.c -- 2.43.0 ^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH 6/6] Documentation: iio: Add AD7768 Documentation 2026-07-09 8:50 ` [PATCH 6/6] Documentation: iio: Add AD7768 Documentation Janani Sunil @ 2026-07-09 9:41 ` sashiko-bot 2026-07-10 2:16 ` Jonathan Cameron 1 sibling, 0 replies; 27+ messages in thread From: sashiko-bot @ 2026-07-09 9:41 UTC (permalink / raw) To: Janani Sunil; +Cc: robh, devicetree, conor+dt Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] The sysfs attribute ``in_voltage<N>_filter_type_available`` is split across two lines in the RST grid table, which will introduce an unwanted space when rendered. -- --- Patch [6]: [PATCH 6/6] Documentation: iio: Add AD7768 Documentation --- commit 14bceb1fc9d8c59930a466f42580515ee4eaaf95 Author: Janani Sunil <janani.sunil@analog.com> Documentation: iio: Add AD7768 Documentation This commit adds driver documentation for the Analog Devices AD7768 and AD7768-4 ADCs. It covers supported features, power modes, data output configurations, and IIO device attributes. > diff --git a/Documentation/iio/ad7768.rst b/Documentation/iio/ad7768.rst > new file mode 100644 > index 0000000000000..438e33d4f3759 > --- /dev/null > +++ b/Documentation/iio/ad7768.rst [ ... ] > +Filter configuration > +--------------------- > + > ++---------------------------------------+--------------------------------------------------------------+ > +| Attribute | Description | > ++=======================================+==============================================================+ > +| ``in_voltage<N>_filter_type`` | Current filter type for channel N: "wideband" or "sinc5". | > +| | Channels assigned to the same mode profile share this value. | > ++---------------------------------------+--------------------------------------------------------------+ > +| ``in_voltage<N>_filter_type_ | Available filter types for channel N: "wideband sinc5". | > +| available`` | | [Severity: Low] This isn't a bug, but splitting the inline literal across two lines in this RST grid table will cause Sphinx to insert an unwanted space. Does this result in the sysfs attribute being incorrectly displayed as in_voltage<N>_filter_type_ available in the rendered HTML or PDF output? > ++---------------------------------------+--------------------------------------------------------------+ -- Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com?part=6 ^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 6/6] Documentation: iio: Add AD7768 Documentation 2026-07-09 8:50 ` [PATCH 6/6] Documentation: iio: Add AD7768 Documentation Janani Sunil 2026-07-09 9:41 ` sashiko-bot @ 2026-07-10 2:16 ` Jonathan Cameron 1 sibling, 0 replies; 27+ messages in thread From: Jonathan Cameron @ 2026-07-10 2:16 UTC (permalink / raw) To: Janani Sunil Cc: Nuno Sá, Michael Hennerich, David Lechner, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski, Jonathan Corbet, Shuah Khan, linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev On Thu, 9 Jul 2026 10:50:17 +0200 Janani Sunil <janani.sunil@analog.com> wrote: > Add driver documentation for AD7768 > > Signed-off-by: Janani Sunil <janani.sunil@analog.com> > --- > Documentation/iio/ad7768.rst | 207 +++++++++++++++++++++++++++++++++++++++++++ > Documentation/iio/index.rst | 1 + > MAINTAINERS | 1 + > 3 files changed, 209 insertions(+) > > diff --git a/Documentation/iio/ad7768.rst b/Documentation/iio/ad7768.rst > new file mode 100644 > index 000000000000..438e33d4f375 > --- /dev/null > +++ b/Documentation/iio/ad7768.rst > @@ -0,0 +1,207 @@ > +.. 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 Too long. Wrap those. In general keep to 80 chars for docs unless you are breaking up some thing greppable like a file name. > + > +Supported features > +================== > + > +Power modes > +----------- > + > +The AD7768 family supports three configurable 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 power mode affects the available sampling frequencies and power consumption. > +Set via the ``adi,power-mode`` device tree property. Control it from the sampling frequency probably as David suggested. > + > +Per-channel calibration > +----------------------- > + > ++---------------------------------------+--------------------------------------------------------------+ > +| Attribute | Description | > ++=======================================+==============================================================+ > +| ``in_voltage<N>_calibbias`` | Channel offset calibration value | > ++---------------------------------------+--------------------------------------------------------------+ > +| ``in_voltage<N>_calibscale`` | Channel gain calibration value | > ++---------------------------------------+--------------------------------------------------------------+ > +| ``in_voltage<N>_phase`` | Channel phase calibration value. | Be consistent on . Also does this mean the phase offset is a tweak, or do we know the scaling? > ++---------------------------------------+--------------------------------------------------------------+ > + ^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2026-07-11 14:40 UTC | newest] Thread overview: 27+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-09 8:50 [PATCH 0/6] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil 2026-07-09 8:50 ` [PATCH 1/6] dt-bindings: iio: adc: Add AD7768 Janani Sunil 2026-07-09 8:59 ` sashiko-bot 2026-07-09 15:43 ` David Lechner 2026-07-10 0:33 ` Jonathan Cameron 2026-07-11 14:40 ` David Lechner 2026-07-10 1:39 ` Jonathan Cameron 2026-07-09 8:50 ` [PATCH 2/6] iio: backend: Add support for CRC Janani Sunil 2026-07-09 9:03 ` sashiko-bot 2026-07-10 0:36 ` Jonathan Cameron 2026-07-09 8:50 ` [PATCH 3/6] iio: adc: adi-axi-adc: " Janani Sunil 2026-07-09 9:14 ` sashiko-bot 2026-07-09 15:54 ` David Lechner 2026-07-10 0:39 ` Jonathan Cameron 2026-07-10 0:46 ` Jonathan Cameron 2026-07-09 8:50 ` [PATCH 4/6] iio: adc: Add AD7768 IIO Driver support Janani Sunil 2026-07-09 9:27 ` sashiko-bot 2026-07-10 2:10 ` Jonathan Cameron 2026-07-10 7:41 ` Uwe Kleine-König 2026-07-09 8:50 ` [PATCH 5/6] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil 2026-07-09 9:38 ` sashiko-bot 2026-07-09 11:05 ` Andy Shevchenko 2026-07-10 2:14 ` Jonathan Cameron 2026-07-10 20:06 ` Linus Walleij 2026-07-09 8:50 ` [PATCH 6/6] Documentation: iio: Add AD7768 Documentation Janani Sunil 2026-07-09 9:41 ` sashiko-bot 2026-07-10 2:16 ` Jonathan Cameron
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox