* [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support
@ 2026-08-06 15:41 Janani Sunil
2026-08-06 15:41 ` [PATCH v2 1/7] dt-bindings: iio: adc: Add AD7768 Janani Sunil
` (6 more replies)
0 siblings, 7 replies; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil
Add support for Analog Devices AD7768 and AD7768-4, 24-bit simultaneous
sampling Sigma-Delta ADCs with automatic power mode selection.
Datasheet:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768.pdf
**Device Overview:**
The AD7768 is an 8-channel, 24-bit simultaneous sampling ADC. The
AD7768-4 is a 4-channel variant. Both support configurable power modes,
programmable decimation filters, and a high-speed serial interface with
1, 2, 4, or 8 data output lines. Per-channel offset, gain, and phase
calibration registers are available.
**Features Implemented:**
- Support for AD7768 (8-channel) and AD7768-4 (4-channel) via device
match data.
- Automatic low-noise power mode selection (fast, median, low power)
with associated decimation rate and output data rate tables.
- Per-channel offset, gain, and conversion delay via IIO attributes.
- SPI communication with custom regmap bus for the device's protocol.
- IIO backend integration with adi-axi-adc for high-speed data capture.
- CRC data integrity verification over the serial interface.
- GPIO auxiliary driver to expose the 5 on-chip GPIO pins.
- VCM common-mode output exposed through the regulator framework, with
runtime PM management while the output is enabled.
**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: regmap**: Add optional runtime PM integration and support for
reading output values from a separate register.
6. **gpio**: Auxiliary bus GPIO driver for the AD7768's 5 general-purpose
I/O pins, reusing the parent's regmap and runtime PM.
7. **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.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
Changes in v2:
- Renamed and expanded supply descriptions, split reference to
ref1-supply and ref2-supply.
- Added START and synchronization signal descriptions.
- Added variant-specific channel and data-line validation.
- Made adi,data-lines-number optional, defaulting to the maximum.
- Renamed the precharge and reference-buffer properties, removed channel
mode and power mode properties.
- Exposed the VCM output through the regulator framework and updated
documentation accordingly.
- Improved runtime PM initialization and requested initial autosuspend
after probe.
- Automatically assigned channels to two hardware mode profiles.
- Selected the lowest noise compatible power mode when ODR ranges overlap.
- Replaced the raw phase attribute with the standard conversion-delay
attribute.
- Added filter-settling delays based on the selected filter and ODR.
- Fixed AD7768-4 register and channel-bit mapping, including standby,
calibration, phase, precharge and reference-buffer handling.
- Added direct-mode protection around runtime channel configuration.
- Added calibration register range validation and documented
calibration values as raw unsigned 24-bit values.
- Added the required reset pulse delay derived from MCLK.
- Added a generic GPIO-regmap patch providing runtime-PM integration
and separate output-register readback.
- Converted the AD7768 GPIO auxiliary driver to use gpio-regmap.
- Documented that AXI ADC CRC control bit 8 is common to the AXI ADC
cores.
- Added the missing AXI ADC mutex initialization.
- Expanded the driver documentation to describe dynamic channel
grouping, power-mode selection, VCM regulation and conversion delay.
- Link to v1: https://lore.kernel.org/r/20260709-ad7768-driver-v1-0-44e1194fd96a@analog.com
---
Janani Sunil (7):
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: regmap: Add runtime PM and read_output_reg_set support
gpio: ad7768: Add AD7768 GPIO auxiliary driver
Documentation: iio: Add AD7768 Documentation
.../devicetree/bindings/iio/adc/adi,ad7768.yaml | 343 ++++
Documentation/iio/ad7768.rst | 240 +++
Documentation/iio/index.rst | 1 +
MAINTAINERS | 10 +
drivers/gpio/Kconfig | 12 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ad7768.c | 78 +
drivers/gpio/gpio-regmap.c | 113 +-
drivers/iio/adc/Kconfig | 16 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ad7768.c | 1720 ++++++++++++++++++++
drivers/iio/adc/adi-axi-adc.c | 26 +
drivers/iio/industrialio-backend.c | 33 +
include/linux/gpio/regmap.h | 8 +
include/linux/iio/backend.h | 6 +
15 files changed, 2592 insertions(+), 16 deletions(-)
---
base-commit: b75697ef53e435d894833dad2a8c2fa882cb9b19
change-id: 20260708-ad7768-driver-10cd15296c20
Best regards,
--
Janani Sunil <janani.sunil@analog.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/7] dt-bindings: iio: adc: Add AD7768
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
@ 2026-08-06 15:41 ` Janani Sunil
2026-08-06 15:41 ` [PATCH v2 2/7] iio: backend: Add support for CRC Janani Sunil
` (5 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil
Devicetree Bindings for AD7768-4 (4 channel) and AD7768 (8 channel)
simultaneous sampling ADCs.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
.../devicetree/bindings/iio/adc/adi,ad7768.yaml | 343 +++++++++++++++++++++
MAINTAINERS | 7 +
2 files changed, 350 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..8688b94c1ec0
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768.yaml
@@ -0,0 +1,343 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad7768.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD7768 and AD7768-4 ADC
+
+maintainers:
+ - Janani Sunil <janani.sunil@analog.com>
+
+description: |
+ The AD7768 is an 8-channel, 24-bit simultaneous sampling ADC with configurable
+ power and performance modes. The AD7768-4 is a 4-channel version.
+
+ This binding covers SPI mode only (register access via SPI). Pin control mode,
+ where configuration is set via hardware pins rather than SPI registers, is not
+ supported.
+
+ The io-backend represents the high-speed data output interface, consuming the
+ DOUTx data lines, DCLK (data clock), and DRDY (data ready) signals.
+
+ Datasheet at:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768.pdf
+
+ HDL reference design:
+ https://github.com/analogdevicesinc/hdl/tree/main/projects/ad7768evb
+
+properties:
+ compatible:
+ enum:
+ - adi,ad7768
+ - adi,ad7768-4
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+ description:
+ Master clock (MCLK). Can be supplied as a CMOS clock on the XTAL2/MCLK
+ pin, a crystal oscillator connected between the XTAL1 and XTAL2 pins, or
+ an LVDS differential clock on the XTAL1/XTAL2 pin pair. The clock source
+ is selected by the CLK_SEL hardware pin.
+
+ avdd1-supply:
+ description:
+ Analog supply voltage for the AVDD1A and AVDD1B pins (5V ± 10% with
+ respect to AVSS). Powers the analog front end, reference input, and
+ common-mode output circuitry. Both pins must be tied to the same
+ potential.
+
+ avss-supply:
+ description:
+ Negative analog supply rail for the AVSS1A, AVSS1B, AVSS2A, AVSS2B,
+ and AVSS pins. This rail is referenced to DGND and all AVSS pins must
+ be tied to the same potential. In single-supply operation AVSS is
+ nominally 0V; in split-supply operation it can range from 0V to
+ -2.75V with respect to DGND.
+
+ avdd2-supply:
+ description:
+ Analog supply voltage for the AVDD2A and AVDD2B pins (2V to 5.5V with
+ respect to AVSS). Connects to internal 1.8V analog LDO regulators that
+ power the ADC core. Both pins must be tied to the same potential.
+
+ iovdd-supply:
+ description:
+ Digital supply voltage IOVDD (2.25V to 3.6V with respect to DGND).
+ Sets the logic levels for all interface pins and powers the digital
+ processing core via an internal LDO. For 1.8V operation (1.72V to
+ 1.88V), IOVDD must be tied to the DREGCAP pin.
+
+ ref1-supply:
+ description:
+ Differential reference voltage supply for the REF1+/REF1- input pair.
+ REF1+ voltage range is from (AVSS + 1V) to AVDD1; REF1- voltage range
+ is from AVSS to (AVDD1 - 1V). The differential reference voltage range
+ is 1V to |AVDD1 - AVSS|. On the AD7768 this reference is used for
+ channels 0 to 3; on the AD7768-4 for channels 0 and 1.
+
+ ref2-supply:
+ description:
+ Differential reference voltage supply for the REF2+/REF2- input pair.
+ REF2+ voltage range is from (AVSS + 1V) to AVDD1; REF2- voltage range
+ is from AVSS to (AVDD1 - 1V). The differential reference voltage range
+ is 1V to |AVDD1 - AVSS|. On the AD7768 this reference is used for
+ channels 4 to 7; on the AD7768-4 for channels 2 and 3.
+
+ reset-gpios:
+ maxItems: 1
+ description: GPIO connected to the active-low RESET pin
+
+ start-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the START pin. An active-low pulse on this pin
+ synchronizes the device to other AD7768 devices. When not used, this
+ pin must be tied to a logic high level through a pull-up resistor.
+ When used, SYNC_OUT must be wired back to SYNC_IN on all devices in
+ the synchronization chain.
+
+ sync-in-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the SYNC_IN pin. Receives the synchronization pulse
+ generated on SYNC_OUT in response to a START pulse. Must be synchronous
+ to the MCLK clock domain. In multi-device or daisy-chained systems,
+ two successive synchronization pulses must be applied to guarantee all
+ ADCs are synchronized.
+
+ sync-out-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the SYNC_OUT pin. Outputs a synchronization pulse
+ synchronous to MCLK in response to a START pulse or SPI_SYNC command.
+ In single-device configurations this pin must be wired back to SYNC_IN.
+ In multi-device configurations it can be routed to the SYNC_IN pins of
+ all devices in the chain.
+
+ gpio-controller: true
+
+ '#gpio-cells':
+ const: 2
+
+ adi,data-lines-number:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [1, 2, 4, 8]
+ description:
+ Number of DOUTx lines used for the high-speed data output interface
+ (distinct from the SPI control interface). AD7768 supports 1, 2, or 8
+ lines; AD7768-4 supports 1 or 4 lines. Defaults to the maximum number
+ of lines for the selected variant if omitted.
+
+ regulators:
+ type: object
+ description: Container for the on-chip VCM regulator.
+ properties:
+ vcm-output:
+ type: object
+ description:
+ The VCM pin provides a buffered common-mode voltage output used to
+ bias the analog inputs. It supports three fixed output levels of
+ 1650000 uV, 2500000 uV, and 2140000 uV, as well as a
+ supply-dependent (AVDD1 - AVSS) / 2 output derived from
+ avdd1-supply. Select the voltage by setting regulator-min-microvolt
+ and regulator-max-microvolt to the same value. Use
+ regulator-always-on when no DT consumer node references the VCM
+ output. The VCM circuitry is associated with channel 0. When VCM is
+ used externally, channel@0 must be present and kept enabled because
+ placing channel 0 in standby disables the VCM output.
+ $ref: /schemas/regulator/regulator.yaml#
+ unevaluatedProperties: false
+ additionalProperties: false
+
+ io-backends:
+ maxItems: 1
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+patternProperties:
+ "^channel@[0-7]$":
+ type: object
+ description: |
+ Represents the external channels which are connected to the device.
+ AD7768 supports channels 0-7, AD7768-4 supports channels 0-3.
+
+ properties:
+ reg:
+ minimum: 0
+ maximum: 7
+ description: The channel number
+
+ adi,prechargebuf-pos-enable:
+ type: boolean
+ description: Enable positive input precharge buffer
+
+ adi,prechargebuf-neg-enable:
+ type: boolean
+ description: Enable negative input precharge buffer
+
+ adi,refbuf-pos-enable:
+ type: boolean
+ description: Enable positive reference buffer
+
+ adi,refbuf-neg-enable:
+ type: boolean
+ description: Enable negative reference buffer
+
+ required:
+ - reg
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - avdd1-supply
+ - avss-supply
+ - avdd2-supply
+ - iovdd-supply
+ - ref1-supply
+ - ref2-supply
+ - 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>;
+
+ avdd1-supply = <&avdd1_regulator>;
+ avss-supply = <&avss_regulator>;
+ avdd2-supply = <&avdd2_regulator>;
+ iovdd-supply = <&iovdd_regulator>;
+ ref1-supply = <&ref1_regulator>;
+ ref2-supply = <&ref2_regulator>;
+ reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>;
+
+ clocks = <&ad7768_mclk>;
+
+ adi,data-lines-number = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+
+ io-backends = <&iio_backend>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ channel@0 {
+ reg = <0>;
+ adi,prechargebuf-pos-enable;
+ adi,refbuf-pos-enable;
+ };
+
+ channel@1 {
+ reg = <1>;
+ adi,prechargebuf-pos-enable;
+ adi,prechargebuf-neg-enable;
+ adi,refbuf-pos-enable;
+ adi,refbuf-neg-enable;
+ };
+
+ channel@7 {
+ reg = <7>;
+ adi,prechargebuf-pos-enable;
+ adi,refbuf-pos-enable;
+ };
+ };
+ };
+
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@1 {
+ compatible = "adi,ad7768-4";
+ reg = <1>;
+ spi-max-frequency = <1000000>;
+
+ avdd1-supply = <&avdd1_regulator>;
+ avss-supply = <&avss_regulator>;
+ avdd2-supply = <&avdd2_regulator>;
+ iovdd-supply = <&iovdd_regulator>;
+ ref1-supply = <&ref1_regulator>;
+ ref2-supply = <&ref2_regulator>;
+
+ clocks = <&ad7768_mclk>;
+
+ adi,data-lines-number = <4>;
+
+ io-backends = <&iio_backend>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ regulators {
+ vcm-output {
+ regulator-name = "vcm-output";
+ regulator-min-microvolt = <2140000>;
+ regulator-max-microvolt = <2140000>;
+ regulator-always-on;
+ };
+ };
+
+ channel@0 {
+ reg = <0>;
+ adi,prechargebuf-pos-enable;
+ adi,refbuf-pos-enable;
+ };
+
+ channel@3 {
+ reg = <3>;
+ adi,prechargebuf-pos-enable;
+ adi,refbuf-pos-enable;
+ };
+ };
+ };
+...
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] 11+ messages in thread
* [PATCH v2 2/7] iio: backend: Add support for CRC
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
2026-08-06 15:41 ` [PATCH v2 1/7] dt-bindings: iio: adc: Add AD7768 Janani Sunil
@ 2026-08-06 15:41 ` Janani Sunil
2026-08-06 15:41 ` [PATCH v2 3/7] iio: adc: adi-axi-adc: " Janani Sunil
` (4 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, 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 | 6 ++++++
2 files changed, 39 insertions(+)
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index f7a4be8ec320..792c2333b057 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..eddaddb47bea 100644
--- a/include/linux/iio/backend.h
+++ b/include/linux/iio/backend.h
@@ -133,6 +133,8 @@ enum iio_backend_capabilities {
* @filter_type_set: Set filter type.
* @interface_data_align: Perform the data alignment process.
* @num_lanes_set: Set the number of lanes enabled.
+ * @crc_enable: Enable CRC generation and verification.
+ * @crc_disable: Disable CRC generation and verification.
* @ddr_enable: Enable interface DDR (Double Data Rate) mode.
* @ddr_disable: Disable interface DDR (Double Data Rate) mode.
* @data_stream_enable: Enable data stream.
@@ -189,6 +191,8 @@ struct iio_backend_ops {
enum iio_backend_filter_type type);
int (*interface_data_align)(struct iio_backend *back, u32 timeout_us);
int (*num_lanes_set)(struct iio_backend *back, unsigned int num_lanes);
+ int (*crc_enable)(struct iio_backend *back);
+ int (*crc_disable)(struct iio_backend *back);
int (*ddr_enable)(struct iio_backend *back);
int (*ddr_disable)(struct iio_backend *back);
int (*data_stream_enable)(struct iio_backend *back);
@@ -237,6 +241,8 @@ int iio_backend_filter_type_set(struct iio_backend *back,
enum iio_backend_filter_type type);
int iio_backend_interface_data_align(struct iio_backend *back, u32 timeout_us);
int iio_backend_num_lanes_set(struct iio_backend *back, unsigned int num_lanes);
+int iio_backend_crc_enable(struct iio_backend *back);
+int iio_backend_crc_disable(struct iio_backend *back);
int iio_backend_ddr_enable(struct iio_backend *back);
int iio_backend_ddr_disable(struct iio_backend *back);
int iio_backend_data_stream_enable(struct iio_backend *back);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 3/7] iio: adc: adi-axi-adc: Add support for CRC
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
2026-08-06 15:41 ` [PATCH v2 1/7] dt-bindings: iio: adc: Add AD7768 Janani Sunil
2026-08-06 15:41 ` [PATCH v2 2/7] iio: backend: Add support for CRC Janani Sunil
@ 2026-08-06 15:41 ` Janani Sunil
2026-08-06 15:41 ` [PATCH v2 4/7] iio: adc: Add AD7768 IIO Driver support Janani Sunil
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, 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 | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 26b9c75bd4d8..a9e25e09880e 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -47,6 +48,9 @@
#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
#define ADI_AXI_ADC_REG_CNTRL_3 0x004c
+/* Bit 8 is common to all AXI ADC cores; bits 7-0 are per-HDL-project. */
+#define ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK BIT(8)
+
#define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2)
#define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0)
#define AXI_AD485X_PACKET_FORMAT_20BIT 0x0
@@ -567,6 +571,22 @@ static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg,
return regmap_write(st->regmap, reg, writeval);
}
+static int axi_adc_crc_enable(struct iio_backend *back)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK);
+}
+
+static int axi_adc_crc_disable(struct iio_backend *back)
+{
+ struct adi_axi_adc_state *st = iio_backend_get_priv(back);
+
+ return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
+ ADI_AXI_ADC_CNTRL_3_CRC_EN_MASK);
+}
+
static const struct regmap_config axi_adc_regmap_config = {
.val_bits = 32,
.reg_bits = 32,
@@ -615,6 +635,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 = {
@@ -684,6 +706,10 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (!st)
return -ENOMEM;
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 4/7] iio: adc: Add AD7768 IIO Driver support
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (2 preceding siblings ...)
2026-08-06 15:41 ` [PATCH v2 3/7] iio: adc: adi-axi-adc: " Janani Sunil
@ 2026-08-06 15:41 ` Janani Sunil
2026-08-07 0:28 ` Andy Shevchenko
2026-08-06 15:41 ` [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support Janani Sunil
` (2 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil
Add support for AD7768 4/8 channel,simultaneous sampling Sigma-Delta
ADCs. The driver supports configurable decimation filters, per-channel
conversion delay, VCM regulation, runtime PM and IIO backend data
capture.
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 | 1720 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1738 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..1bba066435e3
--- /dev/null
+++ b/drivers/iio/adc/ad7768.c
@@ -0,0 +1,1720 @@
+// 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/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/regulator/driver.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_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_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_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_MCLK_DIV_MSK GENMASK(1, 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)
+
+/* 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)
+/* Hardware supports CRC every 4 or 16 samples; backend supports 4-sample only */
+#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_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 AD7768_PICO_PER_SEC 1000000000000ULL
+#define MAX_FREQ_PER_MODE 6
+#define AD7768_MAX_CHANNEL 8
+#define AD7768_NUM_CHANNEL_MODES 2
+#define AD7768_CALIB_REG_MSB_MSK GENMASK(23, 16)
+#define AD7768_CALIB_REG_MID_MSK GENMASK(15, 8)
+#define AD7768_CALIB_REG_LSB_MSK GENMASK(7, 0)
+#define AD7768_CALIB_REG_MSK GENMASK(23, 0)
+#define AD7768_REV_ID_VAL 0x06
+#define AD7768_WIDEBAND_SETTLING_SAMPLES 68
+#define AD7768_SINC5_SETTLING_SAMPLES 7
+
+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
+};
+
+#define AD7768_MAX_FREQS (MAX_FREQ_PER_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_convdelay_params {
+ unsigned int shift;
+ unsigned int max_raw;
+ u64 step_ps;
+};
+
+struct ad7768_avail_freq {
+ unsigned int n_freqs;
+ int freqs[MAX_FREQ_PER_MODE];
+ 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;
+ const u8 *chan_map;
+ u8 prebuf_split;
+};
+
+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;
+ 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 n_freqs;
+ int freqs[AD7768_MAX_FREQS];
+ unsigned int chn_mode[AD7768_MAX_CHANNEL];
+ unsigned int ch_freq[AD7768_MAX_CHANNEL];
+ u64 ch_convdelay_ps[AD7768_MAX_CHANNEL];
+ enum ad7768_filter_type ch_filter[AD7768_MAX_CHANNEL];
+ struct iio_backend *back;
+ struct regulator_dev *vcm_rdev;
+ unsigned int avdd1_uv;
+
+ __be16 d16 __aligned(IIO_DMA_MINALIGN);
+};
+
+static const unsigned int ad7768_vcm_voltage_table[] = {
+ 0, 1650000, 2500000, 2140000
+};
+
+static int ad7768_vcm_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+
+ if (selector >= ARRAY_SIZE(ad7768_vcm_voltage_table))
+ return -EINVAL;
+
+ if (!selector)
+ return DIV_ROUND_CLOSEST(st->avdd1_uv, 2);
+
+ return ad7768_vcm_voltage_table[selector];
+}
+
+static int ad7768_vcm_enable(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&st->spi->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_PD);
+ if (ret)
+ pm_runtime_put_autosuspend(&st->spi->dev);
+
+ return ret;
+}
+
+static int ad7768_vcm_disable(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ int ret;
+
+ ret = regmap_set_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_PD);
+ if (ret)
+ return ret;
+
+ pm_runtime_put_autosuspend(&st->spi->dev);
+
+ return 0;
+}
+
+static int ad7768_vcm_is_enabled(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ unsigned int val;
+ 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_GENERAL_CONFIG, &val);
+ if (ret)
+ return ret;
+
+ return !(val & AD7768_GEN_CONFIG_VCM_PD);
+}
+
+static int ad7768_vcm_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_SEL_MSK, selector);
+}
+
+static int ad7768_vcm_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct ad7768_state *st = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int ret;
+
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(&st->spi->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(st->regmap, AD7768_REG_GENERAL_CONFIG, &val);
+ if (ret)
+ return ret;
+
+ return FIELD_GET(AD7768_GEN_CONFIG_VCM_SEL_MSK, val);
+}
+
+static const struct regulator_ops ad7768_vcm_ops = {
+ .enable = ad7768_vcm_enable,
+ .disable = ad7768_vcm_disable,
+ .is_enabled = ad7768_vcm_is_enabled,
+ .list_voltage = ad7768_vcm_list_voltage,
+ .set_voltage_sel = ad7768_vcm_set_voltage_sel,
+ .get_voltage_sel = ad7768_vcm_get_voltage_sel,
+};
+
+static const struct regulator_desc ad7768_vcm_desc = {
+ .name = "vcm",
+ .of_match = of_match_ptr("vcm-output"),
+ .n_voltages = ARRAY_SIZE(ad7768_vcm_voltage_table),
+ .ops = &ad7768_vcm_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+};
+
+static int ad7768_register_vcm_regulator(struct device *dev,
+ struct ad7768_state *st)
+{
+ struct regulator_config config = {
+ .dev = dev,
+ .driver_data = st,
+ };
+ int ret;
+
+ /*
+ * Start with VCM disabled so each enabled state is paired with the
+ * runtime PM reference acquired by ad7768_vcm_enable().
+ */
+ ret = regmap_set_bits(st->regmap, AD7768_REG_GENERAL_CONFIG,
+ AD7768_GEN_CONFIG_VCM_PD);
+ if (ret)
+ return ret;
+
+ st->vcm_rdev = devm_regulator_register(dev, &ad7768_vcm_desc, &config);
+ return PTR_ERR_OR_ZERO(st->vcm_rdev);
+}
+
+static const 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 u8 ad7768_chan_map[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+};
+
+static const u8 ad7768_4_chan_map[] = {
+ 0, 1, 4, 5,
+};
+
+static const char * const ad7768_supply_names[] = {
+ "avss", "avdd2", "iovdd", "ref1", "ref2",
+};
+
+static u8 ad7768_map_power_mode_to_regval(u8 x)
+{
+ return x ? (x + 1) : 0;
+}
+
+static int ad7768_set_power_mode(struct ad7768_state *st, unsigned int mode);
+
+static u8 ad7768_channel_mask(const struct ad7768_state *st, u8 ch)
+{
+ return BIT(st->chip_info->chan_map[ch]);
+}
+
+static u8 ad7768_all_channels_mask(const struct ad7768_state *st)
+{
+ u8 mask = 0;
+ unsigned int ch;
+
+ for (ch = 0; ch < st->chip_info->num_channels; ch++)
+ mask |= ad7768_channel_mask(st, ch);
+
+ return mask;
+}
+
+static unsigned int ad7768_offset_reg(const struct ad7768_state *st,
+ unsigned int ch)
+{
+ return AD7768_REG_OFFSET(st->chip_info->chan_map[ch]);
+}
+
+static unsigned int ad7768_gain_reg(const struct ad7768_state *st,
+ unsigned int ch)
+{
+ return AD7768_REG_GAIN(st->chip_info->chan_map[ch]);
+}
+
+static unsigned int ad7768_phase_reg(const struct ad7768_state *st,
+ unsigned int ch)
+{
+ return AD7768_REG_PHASE(st->chip_info->chan_map[ch]);
+}
+
+static u8 ad7768_precharge_buf1_mask(const struct ad7768_state *st, u16 val)
+{
+ return val & GENMASK(st->chip_info->prebuf_split - 1, 0);
+}
+
+static u8 ad7768_precharge_buf2_mask(const struct ad7768_state *st, u16 val)
+{
+ return (val >> st->chip_info->prebuf_split) &
+ GENMASK(st->chip_info->prebuf_split - 1, 0);
+}
+
+static int ad7768_regmap_read(void *context, const void *reg_buf,
+ size_t reg_size, void *val_buf, size_t val_size)
+{
+ struct spi_device *spi = context;
+ struct ad7768_state *st = spi_get_drvdata(spi);
+ u8 *data_val = val_buf;
+ unsigned int reg;
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d16,
+ .len = 2,
+ .cs_change = 1,
+ }, {
+ /*
+ * The second transfer clocks out the readback data, so
+ * we must provide dummy TX bytes while receiving the
+ * response. The device ignores MOSI in this phase, so
+ * reuse st->d16 for both TX and RX.
+ */
+ .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)
+ return ret;
+
+ *data_val = 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,
+ .use_single_read = true,
+ .readable_reg = ad7768_readable_reg,
+};
+
+static const struct regmap_config ad7768_4_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AD7768_REG_CHOP_CTRL,
+ .use_single_read = true,
+ .readable_reg = ad7768_4_readable_reg,
+};
+
+static unsigned int ad7768_get_calib_reg_base(struct ad7768_state *st,
+ const struct iio_chan_spec *chan,
+ bool is_gain)
+{
+ if (is_gain)
+ return ad7768_gain_reg(st, chan->address);
+
+ return ad7768_offset_reg(st, chan->address);
+}
+
+static int ad7768_read_calib_value(struct ad7768_state *st,
+ unsigned int base_reg, unsigned int *val)
+{
+ u8 data[3];
+ int ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_bulk_read(st->regmap, base_reg, data, ARRAY_SIZE(data));
+ if (ret)
+ return ret;
+
+ *val = (data[0] << 16) | (data[1] << 8) | data[2];
+
+ return 0;
+}
+
+static int ad7768_write_calib_value(struct ad7768_state *st,
+ unsigned int base_reg, unsigned int val)
+{
+ int ret;
+
+ if (val > AD7768_CALIB_REG_MSK)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+
+ ret = regmap_write(st->regmap, base_reg,
+ FIELD_GET(AD7768_CALIB_REG_MSB_MSK, val));
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, base_reg + 1,
+ FIELD_GET(AD7768_CALIB_REG_MID_MSK, val));
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, base_reg + 2,
+ FIELD_GET(AD7768_CALIB_REG_LSB_MSK, 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, 0);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(st->regmap, AD7768_REG_DATA_CONTROL,
+ AD7768_DATA_CONTROL_SPI_SYNC_MSK,
+ FIELD_PREP(AD7768_DATA_CONTROL_SPI_SYNC_MSK, 1));
+}
+
+static int ad7768_set_clk_divs(struct ad7768_state *st,
+ unsigned int freq)
+{
+ 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 bool ad7768_freq_supported(const struct ad7768_state *st,
+ unsigned int mode, unsigned int freq)
+{
+ unsigned int i;
+
+ for (i = 0; i < st->avail_freq[mode].n_freqs; i++) {
+ if (freq == st->avail_freq[mode].freq_cfg[i].freq)
+ return true;
+ }
+
+ return false;
+}
+
+static bool ad7768_freq_supported_in_any_mode(const struct ad7768_state *st,
+ unsigned int freq)
+{
+ unsigned int mode;
+
+ for (mode = 0; mode < AD7768_NUM_POWER_MODES; mode++) {
+ if (ad7768_freq_supported(st, mode, freq))
+ return true;
+ }
+
+ return false;
+}
+
+static int ad7768_set_lowest_noise_mode(struct ad7768_state *st,
+ const unsigned long *scan_mask)
+{
+ unsigned int channel;
+ unsigned int mode;
+
+ /*
+ * The output data rate ranges overlap between the power modes. At a
+ * common ODR, the faster mode has lower noise, so prefer the fastest
+ * mode that supports every enabled channel.
+ */
+ for (mode = AD7768_NUM_POWER_MODES; mode-- > 0;) {
+ for (channel = 0; channel < st->chip_info->num_channels; channel++) {
+ if (test_bit(channel, scan_mask) &&
+ !ad7768_freq_supported(st, mode, st->ch_freq[channel]))
+ break;
+ }
+
+ if (channel == st->chip_info->num_channels)
+ return ad7768_set_power_mode(st, mode);
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_set_mode_decimation(struct ad7768_state *st,
+ unsigned int freq, unsigned int mode)
+{
+ 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(mode),
+ AD7768_CH_MODE_DEC_RATE_MSK,
+ AD7768_CH_MODE_DEC_RATE_MODE(f_cfg.dec_rate));
+}
+
+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);
+
+ if (!freq)
+ return -EINVAL;
+
+ if (!ad7768_freq_supported_in_any_mode(st, freq))
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+ st->ch_freq[ch] = freq;
+
+ return 0;
+}
+
+static int ad7768_get_freq_cfg(struct ad7768_state *st, unsigned int freq,
+ 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)
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void ad7768_filter_wait(const unsigned int *mode_freq,
+ const enum ad7768_filter_type *mode_filter,
+ const bool *mode_used)
+{
+ unsigned long t_settle_us = 0;
+ unsigned int mode;
+
+ for (mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ unsigned long t_mode_us;
+ unsigned int settling_samples;
+
+ if (!mode_used[mode] || !mode_freq[mode])
+ continue;
+
+ if (mode_filter[mode] == AD7768_FILTER_TYPE_SINC5)
+ settling_samples = AD7768_SINC5_SETTLING_SAMPLES;
+ else
+ settling_samples = AD7768_WIDEBAND_SETTLING_SAMPLES;
+
+ t_mode_us = DIV_ROUND_UP_ULL((u64)settling_samples *
+ USEC_PER_SEC,
+ mode_freq[mode]);
+ t_settle_us = max(t_settle_us, t_mode_us);
+ }
+
+ if (t_settle_us)
+ fsleep(t_settle_us);
+}
+
+static int ad7768_get_convdelay_params(struct ad7768_state *st, unsigned int ch,
+ struct ad7768_convdelay_params *params)
+{
+ struct ad7768_freq_config f_cfg;
+ unsigned int dec_rate;
+ unsigned int mult;
+ u64 mclk;
+ int ret;
+
+ ret = ad7768_get_freq_cfg(st, st->ch_freq[ch], &f_cfg);
+ if (ret)
+ return ret;
+
+ dec_rate = ad7768_dec_rate[f_cfg.dec_rate];
+ switch (dec_rate) {
+ case 32:
+ params->shift = 3;
+ params->max_raw = 31;
+ mult = 1;
+ break;
+ case 64:
+ params->shift = 2;
+ params->max_raw = 63;
+ mult = 1;
+ break;
+ case 128:
+ params->shift = 1;
+ params->max_raw = 127;
+ mult = 1;
+ break;
+ case 256:
+ params->shift = 0;
+ params->max_raw = 255;
+ mult = 1;
+ break;
+ case 512:
+ params->shift = 0;
+ params->max_raw = 255;
+ mult = 2;
+ break;
+ case 1024:
+ params->shift = 0;
+ params->max_raw = 255;
+ mult = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mclk = clk_get_rate(st->mclk);
+ if (!mclk)
+ return -EINVAL;
+
+ params->step_ps = DIV_ROUND_CLOSEST_ULL((u64)mult *
+ AD7768_PICO_PER_SEC *
+ ad7768_mclk_div[st->power_mode],
+ mclk);
+
+ return 0;
+}
+
+static int ad7768_set_channel_convdelay(struct ad7768_state *st,
+ unsigned int ch)
+{
+ struct ad7768_convdelay_params params;
+ u64 delay_ps = st->ch_convdelay_ps[ch];
+ u64 max_delay_ps;
+ u64 raw;
+ int ret;
+
+ ret = ad7768_get_convdelay_params(st, ch, ¶ms);
+ if (ret)
+ return ret;
+
+ max_delay_ps = (u64)params.max_raw * params.step_ps;
+ if (delay_ps > max_delay_ps)
+ return -EINVAL;
+
+ raw = DIV_ROUND_CLOSEST_ULL(delay_ps, params.step_ps);
+ if (raw > params.max_raw)
+ return -EINVAL;
+
+ return regmap_write(st->regmap, ad7768_phase_reg(st, ch),
+ raw << params.shift);
+}
+
+static int ad7768_find_matching_mode(const bool *mode_used,
+ const unsigned int *mode_freq,
+ const enum ad7768_filter_type *mode_filter,
+ unsigned int freq,
+ enum ad7768_filter_type filter)
+{
+ unsigned int mode;
+
+ for (mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ if (!mode_used[mode] ||
+ (mode_freq[mode] == freq && mode_filter[mode] == filter))
+ return mode;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_apply_channel_modes(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned int mode_freq[AD7768_NUM_CHANNEL_MODES];
+ enum ad7768_filter_type mode_filter[AD7768_NUM_CHANNEL_MODES];
+ bool mode_used[AD7768_NUM_CHANNEL_MODES] = { };
+ unsigned int channel_mask;
+ unsigned int standby_mask;
+ unsigned int max_freq = 0;
+ unsigned int c;
+ int mode, ret;
+
+ guard(mutex)(&st->lock);
+
+ ret = ad7768_set_lowest_noise_mode(st, scan_mask);
+ if (ret == -EINVAL)
+ return dev_err_probe(&st->spi->dev, ret,
+ "No power mode supports all enabled channel frequencies\n");
+ if (ret)
+ return ret;
+
+ channel_mask = ad7768_all_channels_mask(st);
+ standby_mask = channel_mask;
+
+ for (c = 0; c < st->chip_info->num_channels; c++) {
+ unsigned int mask;
+
+ if (!test_bit(c, scan_mask))
+ continue;
+
+ standby_mask &= ~ad7768_channel_mask(st, c);
+
+ mode = ad7768_find_matching_mode(mode_used, mode_freq,
+ mode_filter, st->ch_freq[c],
+ st->ch_filter[c]);
+ if (mode < 0)
+ return dev_err_probe(&st->spi->dev, -EINVAL,
+ "Enabled channels require more than %d mode profiles\n",
+ AD7768_NUM_CHANNEL_MODES);
+
+ mode_freq[mode] = st->ch_freq[c];
+ mode_filter[mode] = st->ch_filter[c];
+ mode_used[mode] = true;
+ st->chn_mode[c] = mode;
+
+ mask = ad7768_channel_mask(st, c);
+ ret = regmap_update_bits(st->regmap, AD7768_REG_CH_MODE_SEL,
+ mask, mode ? mask : 0);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY,
+ channel_mask, standby_mask);
+ if (ret)
+ return ret;
+
+ for (mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
+ if (!mode_used[mode])
+ continue;
+
+ ret = ad7768_set_mode_decimation(st, mode_freq[mode], mode);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD7768_REG_CH_MODE(mode),
+ AD7768_CH_MODE_FILTER_TYPE_MSK,
+ AD7768_CH_MODE_FILTER_TYPE_MODE(mode_filter[mode]));
+ if (ret)
+ return ret;
+
+ max_freq = max(max_freq, mode_freq[mode]);
+ }
+
+ for (c = 0; c < st->chip_info->num_channels; c++) {
+ if (!test_bit(c, scan_mask))
+ continue;
+
+ ret = ad7768_set_channel_convdelay(st, c);
+ if (ret == -EINVAL)
+ return dev_err_probe(&st->spi->dev, ret,
+ "Invalid conversion delay for channel %u\n",
+ c);
+ if (ret)
+ return ret;
+ }
+
+ ret = ad7768_set_clk_divs(st, max_freq);
+ if (ret)
+ return ret;
+
+ ret = ad7768_sync(st);
+ if (ret)
+ return ret;
+
+ /* Apply a filter settling time (Datasheet table 35 and 36) */
+ ad7768_filter_wait(mode_freq, mode_filter, mode_used);
+
+ return 0;
+}
+
+static int ad7768_set_power_mode(struct ad7768_state *st, unsigned int mode)
+{
+ unsigned int regval;
+ int ret;
+
+ if (mode >= AD7768_NUM_POWER_MODES)
+ return -EINVAL;
+
+ 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)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_POWER_MODE_MCLK_DIV_MSK,
+ FIELD_PREP(AD7768_POWER_MODE_MCLK_DIV_MSK, regval));
+ if (ret)
+ return ret;
+
+ ret = ad7768_sync(st);
+ if (ret)
+ return ret;
+
+ st->power_mode = mode;
+
+ return 0;
+}
+
+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;
+ unsigned int calib;
+ 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->ch_freq[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, &calib);
+ if (ret)
+ return ret;
+
+ *val = calib;
+ 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, &calib);
+ if (ret)
+ return ret;
+
+ *val = calib;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_CONVDELAY: {
+ guard(mutex)(&st->lock);
+ iio_val_s64_decompose(st->ch_convdelay_ps[chan->channel],
+ val, val2);
+
+ return IIO_VAL_DECIMAL64_PICO;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ case IIO_CHAN_INFO_CALIBBIAS:
+ case IIO_CHAN_INFO_CALIBSCALE:
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CONVDELAY:
+ return IIO_VAL_DECIMAL64_PICO;
+ default:
+ return -EINVAL;
+ }
+}
+
+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);
+ s64 delay_ps;
+ unsigned int base_reg;
+ int ret;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ 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:
+ if (val < 0 || val > AD7768_CALIB_REG_MSK)
+ return -EINVAL;
+
+ base_reg = ad7768_get_calib_reg_base(st, chan, false);
+ return ad7768_write_calib_value(st, base_reg, val);
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val < 0 || val > AD7768_CALIB_REG_MSK)
+ return -EINVAL;
+
+ base_reg = ad7768_get_calib_reg_base(st, chan, true);
+ return ad7768_write_calib_value(st, base_reg, val);
+
+ case IIO_CHAN_INFO_CONVDELAY: {
+ delay_ps = iio_val_s64_compose(val, val2);
+ if (delay_ps < 0)
+ return -EINVAL;
+
+ guard(mutex)(&st->lock);
+ st->ch_convdelay_ps[chan->channel] = delay_ps;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7768_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ if (info != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ *vals = st->freqs;
+ *type = IIO_VAL_INT;
+ *length = st->n_freqs;
+
+ return IIO_AVAIL_LIST;
+}
+
+static 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;
+
+ ret = ad7768_apply_channel_modes(indio_dev, scan_mask);
+ if (ret)
+ return 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),
+ .chan_map = ad7768_chan_map,
+ .prebuf_split = 8,
+};
+
+static const struct ad7768_chip_info ad7768_4_chip_info = {
+ .name = "ad7768-4",
+ .num_channels = 4,
+ .regmap_config = &ad7768_4_regmap_config,
+ .available_datalines = ad7768_4_available_datalines,
+ .num_datalines = ARRAY_SIZE(ad7768_4_available_datalines),
+ .chan_map = ad7768_4_chan_map,
+ .prebuf_split = 4,
+};
+
+static int ad7768_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ return pm_runtime_resume_and_get(&st->spi->dev);
+}
+
+static int ad7768_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_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_get_fmt = ad7768_write_raw_get_fmt,
+ .write_raw = ad7768_write_raw,
+ .read_avail = ad7768_read_avail,
+ .update_scan_mode = ad7768_update_scan_mode,
+};
+
+static void ad7768_set_available_sampl_freq(struct ad7768_state *st)
+{
+ struct ad7768_avail_freq *avail_freq;
+ unsigned int mclk = clk_get_rate(st->mclk);
+ unsigned int mode;
+
+ for (mode = 0; mode < AD7768_NUM_POWER_MODES; mode++) {
+ avail_freq = &st->avail_freq[mode];
+ for (unsigned int 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->freqs[avail_freq->n_freqs] = freq_cfg.freq;
+ avail_freq->freq_cfg[avail_freq->n_freqs++] = freq_cfg;
+ }
+ }
+
+ if (st->datalines == 1 &&
+ st->chip_info->num_channels == AD7768_MAX_CHANNEL)
+ st->avail_freq[AD7768_FAST_MODE].n_freqs--;
+
+ for (mode = 0; mode < AD7768_NUM_POWER_MODES; mode++) {
+ avail_freq = &st->avail_freq[mode];
+ for (unsigned int i = 0; i < avail_freq->n_freqs; i++) {
+ unsigned int freq = avail_freq->freqs[i];
+
+ if (st->n_freqs && st->freqs[st->n_freqs - 1] >= freq)
+ continue;
+
+ st->freqs[st->n_freqs++] = freq;
+ }
+ }
+}
+
+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;
+
+ /* Use the SPI bus number and chip select to derive a stable per-device ID. */
+ id = (st->spi->controller->bus_num << 8) | spi_get_chipselect(st->spi, 0);
+ adev = __devm_auxiliary_device_create(dev, KBUILD_MODNAME, "gpio",
+ NULL, id);
+ if (!adev)
+ return dev_err_probe(dev, -ENODEV,
+ "Failed to create GPIO auxiliary device\n");
+
+ return 0;
+}
+
+static int ad7768_set_filter_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ guard(mutex)(&st->lock);
+ st->ch_filter[chan->channel] = mode;
+ return 0;
+}
+
+static int ad7768_get_filter_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ guard(mutex)(&st->lock);
+ return st->ch_filter[chan->channel];
+}
+
+static int ad7768_configure_precharge_buffers(struct iio_dev *indio_dev,
+ struct ad7768_precharge_config *precharge_cfg)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ u8 prebuf1_val, prebuf2_val;
+ u16 prebuf_mask = 0;
+ u8 refbufp_val = 0;
+ u8 refbufn_val = 0;
+ int ret;
+ u8 ch;
+
+ 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_channel_mask(st, channel);
+
+ if (precharge_cfg[channel].refbufn)
+ refbufn_val |= ad7768_channel_mask(st, channel);
+ }
+
+ prebuf1_val = ad7768_precharge_buf1_mask(st, ~prebuf_mask);
+ prebuf2_val = ad7768_precharge_buf2_mask(st, ~prebuf_mask);
+
+ ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF1, prebuf1_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_PRECHARGE_BUF2, prebuf2_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_REFP_BUF, refbufp_val);
+ if (ret)
+ return ret;
+
+ return regmap_write(st->regmap, AD7768_REG_REFN_BUF, refbufn_val);
+}
+
+static const char *const ad7768_filter_types[] = {
+ [AD7768_FILTER_TYPE_WIDEBAND] = "wideband",
+ [AD7768_FILTER_TYPE_SINC5] = "sinc5",
+};
+
+static const struct iio_enum ad7768_filter_types_enum = {
+ .items = ad7768_filter_types,
+ .num_items = ARRAY_SIZE(ad7768_filter_types),
+ .set = ad7768_set_filter_mode,
+ .get = ad7768_get_filter_mode,
+};
+
+static struct iio_chan_spec_ext_info ad7768_ext_info[] = {
+ IIO_ENUM("filter_type", IIO_SEPARATE,
+ &ad7768_filter_types_enum),
+ IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, &ad7768_filter_types_enum),
+ {}
+};
+
+static int ad7768_parse_config(struct iio_dev *indio_dev,
+ struct device *dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ const struct ad7768_avail_freq *avail_freq;
+ 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;
+ unsigned int default_freq;
+ int chan_idx = 0;
+ int ret;
+
+ num_channels = 0;
+ device_for_each_child_node_scoped(dev, child) {
+ if (!fwnode_property_present(child, "reg"))
+ continue;
+
+ num_channels++;
+ }
+
+ 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,
+ ad7768_all_channels_mask(st));
+ if (ret)
+ return ret;
+
+ device_for_each_child_node_scoped(dev, child) {
+ if (!fwnode_property_present(child, "reg"))
+ continue;
+
+ 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 from firmware\n",
+ channel);
+
+ ret = regmap_update_bits(st->regmap, AD7768_REG_CH_STANDBY,
+ ad7768_channel_mask(st, channel), 0);
+ if (ret)
+ return ret;
+
+ if (fwnode_property_read_bool(child, "adi,prechargebuf-pos-enable"))
+ precharge_cfg[channel].prebufp_en = true;
+
+ if (fwnode_property_read_bool(child, "adi,prechargebuf-neg-enable"))
+ precharge_cfg[channel].prebufn_en = true;
+
+ if (fwnode_property_read_bool(child, "adi,refbuf-pos-enable"))
+ precharge_cfg[channel].refbufp = true;
+
+ if (fwnode_property_read_bool(child, "adi,refbuf-neg-enable"))
+ precharge_cfg[channel].refbufn = true;
+
+ chan[chan_idx] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_CONVDELAY) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .indexed = 1,
+ .address = channel,
+ .channel = channel,
+ .scan_index = channel,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ },
+ .ext_info = ad7768_ext_info,
+ };
+ chan_idx++;
+ }
+
+ ret = ad7768_configure_precharge_buffers(indio_dev, precharge_cfg);
+ if (ret)
+ return ret;
+
+ st->datalines = st->chip_info->available_datalines[st->chip_info->num_datalines - 1];
+ ret = device_property_read_u32(dev, "adi,data-lines-number",
+ &st->datalines);
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "Invalid \"adi,data-lines-number\" property\n");
+
+ ad7768_set_available_sampl_freq(st);
+
+ /* Start in fast mode; capture setup may select another compatible mode. */
+ scoped_guard(mutex, &st->lock) {
+ ret = ad7768_set_power_mode(st, AD7768_FAST_MODE);
+ }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set power mode\n");
+
+ avail_freq = &st->avail_freq[AD7768_FAST_MODE];
+ default_freq = avail_freq->freq_cfg[avail_freq->n_freqs - 1].freq;
+ for (i = 0; i < st->chip_info->num_channels; i++) {
+ st->ch_freq[i] = default_freq;
+ st->ch_filter[i] = AD7768_FILTER_TYPE_WIDEBAND;
+ }
+
+ 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)
+ break;
+ }
+
+ if (i == len)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid data-lines-number %d for %s\n",
+ st->datalines, st->chip_info->name);
+
+ return 0;
+}
+
+static int ad7768_reset(struct ad7768_state *st)
+{
+ struct reset_control *reset_ctrl;
+ unsigned long reset_low_us;
+ unsigned long mclk;
+ 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) {
+ mclk = clk_get_rate(st->mclk);
+ if (!mclk)
+ return -EINVAL;
+
+ /* Minimum RESET low pulse width: 2 x tMCLK (datasheet Table 1). */
+ reset_low_us = DIV_ROUND_UP_ULL(2ULL * USEC_PER_SEC, mclk);
+
+ ret = reset_control_assert(reset_ctrl);
+ if (ret)
+ return ret;
+
+ fsleep(max_t(unsigned long, 1, reset_low_us));
+
+ ret = reset_control_deassert(reset_ctrl);
+ if (ret)
+ return ret;
+ } else {
+ ret = regmap_write(st->regmap, AD7768_REG_DATA_CONTROL,
+ AD7768_DATA_CONTROL_SPI_RESET_1);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(st->regmap, AD7768_REG_DATA_CONTROL,
+ AD7768_DATA_CONTROL_SPI_RESET_2);
+ if (ret)
+ return ret;
+ }
+
+ /* ADC start-up time after reset: 1.66 ms max (datasheet Table 1) */
+ fsleep(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_get_enable_read_voltage(dev, "avdd1");
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable AVDD1 supply\n");
+
+ st->avdd1_uv = ret;
+
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(ad7768_supply_names),
+ ad7768_supply_names);
+ 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)
+ 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)
+ 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);
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ ret = ad7768_register_vcm_regulator(dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register VCM regulator\n");
+
+ indio_dev->setup_ops = &ad7768_buffer_ops;
+
+ ret = ad7768_gpio_adev_init(st);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = pm_request_autosuspend(dev);
+ if (ret < 0 && ret != -EAGAIN)
+ return dev_err_probe(dev, ret,
+ "Failed to request initial autosuspend\n");
+
+ return 0;
+}
+
+static int ad7768_runtime_suspend(struct device *dev)
+{
+ struct ad7768_state *st = dev_get_drvdata(dev);
+
+ return regmap_set_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_SLEEP_MODE_MSK);
+}
+
+static int ad7768_runtime_resume(struct device *dev)
+{
+ struct ad7768_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_clear_bits(st->regmap, AD7768_REG_POWER_MODE,
+ AD7768_SLEEP_MODE_MSK);
+ if (ret)
+ return ret;
+
+ /* Empirically determined delay to re-enable ADC and digital clocks.*/
+ 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] 11+ messages in thread
* [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (3 preceding siblings ...)
2026-08-06 15:41 ` [PATCH v2 4/7] iio: adc: Add AD7768 IIO Driver support Janani Sunil
@ 2026-08-06 15:41 ` Janani Sunil
2026-08-06 21:31 ` Linus Walleij
2026-08-07 0:02 ` Andy Shevchenko
2026-08-06 15:41 ` [PATCH v2 6/7] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil
2026-08-06 15:41 ` [PATCH v2 7/7] Documentation: iio: Add AD7768 Documentation Janani Sunil
6 siblings, 2 replies; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil
The new pm_dev field in gpio_regmap_config allows a driver to supply a
device for runtime PM. All operations call pm_runtime_resume_and_get()
before accessing the regmap and pm_runtime_put_autosuspend() on return.
The new read_output_reg_set flag when set, gpio_regmap_get() checks the
pin direction first and reads from reg_set_base instead of reg_dat_base
for output pins. Requires both reg_dat_base and reg_set_base to be
configured.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
drivers/gpio/gpio-regmap.c | 113 +++++++++++++++++++++++++++++++++++++-------
include/linux/gpio/regmap.h | 8 ++++
2 files changed, 105 insertions(+), 16 deletions(-)
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index 51b4d69b8740..b94d3c51013a 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -10,6 +10,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -31,6 +32,8 @@ struct gpio_regmap {
unsigned int reg_clr_base;
unsigned int reg_dir_in_base;
unsigned int reg_dir_out_base;
+ struct device *pm_dev;
+ bool read_output_reg_set;
unsigned long *fixed_direction_mask;
unsigned long *fixed_direction_output;
@@ -67,6 +70,23 @@ static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
return 0;
}
+static int gpio_regmap_runtime_get(struct gpio_regmap *gpio)
+{
+ if (!gpio->pm_dev)
+ return 0;
+
+ return pm_runtime_resume_and_get(gpio->pm_dev);
+}
+
+static void gpio_regmap_runtime_put(struct gpio_regmap *gpio)
+{
+ if (gpio->pm_dev)
+ pm_runtime_put_autosuspend(gpio->pm_dev);
+}
+
+static int gpio_regmap_get_direction(struct gpio_chip *chip,
+ unsigned int offset);
+
static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
@@ -79,19 +99,34 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
else
base = gpio_regmap_addr(gpio->reg_set_base);
- ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ if (gpio->read_output_reg_set) {
+ ret = gpio_regmap_get_direction(chip, offset);
+ if (ret < 0)
+ return ret;
+
+ if (ret == GPIO_LINE_DIRECTION_OUT)
+ base = gpio_regmap_addr(gpio->reg_set_base);
+ }
+
+ ret = gpio_regmap_runtime_get(gpio);
if (ret)
return ret;
+ ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ if (ret)
+ goto out_pm;
+
/* ensure we don't spoil any register cache with pin input values */
if (gpio->reg_dat_base == gpio->reg_set_base)
ret = regmap_read_bypassed(gpio->regmap, reg, &val);
else
ret = regmap_read(gpio->regmap, reg, &val);
- if (ret)
- return ret;
+ if (!ret)
+ ret = !!(val & mask);
- return !!(val & mask);
+out_pm:
+ gpio_regmap_runtime_put(gpio);
+ return ret;
}
static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
@@ -102,10 +137,14 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
unsigned int reg, mask, mask_val;
int ret;
- ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ ret = gpio_regmap_runtime_get(gpio);
if (ret)
return ret;
+ ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ if (ret)
+ goto out_pm;
+
if (val)
mask_val = mask;
else
@@ -117,6 +156,8 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
else
ret = regmap_update_bits(gpio->regmap, reg, mask, mask_val);
+out_pm:
+ gpio_regmap_runtime_put(gpio);
return ret;
}
@@ -127,6 +168,10 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
unsigned int base, reg, mask;
int ret;
+ ret = gpio_regmap_runtime_get(gpio);
+ if (ret)
+ return ret;
+
if (val)
base = gpio_regmap_addr(gpio->reg_set_base);
else
@@ -134,9 +179,13 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
if (ret)
- return ret;
+ goto out_pm;
+
+ ret = regmap_write(gpio->regmap, reg, mask);
- return regmap_write(gpio->regmap, reg, mask);
+out_pm:
+ gpio_regmap_runtime_put(gpio);
+ return ret;
}
static bool gpio_regmap_fixed_direction(struct gpio_regmap *gpio,
@@ -182,18 +231,26 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
- ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ ret = gpio_regmap_runtime_get(gpio);
if (ret)
return ret;
+ ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ if (ret)
+ goto out_pm;
+
ret = regmap_read(gpio->regmap, reg, &val);
if (ret)
- return ret;
+ goto out_pm;
if (!!(val & mask) ^ invert)
- return GPIO_LINE_DIRECTION_OUT;
+ ret = GPIO_LINE_DIRECTION_OUT;
else
- return GPIO_LINE_DIRECTION_IN;
+ ret = GPIO_LINE_DIRECTION_IN;
+
+out_pm:
+ gpio_regmap_runtime_put(gpio);
+ return ret;
}
static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio,
@@ -236,16 +293,24 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
- ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ ret = gpio_regmap_runtime_get(gpio);
if (ret)
return ret;
+ ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
+ if (ret)
+ goto out_pm;
+
if (invert)
val = output ? 0 : mask;
else
val = output ? mask : 0;
- return regmap_update_bits(gpio->regmap, reg, mask, val);
+ ret = regmap_update_bits(gpio->regmap, reg, mask, val);
+
+out_pm:
+ gpio_regmap_runtime_put(gpio);
+ return ret;
}
static int gpio_regmap_direction_input(struct gpio_chip *chip,
@@ -260,6 +325,10 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
struct gpio_regmap *gpio = gpiochip_get_data(chip);
int ret;
+ ret = gpio_regmap_runtime_get(gpio);
+ if (ret)
+ return ret;
+
/*
* First check if this is gonna work on a fixed direction line,
* if it doesn't (i.e. this is a fixed input line), then do not
@@ -268,12 +337,18 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
if (gpio_regmap_fixed_direction(gpio, offset)) {
ret = gpio_regmap_try_direction_fixed(gpio, offset, true);
if (ret)
- return ret;
+ goto out_pm;
}
- gpio_regmap_set(chip, offset, value);
+ ret = gpio_regmap_set(chip, offset, value);
+ if (ret)
+ goto out_pm;
+
+ ret = gpio_regmap_set_direction(chip, offset, true);
- return gpio_regmap_set_direction(chip, offset, true);
+out_pm:
+ gpio_regmap_runtime_put(gpio);
+ return ret;
}
void *gpio_regmap_get_drvdata(struct gpio_regmap *gpio)
@@ -307,6 +382,10 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
(!config->reg_dat_base || !config->reg_set_base))
return ERR_PTR(-EINVAL);
+ if (config->read_output_reg_set &&
+ (!config->reg_dat_base || !config->reg_set_base))
+ return ERR_PTR(-EINVAL);
+
/* we don't support having both registers simultaneously for now */
if (config->reg_dir_out_base && config->reg_dir_in_base)
return ERR_PTR(-EINVAL);
@@ -323,6 +402,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
gpio->reg_clr_base = config->reg_clr_base;
gpio->reg_dir_in_base = config->reg_dir_in_base;
gpio->reg_dir_out_base = config->reg_dir_out_base;
+ gpio->pm_dev = config->pm_dev;
+ gpio->read_output_reg_set = config->read_output_reg_set;
chip = &gpio->gpio_chip;
chip->parent = config->parent;
diff --git a/include/linux/gpio/regmap.h b/include/linux/gpio/regmap.h
index 06255756710d..5b353ae5b167 100644
--- a/include/linux/gpio/regmap.h
+++ b/include/linux/gpio/regmap.h
@@ -3,6 +3,8 @@
#ifndef _LINUX_GPIO_REGMAP_H
#define _LINUX_GPIO_REGMAP_H
+#include <linux/types.h>
+
struct device;
struct fwnode_handle;
struct gpio_regmap;
@@ -29,6 +31,10 @@ struct regmap;
* @reg_clr_base: (Optional) clear register base address
* @reg_dir_in_base: (Optional) in setting register base address
* @reg_dir_out_base: (Optional) out setting register base address
+ * @pm_dev: (Optional) Device to use for runtime power management.
+ * @read_output_reg_set:
+ * (Optional) Read output values from @reg_set_base
+ * instead of @reg_dat_base.
* @reg_stride: (Optional) May be set if the registers (of the
* same type, dat, set, etc) are not consecutive.
* @ngpio_per_reg: (Optional) Number of GPIOs per register
@@ -92,6 +98,8 @@ struct gpio_regmap_config {
unsigned int reg_clr_base;
unsigned int reg_dir_in_base;
unsigned int reg_dir_out_base;
+ struct device *pm_dev;
+ bool read_output_reg_set;
int reg_stride;
int ngpio_per_reg;
struct irq_domain *irq_domain;
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 6/7] gpio: ad7768: Add AD7768 GPIO auxiliary driver
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (4 preceding siblings ...)
2026-08-06 15:41 ` [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support Janani Sunil
@ 2026-08-06 15:41 ` Janani Sunil
2026-08-06 15:41 ` [PATCH v2 7/7] Documentation: iio: Add AD7768 Documentation Janani Sunil
6 siblings, 0 replies; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, 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 | 12 +++++++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-ad7768.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 92 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..85cb0c82a823 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1796,6 +1796,18 @@ endmenu
menu "Auxiliary Bus GPIO drivers"
depends on AUXILIARY_BUS
+config GPIO_AD7768
+ tristate "Analog Devices AD7768 GPIO support"
+ depends on AD7768
+ depends on GPIOLIB
+ select GPIO_REGMAP
+ 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..70c024460e0c
--- /dev/null
+++ b/drivers/gpio/gpio-ad7768.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7768 GPIO auxiliary driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/regmap.h>
+#include <linux/module.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_NUM_GPIOS 5
+
+static int ad7768_gpio_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct device *dev = &adev->dev;
+ struct gpio_regmap_config config = {
+ .parent = dev,
+ .label = dev_name(dev->parent),
+ .ngpio = AD7768_NUM_GPIOS,
+ .reg_dat_base = AD7768_REG_GPIO_READ,
+ .reg_set_base = AD7768_REG_GPIO_WRITE,
+ .reg_dir_out_base = AD7768_REG_GPIO_CONTROL,
+ .pm_dev = dev->parent,
+ .read_output_reg_set = true,
+ };
+ struct gpio_regmap *gpio;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev->parent, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(regmap, AD7768_REG_GPIO_CONTROL,
+ AD7768_GPIO_UGPIO_ENABLE);
+ if (ret)
+ return ret;
+
+ config.regmap = regmap;
+ gpio = devm_gpio_regmap_register(dev, &config);
+
+ return PTR_ERR_OR_ZERO(gpio);
+}
+
+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] 11+ messages in thread
* [PATCH v2 7/7] Documentation: iio: Add AD7768 Documentation
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
` (5 preceding siblings ...)
2026-08-06 15:41 ` [PATCH v2 6/7] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil
@ 2026-08-06 15:41 ` Janani Sunil
6 siblings, 0 replies; 11+ messages in thread
From: Janani Sunil @ 2026-08-06 15:41 UTC (permalink / raw)
To: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle
Cc: linux, linux-iio, devicetree, linux-kernel, linux-gpio, linux-doc,
jananisunil.dev, Uwe Kleine-König, Janani Sunil
Add driver documentation for AD7768.
Signed-off-by: Janani Sunil <janani.sunil@analog.com>
---
Documentation/iio/ad7768.rst | 240 +++++++++++++++++++++++++++++++++++++++++++
Documentation/iio/index.rst | 1 +
MAINTAINERS | 1 +
3 files changed, 242 insertions(+)
diff --git a/Documentation/iio/ad7768.rst b/Documentation/iio/ad7768.rst
new file mode 100644
index 000000000000..928d4dbc0fea
--- /dev/null
+++ b/Documentation/iio/ad7768.rst
@@ -0,0 +1,240 @@
+.. 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 power and performance modes:
+
+* **Low power mode** - Optimized for lowest power consumption
+* **Median mode** - Balanced power and performance
+* **Fast mode** - Highest performance with maximum sampling rates
+
+The driver initializes the device in fast mode and uses the maximum fast-mode
+output data rate as the default sampling frequency.
+
+When buffered capture starts, the driver selects the lowest-noise mode that can
+produce the requested output data rates for all enabled channels. Where output
+data rates overlap, fast mode is preferred over median mode, and median mode is
+preferred over low power mode. This prioritizes the lower RMS noise and higher
+dynamic range offered by a faster mode at the same output data rate.
+
+Data output configuration
+-------------------------
+
+The devices support flexible serial data output configurations:
+
+AD7768 data lines
+^^^^^^^^^^^^^^^^^
+
+* 1 data line (DOUT0) - Standard single-lane output
+* 2 data lines (DOUT0, DOUT1) - Dual-lane output for higher throughput
+* 8 data lines (DOUT0-DOUT7) - Maximum throughput, one line per channel
+
+AD7768-4 data lines
+^^^^^^^^^^^^^^^^^^^
+
+* 1 data line (DOUT0) - Standard single-lane output
+* 4 data lines (DOUT0-DOUT3) - Maximum throughput, one line per channel
+
+The number of data lines can be configured via the ``adi,data-lines-number``
+device tree property. If omitted, the driver uses the maximum supported by the
+selected variant: eight lines for AD7768 and four lines for AD7768-4.
+
+Channel configuration
+---------------------
+
+Each channel can be individually configured with:
+
+Channel modes
+^^^^^^^^^^^^^
+
+* **Mode A** - First set of filter and decimation settings
+* **Mode B** - Second set of filter and decimation settings
+
+The hardware provides two mode profiles (A and B), each holding one
+(frequency, filter) combination. When buffered capture is started,
+enabled channels are grouped by their configured (frequency, filter)
+pair. Up to two distinct groups are supported; the driver automatically
+assigns each group to a mode slot and programs the hardware accordingly.
+
+Precharge and reference buffers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Per-channel buffer control for optimal signal integrity:
+
+* Positive input precharge buffer (``adi,prechargebuf-pos-enable``)
+* Negative input precharge buffer (``adi,prechargebuf-neg-enable``)
+* Positive reference buffer (``adi,refbuf-pos-enable``)
+* Negative reference buffer (``adi,refbuf-neg-enable``)
+
+Common mode voltage
+-------------------
+
+The VCM pin provides a buffered common-mode voltage output used to bias
+the analog inputs. The driver exposes this as a standard voltage regulator
+provider under a ``regulators`` subnode in the device tree. Supported
+output voltage levels are:
+
+* (AVDD1 - AVSS) / 2 - Mid-supply (hardware default), reported as half the
+ voltage provided by ``avdd1-supply``
+* 1,650,000 µV - 1.65V
+* 2,500,000 µV - 2.5V
+* 2,140,000 µV - 2.14V
+
+The regulator can be enabled and disabled at runtime using the standard
+regulator framework interfaces.
+
+The VCM circuitry is associated with channel 0. When VCM is used externally,
+``channel@0`` must be present in the device tree and channel 0 must remain
+enabled in the active scan mask. Placing channel 0 in standby disables the VCM
+output.
+
+Filter types
+------------
+
+Two digital filter types are available:
+
+* **Wideband** - Optimized for wide bandwidth applications
+* **Sinc5** - Fifth-order sinc filter for high rejection of out-of-band noise
+
+IIO backend support
+-------------------
+
+The driver integrates with IIO backends (e.g., AXI ADC) for high-speed data
+capture and DMA operations. Features include:
+
+* Automatic channel enable/disable based on scan mask
+* CRC on data interface. CRC replaces the header every 4th output sample.
+* High-throughput buffered data acquisition
+
+GPIO controller
+---------------
+
+The AD7768 includes a 5-pin GPIO controller for auxiliary digital I/O
+operations. The GPIO pins can be configured as inputs or outputs.
+
+Device attributes
+=================
+
+The following IIO attributes are available for each enabled channel:
+
+Sampling frequency
+------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - Attribute
+ - Description
+ * - ``in_voltage<N>_sampling_frequency``
+ - Requested sampling frequency in Hz for channel N. Enabled channels are
+ grouped into up to two profiles at capture time.
+ * - ``in_voltage<N>_sampling_frequency_available``
+ - Available sampling frequencies in Hz for channel N across all power
+ modes, based on the master clock frequency. Buffer setup fails if no
+ single power mode supports the frequencies requested by all enabled
+ channels.
+
+Filter configuration
+---------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - Attribute
+ - Description
+ * - ``in_voltage<N>_filter_type``
+ - Requested filter type for channel N: "wideband" or "sinc5". It is
+ grouped with sampling frequency at capture time.
+ * - ``in_voltage<N>_filter_type_available``
+ - Available filter types for channel N: "wideband sinc5".
+
+Per-channel calibration
+-----------------------
+
+.. list-table::
+ :header-rows: 1
+ :widths: 40 60
+
+ * - Attribute
+ - Description
+ * - ``in_voltage<N>_calibbias``
+ - Raw unsigned 24-bit channel offset register value.
+ * - ``in_voltage<N>_calibscale``
+ - Raw unsigned 24-bit channel gain register value.
+ * - ``in_voltage<N>_convdelay``
+ - Per-channel conversion delay. The driver exposes the phase/modulator
+ delay register in seconds with picosecond precision. Resolution and
+ valid range depend on the decimation ratio in use (see datasheet
+ Table 30).
+
+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
+
+ # Buffer setup fails if enabled channels request more than two distinct
+ # (sampling frequency, filter type) combinations.
+
+ # Read calibration scale for channel 0
+ cat /sys/bus/iio/devices/iio:device0/in_voltage0_calibscale
+
+ # Read conversion delay for channel 0
+ cat /sys/bus/iio/devices/iio:device0/in_voltage0_convdelay
+
+
+Unimplemented features
+======================
+
+* CRC message every 16 samples (CRC_SEL configuration) - currently only
+ supports CRC every 4 samples
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index b02b879b053a..73c58cec7620 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -29,6 +29,7 @@ Industrial I/O Kernel Drivers
ad7380
ad7606
ad7625
+ ad7768
ad7944
ade9000
adf41513
diff --git a/MAINTAINERS b/MAINTAINERS
index 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] 11+ messages in thread
* Re: [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support
2026-08-06 15:41 ` [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support Janani Sunil
@ 2026-08-06 21:31 ` Linus Walleij
2026-08-07 0:02 ` Andy Shevchenko
1 sibling, 0 replies; 11+ messages in thread
From: Linus Walleij @ 2026-08-06 21:31 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, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, linux, linux-iio,
devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev,
Uwe Kleine-König
Hi Janani,
thanks for your patch!
On Thu, Aug 6, 2026 at 5:42 PM Janani Sunil <janani.sunil@analog.com> wrote:
> The new pm_dev field in gpio_regmap_config allows a driver to supply a
> device for runtime PM. All operations call pm_runtime_resume_and_get()
> before accessing the regmap and pm_runtime_put_autosuspend() on return.
>
> The new read_output_reg_set flag when set, gpio_regmap_get() checks the
> pin direction first and reads from reg_set_base instead of reg_dat_base
> for output pins. Requires both reg_dat_base and reg_set_base to be
> configured.
>
> Signed-off-by: Janani Sunil <janani.sunil@analog.com>
Do you have to do this in one big patch?
To me it seems like two unrelated changes that should be
two patches?
> +static int gpio_regmap_get_direction(struct gpio_chip *chip,
> + unsigned int offset);
Can't you just move this function up in the file to
avoid the forward declaration?
> - ret = gpio->reg_mask_xlate(gpio, base, offset, ®, &mask);
> + if (gpio->read_output_reg_set) {
> + ret = gpio_regmap_get_direction(chip, offset);
> + if (ret < 0)
> + return ret;
> +
> + if (ret == GPIO_LINE_DIRECTION_OUT)
> + base = gpio_regmap_addr(gpio->reg_set_base);
> + }
Add a comment above this new code explaining what is going on
otherwise this gets really hard to read and understand.
Other than that it looks good to me.
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support
2026-08-06 15:41 ` [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support Janani Sunil
2026-08-06 21:31 ` Linus Walleij
@ 2026-08-07 0:02 ` Andy Shevchenko
1 sibling, 0 replies; 11+ messages in thread
From: Andy Shevchenko @ 2026-08-07 0:02 UTC (permalink / raw)
To: Janani Sunil
Cc: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, linux, linux-iio,
devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev,
Uwe Kleine-König
On Thu, Aug 06, 2026 at 05:41:24PM +0200, Janani Sunil wrote:
> The new pm_dev field in gpio_regmap_config allows a driver to supply a
> device for runtime PM. All operations call pm_runtime_resume_and_get()
> before accessing the regmap and pm_runtime_put_autosuspend() on return.
>
> The new read_output_reg_set flag when set, gpio_regmap_get() checks the
> pin direction first and reads from reg_set_base instead of reg_dat_base
> for output pins. Requires both reg_dat_base and reg_set_base to be
> configured.
...
> struct gpio_regmap {
> unsigned int reg_clr_base;
> unsigned int reg_dir_in_base;
> unsigned int reg_dir_out_base;
> + struct device *pm_dev;
> + bool read_output_reg_set;
> unsigned long *fixed_direction_mask;
> unsigned long *fixed_direction_output;
> }
Have you run `pahole`? Is it okay to put bool there and not after one of
unsigned int:s (just from the given context, it might be even better location)?
...
> +static int gpio_regmap_runtime_get(struct gpio_regmap *gpio)
> +{
> + if (!gpio->pm_dev)
> + return 0;
> +
> + return pm_runtime_resume_and_get(gpio->pm_dev);
> +}
> +
> +static void gpio_regmap_runtime_put(struct gpio_regmap *gpio)
> +{
> + if (gpio->pm_dev)
> + pm_runtime_put_autosuspend(gpio->pm_dev);
Same pattern as per above.
> +}
...
> +static int gpio_regmap_get_direction(struct gpio_chip *chip,
> + unsigned int offset);
Can this be avoidable?
...
> /* ensure we don't spoil any register cache with pin input values */
> if (gpio->reg_dat_base == gpio->reg_set_base)
> ret = regmap_read_bypassed(gpio->regmap, reg, &val);
> else
> ret = regmap_read(gpio->regmap, reg, &val);
> - if (ret)
> - return ret;
> + if (!ret)
> + ret = !!(val & mask);
>
> - return !!(val & mask);
You want regmap_test_bits() in one case, and the regular pattern in the other.
> +out_pm:
labels should be marked better to show what will be when goto them.
out_pm_put:
> + gpio_regmap_runtime_put(gpio);
> + return ret;
...
> +out_pm:
> + gpio_regmap_runtime_put(gpio);
> return ret;
Ditto.
...
> ret = regmap_read(gpio->regmap, reg, &val);
> if (ret)
> - return ret;
> + goto out_pm;
>
> if (!!(val & mask) ^ invert)
> - return GPIO_LINE_DIRECTION_OUT;
> + ret = GPIO_LINE_DIRECTION_OUT;
> else
> - return GPIO_LINE_DIRECTION_IN;
> + ret = GPIO_LINE_DIRECTION_IN;
Also can be transformed to use regmap_test_bits() (but in a separate change).
...
> struct gpio_regmap_config {
> unsigned int reg_clr_base;
> unsigned int reg_dir_in_base;
> unsigned int reg_dir_out_base;
> + struct device *pm_dev;
> + bool read_output_reg_set;
> int reg_stride;
> int ngpio_per_reg;
> struct irq_domain *irq_domain;
`pahole`?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 4/7] iio: adc: Add AD7768 IIO Driver support
2026-08-06 15:41 ` [PATCH v2 4/7] iio: adc: Add AD7768 IIO Driver support Janani Sunil
@ 2026-08-07 0:28 ` Andy Shevchenko
0 siblings, 0 replies; 11+ messages in thread
From: Andy Shevchenko @ 2026-08-07 0:28 UTC (permalink / raw)
To: Janani Sunil
Cc: Nuno Sá, Michael Hennerich, Jonathan Cameron, David Lechner,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Olivier Moysan, Philipp Zabel, Linus Walleij, Bartosz Golaszewski,
Jonathan Corbet, Shuah Khan, Michael Walle, linux, linux-iio,
devicetree, linux-kernel, linux-gpio, linux-doc, jananisunil.dev,
Uwe Kleine-König
On Thu, Aug 06, 2026 at 05:41:23PM +0200, Janani Sunil wrote:
> Add support for AD7768 4/8 channel,simultaneous sampling Sigma-Delta
> ADCs. The driver supports configurable decimation filters, per-channel
> conversion delay, VCM regulation, runtime PM and IIO backend data
> capture.
Can you split this to the basic minimum + patch per feature?
1.7k LoC is too much for a review, the usual size is ~750±150 per patch.
Here sounds like 3+ patches.
...
> +#define AD7768_PICO_PER_SEC 1000000000000ULL
Don't we have this in time.h (beneath it somewhere)?
...
> +struct ad7768_freq_config {
> + unsigned int freq;
freq_Hz?
> + unsigned int dec_rate;
> +};
...
> +struct ad7768_avail_freq {
> + unsigned int n_freqs;
> + int freqs[MAX_FREQ_PER_MODE];
Negative frequency?
> + struct ad7768_freq_config freq_cfg[MAX_FREQ_PER_MODE];
> +};
...
> +struct ad7768_state {
> + struct spi_device *spi;
> + struct regmap *regmap;
One of them seems redundant. Is regmap created out from &spi->dev?
> + struct mutex lock; /* Protects device register access and configuration */
> + struct clk *mclk;
> + unsigned int datalines;
> + 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 n_freqs;
> + int freqs[AD7768_MAX_FREQS];
> + unsigned int chn_mode[AD7768_MAX_CHANNEL];
> + unsigned int ch_freq[AD7768_MAX_CHANNEL];
> + u64 ch_convdelay_ps[AD7768_MAX_CHANNEL];
> + enum ad7768_filter_type ch_filter[AD7768_MAX_CHANNEL];
> + struct iio_backend *back;
> + struct regulator_dev *vcm_rdev;
> + unsigned int avdd1_uv;
_uV
> +
> + __be16 d16 __aligned(IIO_DMA_MINALIGN);
> +};
...
> +static const unsigned int ad7768_vcm_voltage_table[] = {
> + 0, 1650000, 2500000, 2140000
In this case, keep trailing comma.
> +};
...
> +static int ad7768_vcm_is_enabled(struct regulator_dev *rdev)
> +{
> + struct ad7768_state *st = rdev_get_drvdata(rdev);
> + unsigned int val;
> + 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_GENERAL_CONFIG, &val);
> + if (ret)
> + return ret;
> +
> + return !(val & AD7768_GEN_CONFIG_VCM_PD);
regmap_test_bits() ?
> +}
...
> +static const int ad7768_dec_rate[MAX_FREQ_PER_MODE] = {
> + 32, 64, 128, 256, 512, 1024
Even in the very same page you have inconsistent approach. Make sure your code
is consistent in every aspect. Two+ people wrote the driver?
> +};
> +
> +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 u8 ad7768_chan_map[] = {
> + 0, 1, 2, 3, 4, 5, 6, 7,
> +};
> +
> +static const u8 ad7768_4_chan_map[] = {
> + 0, 1, 4, 5,
> +};
...
> +static int ad7768_set_power_mode(struct ad7768_state *st, unsigned int mode);
Can forward decl be avoided?
...
> +static u8 ad7768_all_channels_mask(const struct ad7768_state *st)
> +{
> + u8 mask = 0;
> + unsigned int ch;
> +
> + for (ch = 0; ch < st->chip_info->num_channels; ch++)
for (unsigned int ch = 0; ch < st->chip_info->num_channels; ch++)
> + mask |= ad7768_channel_mask(st, ch);
> +
> + return mask;
> +}
...
> +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);
> + u8 *data_val = val_buf;
> + unsigned int reg;
> + int ret;
> + struct spi_transfer t[] = {
> + {
> + .tx_buf = &st->d16,
> + .len = 2,
> + .cs_change = 1,
> + }, {
> + /*
> + * The second transfer clocks out the readback data, so
> + * we must provide dummy TX bytes while receiving the
> + * response. The device ignores MOSI in this phase, so
> + * reuse st->d16 for both TX and RX.
> + */
> + .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));
be16_replace_bits() ?
> + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
> + if (ret)
> + return ret;
> +
> + *data_val = FIELD_GET(AD7768_SPI_DATA_MASK, be16_to_cpu(st->d16));
be16_get_bits()?
> + return ret;
> +}
...
> +static int ad7768_read_calib_value(struct ad7768_state *st,
> + unsigned int base_reg, unsigned int *val)
> +{
> + u8 data[3];
> + int ret;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = regmap_bulk_read(st->regmap, base_reg, data, ARRAY_SIZE(data));
> + if (ret)
> + return ret;
> +
> + *val = (data[0] << 16) | (data[1] << 8) | data[2];
get_unaligned_be24()
> + return 0;
> +}
> +static int ad7768_write_calib_value(struct ad7768_state *st,
> + unsigned int base_reg, unsigned int val)
> +{
> + int ret;
> +
> + if (val > AD7768_CALIB_REG_MSK)
> + return -EINVAL;
> +
> + guard(mutex)(&st->lock);
> +
> + ret = regmap_write(st->regmap, base_reg,
> + FIELD_GET(AD7768_CALIB_REG_MSB_MSK, val));
> + if (ret)
> + return ret;
> +
> + ret = regmap_write(st->regmap, base_reg + 1,
> + FIELD_GET(AD7768_CALIB_REG_MID_MSK, val));
> + if (ret)
> + return ret;
> +
> + return regmap_write(st->regmap, base_reg + 2,
> + FIELD_GET(AD7768_CALIB_REG_LSB_MSK, val));
Can you prepare value and use bulk write? Probably you want
put_unaligned_be24().
> +}
...
> +static int ad7768_set_clk_divs(struct ad7768_state *st,
> + unsigned int freq)
> +{
> + 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);
_ULL for sure?! Please, use 32-bit arithmetics for 32-bit values (yes, 32-bit,
they never be 64 in real life).
> + 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);
rounddown_pow_of_two() ?
> + 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 bool ad7768_freq_supported(const struct ad7768_state *st,
> + unsigned int mode, unsigned int freq)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < st->avail_freq[mode].n_freqs; i++) {
for (unsigned int i = 0; i < st->avail_freq[mode].n_freqs; i++) {
> + if (freq == st->avail_freq[mode].freq_cfg[i].freq)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static bool ad7768_freq_supported_in_any_mode(const struct ad7768_state *st,
> + unsigned int freq)
> +{
> + unsigned int mode;
> +
> + for (mode = 0; mode < AD7768_NUM_POWER_MODES; mode++) {
Ditto.
> + if (ad7768_freq_supported(st, mode, freq))
> + return true;
> + }
> +
> + return false;
> +}
...
> +static int ad7768_set_lowest_noise_mode(struct ad7768_state *st,
> + const unsigned long *scan_mask)
> +{
> + unsigned int channel;
> + unsigned int mode;
> +
> + /*
> + * The output data rate ranges overlap between the power modes. At a
> + * common ODR, the faster mode has lower noise, so prefer the fastest
> + * mode that supports every enabled channel.
> + */
> + for (mode = AD7768_NUM_POWER_MODES; mode-- > 0;) {
unsigned int mode = AD7768_NUM_POWER_MODES;
...
while (mode--) {
> + for (channel = 0; channel < st->chip_info->num_channels; channel++) {
> + if (test_bit(channel, scan_mask) &&
> + !ad7768_freq_supported(st, mode, st->ch_freq[channel]))
> + break;
> + }
> +
> + if (channel == st->chip_info->num_channels)
> + return ad7768_set_power_mode(st, mode);
> + }
> +
> + return -EINVAL;
> +}
...
> + struct ad7768_state *st = iio_priv(indio_dev);
> +
> + if (!freq)
> + return -EINVAL;
> +
> + if (!ad7768_freq_supported_in_any_mode(st, freq))
> + return -EINVAL;
> +
> + guard(mutex)(&st->lock);
+ blank line here.
> + st->ch_freq[ch] = freq;
> +
> + return 0;
...
> +static int ad7768_get_freq_cfg(struct ad7768_state *st, unsigned int freq,
> + struct ad7768_freq_config *f_cfg)
> +{
> + unsigned int i;
Embed into for-loop.
> + 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)
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
...
> +static void ad7768_filter_wait(const unsigned int *mode_freq,
> + const enum ad7768_filter_type *mode_filter,
> + const bool *mode_used)
> +{
> + unsigned long t_settle_us = 0;
> + unsigned int mode;
> +
> + for (mode = 0; mode < AD7768_NUM_CHANNEL_MODES; mode++) {
> + unsigned long t_mode_us;
Hmm... Are you sure about the type? Shouldn't it be always 64-bit (or 32-bit)?
> + unsigned int settling_samples;
> +
> + if (!mode_used[mode] || !mode_freq[mode])
> + continue;
> +
> + if (mode_filter[mode] == AD7768_FILTER_TYPE_SINC5)
> + settling_samples = AD7768_SINC5_SETTLING_SAMPLES;
> + else
> + settling_samples = AD7768_WIDEBAND_SETTLING_SAMPLES;
> +
> + t_mode_us = DIV_ROUND_UP_ULL((u64)settling_samples *
> + USEC_PER_SEC,
> + mode_freq[mode]);
> + t_settle_us = max(t_settle_us, t_mode_us);
> + }
> +
> + if (t_settle_us)
> + fsleep(t_settle_us);
> +}
...
I stopped here as this is too much and unreviewable bulk.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-08-07 0:28 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06 15:41 [PATCH v2 0/7] iio: adc: Add AD7768/AD7768-4 ADC driver support Janani Sunil
2026-08-06 15:41 ` [PATCH v2 1/7] dt-bindings: iio: adc: Add AD7768 Janani Sunil
2026-08-06 15:41 ` [PATCH v2 2/7] iio: backend: Add support for CRC Janani Sunil
2026-08-06 15:41 ` [PATCH v2 3/7] iio: adc: adi-axi-adc: " Janani Sunil
2026-08-06 15:41 ` [PATCH v2 4/7] iio: adc: Add AD7768 IIO Driver support Janani Sunil
2026-08-07 0:28 ` Andy Shevchenko
2026-08-06 15:41 ` [PATCH v2 5/7] gpio: regmap: Add runtime PM and read_output_reg_set support Janani Sunil
2026-08-06 21:31 ` Linus Walleij
2026-08-07 0:02 ` Andy Shevchenko
2026-08-06 15:41 ` [PATCH v2 6/7] gpio: ad7768: Add AD7768 GPIO auxiliary driver Janani Sunil
2026-08-06 15:41 ` [PATCH v2 7/7] Documentation: iio: Add AD7768 Documentation Janani Sunil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox