Skip to contentSkip to conversation

iio: adc: Add support for LTC2378 and similar ADCs

PATCH Superseded Marcelo Schmitt 6 participants 20 messages Jun 25 – Jul 1

Subsystems (4)

v4 of 10 Superseded Latest is v10 →

lore
Share & tools
lore

Marcelo Schmitt

author maintainer
#0 lore raw

v4 cover iio: adc: Add support for LTC2378 and similar ADCs

This patch series adds support for LTC2378 and similar low noise, low power, high speed, successive approximation register (SAR) ADCs. These ADCs are similar among each other, varying mainly on the amount of precision bits, maximum sample rate, and input configuration (either fully differential or pseudo-differential).

Patch 1 adds device tree documentation for LTC2378.

Patch 2 enables single-shot sample read with a GPIO connected to the LTC2378 CNV pin.

Patch 3 enables high-speed data captures with SPI offloading.
The setup is similar to AD4030, with a specialized PWM generator being used both for SPI offload triggering and conversion start signaling.

Patch 4 enables running buffered data captures without SPI offloading.

Even though these parts are somewhat similar to AD4000, the wiring configuration for LTC parts is different as well as the available HDL for high speed sample rate mode. Because of that, I propose creating a new device driver for supporting LTC2378-like devices.

Specifications can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf

Despite the initial version of the LTC2378 driver had been developed by Ioan-Daniel. The current version has been greatly rewritten such that very little remained from that initial version. Due to that, having Ioan-Daniel in the author list now seems inaccurate.

Previous submissions:
v3: https://lore.kernel.org/linux-iio/[email protected]/
v2: https://lore.kernel.org/linux-iio/[email protected]/
v1: https://lore.kernel.org/linux-iio/[email protected]/

Change log v3 -> v4:
[DT]
- Added PWM property.
- Documented LTC2338-18 specific refin-supply.
- Picked up Conor's review tag.
[IIO]
- Dropped DMAengine buffer changes.
- Reworked the driver to make IIO channels static according to v3 feedback.
- Updated to use default 8 bits_per_word for non-offloaded transfers.
- Handled refin voltage reference supply for LTC2338.
- Used spi_bpw_to_bytes() where applicable.

With best regards,
Marcelo

Marcelo Schmitt (4):
dt-bindings: iio: adc: Add ltc2378
iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
iio: adc: ltc2378: Enable high-speed data capture
iio: adc: ltc2378: Enable triggered buffer data capture

 .../bindings/iio/adc/adi,ltc2378.yaml         | 185 +++++
 MAINTAINERS                                   |   8 +
 drivers/iio/adc/Kconfig                       |  19 +
 drivers/iio/adc/Makefile                      |   1 +
 drivers/iio/adc/ltc2378.c                     | 778 ++++++++++++++++++
 5 files changed, 991 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
 create mode 100644 drivers/iio/adc/ltc2378.c

base-commit: cc746297b23e89bd5df9f91f3a0ca209e8991763

Signature · 1 line
2.53.0
author maintainer
#1 lore raw

PATCH v4 1/4 dt-bindings: iio: adc: Add ltc2378

Document how to describe LTC2378-20 and similar ADCs in device tree.

---
Change log v3 -> v4:
- Added PWM property.
- Documented LTC2338-18 specific refin-supply.
- Picked up Conor's review tag.

 .../bindings/iio/adc/adi,ltc2378.yaml         | 185 ++++++++++++++++++
 MAINTAINERS                                   |   7 +
 2 files changed, 192 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +185 -0
new file mode 100644
index 000000000000..9adc420fe142
@@ -0,0 +1,185 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ltc2378.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices LTC2378 and similar Analog to Digital Converters
+
+maintainers:
+  - Marcelo Schmitt <[email protected]>
+
+description: |
+  Analog Devices LTC2378 series of ADCs.
+  Specifications can be found at:
+    https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf
+    https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+  compatible:
+    oneOf:
+      # Single compatible string match.
+      - enum:
+          - adi,ltc2338-18
+          - adi,ltc2364-16
+          - adi,ltc2364-18
+          - adi,ltc2367-16
+          - adi,ltc2367-18
+          - adi,ltc2368-16
+          - adi,ltc2368-18
+          - adi,ltc2369-18
+          - adi,ltc2370-16
+          - adi,ltc2376-16
+          - adi,ltc2376-18
+          - adi,ltc2376-20
+          - adi,ltc2377-16
+          - adi,ltc2377-18
+          - adi,ltc2377-20
+          - adi,ltc2378-16
+          - adi,ltc2378-18
+          - adi,ltc2378-20
+          - adi,ltc2379-18
+          - adi,ltc2380-16
+
+      # Low sample rate fallback for 16-bit unipolar sensors.
+      - items:
+          - enum:
+              - adi,ltc2370-16 # 2 MSPS
+              - adi,ltc2368-16 # 1 MSPS
+              - adi,ltc2367-16 # 500 kSPS
+          - const: adi,ltc2364-16 # fallback (250 kSPS)
+
+      # Low sample rate fallback for 18-bit unipolar sensors.
+      - items:
+          - enum:
+              - adi,ltc2369-18 # 1.6 MSPS
+              - adi,ltc2368-18 # 1 MSPS
+              - adi,ltc2367-18 # 500 kSPS
+          - const: adi,ltc2364-18 # fallback (250 kSPS)
+
+      # Low sample rate fallback for 16-bit bipolar sensors.
+      - items:
+          - enum:
+              - adi,ltc2380-16 # 2 MSPS
+              - adi,ltc2378-16 # 1 MSPS
+              - adi,ltc2377-16 # 500 kSPS
+          - const: adi,ltc2376-16 # fallback (250 kSPS)
+
+      # Low sample rate fallback for 18-bit bipolar sensors.
+      - items:
+          - enum:
+              - adi,ltc2379-18 # 1.6 MSPS
+              - adi,ltc2338-18 # 1 MSPS
+              - adi,ltc2378-18 # 1 MSPS
+              - adi,ltc2377-18 # 500 kSPS
+          - const: adi,ltc2376-18 # fallback (250 kSPS)
+
+      # Low sample rate fallback for 20-bit bipolar sensors.
+      - items:
+          - enum:
+              - adi,ltc2378-20 # 1 MSPS
+              - adi,ltc2377-20 # 500 kSPS
+          - const: adi,ltc2376-20 # fallback (250 kSPS)
+
+  reg:
+    maxItems: 1
+
+  spi-max-frequency:
+    maximum: 100000000
+
+  vdd-supply:
+    description: A 2.5V supply that powers the chip (VDD).
+
+  ovdd-supply:
+    description:
+      A 1.71V to 5.25V supply that sets the logic level for digital interface.
+
+  ref-supply:
+    description:
+      Voltage reference input that determines the scale of ADC conversions.
+
+  refin-supply:
+    description:
+      Alternative voltage reference input.
+
+  cnv-gpios:
+    description:
+      When provided, this property indicates the GPIO that is connected to the
+      CNV pin.
+    maxItems: 1
+
+  pwms:
+    description: PWM signal connected to the CNV pin.
+    maxItems: 1
+
+  interrupts:
+    description:
+      Interrupt for signaling the completion of conversion results. The active
+      low signal provided on the BUSY pin asserts when ADC conversions finish.
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - vdd-supply
+  - ovdd-supply
+
+allOf:
+  # Except for LTC2338, all designs require a voltage reference input
+  - if:
+      properties:
+        compatible:
+          not:
+            contains:
+              enum:
+                - adi,ltc2338-18
+    then:
+      required:
+        - ref-supply
+      properties:
+        refin-supply: false
+    else:
+      properties:
+        ref-supply: false
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        adc@0 {
+            compatible = "adi,ltc2378-20", "adi,ltc2376-20";
+            reg = <0>;
+            spi-max-frequency = <71000000>;
+            vdd-supply = <&supply_2_5V>;
+            ovdd-supply = <&supply_3_3V>;
+            ref-supply = <&supply_5V>;
+            cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>;
+            interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
+            interrupt-parent = <&gpio>;
+        };
+    };
MAINTAINERS +7 -0
index b051eccafa60..205acb4b0789 100644
@@ -15220,6 +15220,13 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml
 F:	drivers/iio/dac/ltc1660.c
 
+LTC2378 IIO ADC DRIVER
+M:	Marcelo Schmitt <[email protected]>
+L:	[email protected]
+S:	Supported
+W:	https://ez.analog.com/linux-software-drivers
+F:	Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
+
 LTC2664 IIO DAC DRIVER
 M:	Michael Hennerich <[email protected]>
 M:	Kim Seer Paller <[email protected]>
Signature · 1 line
2.53.0
#2 lore raw

Re: [PATCH v4 1/4] dt-bindings: iio: adc: Add ltc2378

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The compatible string 'adi,ltc2338-18' incorrectly specifies 'adi,ltc2376-18' as a fallback…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The compatible string 'adi,ltc2338-18' incorrectly specifies 'adi,ltc2376-18' as a fallback despite conflicting hardware constraints for 'ref-supply', breaking backward compatibility. --

commit aa175408226610e4bc2c27ed8ee768b0307ee6fa
Author: Marcelo Schmitt <[email protected]>

dt-bindings: iio: adc: Add ltc2378

This commit documents the device tree bindings for the Analog Devices LTC2378 and similar series of ADCs. It specifies the compatible strings, required properties, and voltage reference constraints.

Quoted 5 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +0 -0
new file mode 100644
index 0000000000000..9adc420fe142b

[ ... ]

Quoted 8 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +8 -0
+      # Low sample rate fallback for 18-bit bipolar sensors.
+      - items:
+          - enum:
+              - adi,ltc2379-18 # 1.6 MSPS
+              - adi,ltc2338-18 # 1 MSPS
+              - adi,ltc2378-18 # 1 MSPS
+              - adi,ltc2377-18 # 500 kSPS
+          - const: adi,ltc2376-18 # fallback (250 kSPS)

[ ... ]

Quoted 7 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +7 -0
+allOf:
+  # Except for LTC2338, all designs require a voltage reference input
+  - if:
+      properties:
+        compatible:
+          not:
+            contains:

Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml line 163

Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +10 -0
+              enum:
+                - adi,ltc2338-18
+    then:
+      required:
+        - ref-supply
+      properties:
+        refin-supply: false
+    else:
+      properties:
+        ref-supply: false

[Severity: Medium]
Will this cause probe failures on older kernels when using the fallback?

The compatible string adi,ltc2338-18 is configured to use adi,ltc2376-18 as a fallback. However, this conditional block strictly forbids the ref-supply property for adi,ltc2338-18.

Because adi,ltc2376-18 requires a ref-supply, a device tree correctly following this binding for adi,ltc2338-18 will lack that property.

When an older kernel attempts to bind using the adi,ltc2376-18 fallback string, will it fail to find the expected ref-supply regulator and abort probing?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
author maintainer
#3 lore raw

PATCH v4 2/4 iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

In reply to Marcelo Schmitt

Support for LTC2378-20 and similar analog-to-digital converters.

---
Change log v3 -> v4:
- Reworked the driver to make IIO channels static according to v3 feedback.
- Updated to use default 8 bits_per_word for non-offloaded transfers.
- Handled refin voltage reference supply for LTC2338.
- Used spi_bpw_to_bytes() where applicable.

 MAINTAINERS               |   1 +
 drivers/iio/adc/Kconfig   |  12 ++
 drivers/iio/adc/Makefile  |   1 +
 drivers/iio/adc/ltc2378.c | 395 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 409 insertions(+)
 create mode 100644 drivers/iio/adc/ltc2378.c
MAINTAINERS +1 -0
index 205acb4b0789..db24dfa087d9 100644
@@ -15226,6 +15226,7 @@ L:	[email protected]
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
+F:	drivers/iio/adc/ltc2378.c
 
 LTC2664 IIO DAC DRIVER
 M:	Michael Hennerich <[email protected]>
drivers/iio/adc/Kconfig +12 -0
index 756a3b1137a7..07a8a5911a09 100644
@@ -941,6 +941,18 @@ config LTC2309
 	  This driver can also be built as a module. If so, the module will
 	  be called ltc2309.
 
+config LTC2378
+	tristate "Analog Devices LTC2378 ADC driver"
+	depends on SPI
+	depends on REGULATOR || COMPILE_TEST
+	depends on GPIOLIB
+	help
+	  Say yes here to build support for Analog Devices LTC2378-20 and
+	  similar analog to digital converters.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called ltc2378.
+
 config LTC2471
 	tristate "Linear Technology LTC2471 and LTC2473 ADC driver"
 	depends on I2C
drivers/iio/adc/Makefile +1 -0
index 707dd708912f..1814fb78dde3 100644
@@ -81,6 +81,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
 obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
 obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
 obj-$(CONFIG_LTC2309) += ltc2309.o
+obj-$(CONFIG_LTC2378) += ltc2378.o
 obj-$(CONFIG_LTC2471) += ltc2471.o
 obj-$(CONFIG_LTC2485) += ltc2485.o
 obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
drivers/iio/adc/ltc2378.c new file +395 -0

395 lines not on this page · raw message

Signature · 1 line
2.53.0
#4 lore raw

Re: [PATCH v4 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] The voltage from the 'refin' regulator is not multiplied by the internal buffer gain, resulting in…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The voltage from the 'refin' regulator is not multiplied by the internal buffer gain, resulting in incorrect ADC scale.
- [Medium] External 'ref' regulator is completely ignored for chips with an internal reference, breaking the ability to overdrive the internal reference. --

commit 06f09452b5298cd69acd94f1c33c2c91342a505d
Author: Marcelo Schmitt <[email protected]>

iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

This commit adds a new driver for the Analog Devices LTC2378-20 and similar analog-to-digital converters.

Quoted 5 lines
drivers/iio/adc/ltc2378.c new file +0 -0
new file mode 100644
index 0000000000000..9a9f32e4989ba

[ ... ]

drivers/iio/adc/ltc2378.c line 275

Quoted 9 lines
drivers/iio/adc/ltc2378.c new file +9 -0
+static int ltc2378_regulator_setup(struct device *dev, struct ltc2378_state *st)
+{
+	int ret;
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+	if (ret < 0 && ret != -ENODEV) {
+		return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+	} else if (ret > 0) {
+		st->ref_uV = ret;

[Severity: High]
Does this assignment account for the internal buffer's amplification gain?

For example, on chips like the LTC2338-18, the internal reference buffer amplifies the REFIN voltage by a factor of 2. If the 'refin' regulator provides 2.048V, the internal REF pin sees 4.096V. But this code appears to assign the direct REFIN voltage to st->ref_uV, which would result in an incorrect ADC scale calculation.

drivers/iio/adc/ltc2378.c line 281

Quoted 6 lines
drivers/iio/adc/ltc2378.c new file +6 -0
+		return 0;
+	}
+
+	if (st->info->internal_ref_uv) {
+		st->ref_uV = st->info->internal_ref_uv;
+		return 0;

[Severity: Medium]
Does this early return prevent a board from overdriving the internal reference with an external 'ref' supply?

If the hardware supports overdriving the internal reference and a 'ref-supply' is provided in the device tree, it looks like this code will silently ignore it and use the internal reference value instead.

Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10 -0
+	}
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to read ref regulator\n");
+
+	st->ref_uV = ret;
+
+	return 0;
+}

[ ... ]

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
reviewer
#5 lore raw

Re: [PATCH v4 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

In reply to Marcelo Schmitt
Quoted 1 line · Marcelo Schmitt

Support for LTC2378-20 and similar analog-to-digital converters.

This should mention that it contains some future prep for offloading. (Although would be a bit easier to review if we deferred adding the offload stuff).

Quoted 16 lines

---
Change log v3 -> v4:
- Reworked the driver to make IIO channels static according to v3 feedback.
- Updated to use default 8 bits_per_word for non-offloaded transfers.
- Handled refin voltage reference supply for LTC2338.
- Used spi_bpw_to_bytes() where applicable.

 MAINTAINERS               |   1 +
 drivers/iio/adc/Kconfig   |  12 ++
 drivers/iio/adc/Makefile  |   1 +
 drivers/iio/adc/ltc2378.c | 395 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 409 insertions(+)
 create mode 100644 drivers/iio/adc/ltc2378.c

...

Quoted 21 lines
drivers/iio/adc/ltc2378.c new file +15 -0
new file mode 100644
index 000000000000..9a9f32e4989b
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices LTC2378 ADC series driver
+ *
+ * Copyright (C) 2026 Analog Devices Inc.
+ * Author: Marcelo Schmitt <[email protected]>
+ */
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>

drivers/iio/adc/ltc2378.c line 25

Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10 -0
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#define __LTC2378_DIFF_CHANNEL(_sign, _real_bits, _storage_bits, _offl)\

Why the leading underscores? I don't see a conflicting name.

Quoted 12 lines
drivers/iio/adc/ltc2378.c new file +12 -0
+{										\
+	.type = IIO_VOLTAGE,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |				\
+			      BIT(IIO_CHAN_INFO_SCALE) |			\
+			      (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0),	\
+	.info_mask_separate_available = _offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0,\
+	.scan_index = 0,							\
+	.scan_type = {								\
+		.format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT :			\
+				  IIO_SCAN_FORMAT_UNSIGNED_INT,			\
+		.realbits = _real_bits,						\
+		.storagebits = _storage_bits,					\

drivers/iio/adc/ltc2378.c line 47

Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10 -0
+		.shift = (_offl ? 0 : _storage_bits - _real_bits),		\
+		.endianness = _offl ? IIO_CPU : IIO_BE				\
+	},									\
+}
+
+#define LTC2378_BIPOLAR_DIFF_CHANNEL(_real_bits)				\
+	__LTC2378_DIFF_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16), 0)
+
+#define LTC2378_UNIPOLAR_DIFF_CHANNEL(_real_bits)				\
+	__LTC2378_DIFF_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16), 0)

Why not move the (((_real_bits) > 16) ? 32 : 16) into the __LTC2378_DIFF_CHANNEL() macro to avoid repeating it?

drivers/iio/adc/ltc2378.c line 51

Quoted 4 lines
drivers/iio/adc/ltc2378.c new file +4 -0
+
+struct ltc2378_chip_info {
+	const char *name;
+	unsigned int internal_ref_uv;

_uV (to be consistent)

Quoted 3 lines
drivers/iio/adc/ltc2378.c new file +3 -0
+	struct iio_chan_spec chan;
+};
+

...

drivers/iio/adc/ltc2378.c line 274

Quoted 8 lines
drivers/iio/adc/ltc2378.c new file +8 -0
+static int ltc2378_regulator_setup(struct device *dev, struct ltc2378_state *st)
+{
+	int ret;
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+	if (ret < 0 && ret != -ENODEV) {
+		return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+	} else if (ret > 0) {

Else is not needed here.

drivers/iio/adc/ltc2378.c line 282

Quoted 8 lines
drivers/iio/adc/ltc2378.c new file +8 -0
+		st->ref_uV = ret;
+		return 0;
+	}
+
+	if (st->info->internal_ref_uv) {
+		st->ref_uV = st->info->internal_ref_uv;
+		return 0;
+	}

I would be tempted to have two separate functions here and only call one depending on the chip. Otherwise, it allows incorrect devicetree.

Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10 -0
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to read ref regulator\n");
+
+	st->ref_uV = ret;
+
+	return 0;
+}
+
#6 lore raw

Re: [PATCH v4 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

Quoted 17 lines
Quoted 17 lines
drivers/iio/adc/ltc2378.c new file +17 -0
+{										\
+	.type = IIO_VOLTAGE,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |				\
+			      BIT(IIO_CHAN_INFO_SCALE) |			\
+			      (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0),	\
+	.info_mask_separate_available = _offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0,\
+	.scan_index = 0,							\
+	.scan_type = {								\
+		.format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT :			\
+				  IIO_SCAN_FORMAT_UNSIGNED_INT,			\
+		.realbits = _real_bits,						\
+		.storagebits = _storage_bits,					\
+		.shift = (_offl ? 0 : _storage_bits - _real_bits),		\
+		.endianness = _offl ? IIO_CPU : IIO_BE				\
+	},									\
+}
+
Quoted 9 lines

drivers/iio/adc/ltc2378.c line 47

Quoted 5 lines
drivers/iio/adc/ltc2378.c new file +5 -0
+#define LTC2378_BIPOLAR_DIFF_CHANNEL(_real_bits)				\
+	__LTC2378_DIFF_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16), 0)
+
+#define LTC2378_UNIPOLAR_DIFF_CHANNEL(_real_bits)				\
+	__LTC2378_DIFF_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16), 0)

Why not move the (((_real_bits) > 16) ? 32 : 16) into the __LTC2378_DIFF_CHANNEL() macro to avoid repeating it?

Because that would go wrong for LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL() in patch 3.

Quoted 4 lines
Quoted 4 lines
drivers/iio/adc/ltc2378.c new file +4 -0
+
+struct ltc2378_chip_info {
+	const char *name;
+	unsigned int internal_ref_uv;

...

Quoted 11 lines

drivers/iio/adc/ltc2378.c line 274

Quoted 9 lines
drivers/iio/adc/ltc2378.c new file +8 -0
+static int ltc2378_regulator_setup(struct device *dev, struct ltc2378_state *st)
+{
+	int ret;
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+	if (ret < 0 && ret != -ENODEV) {
+		return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+	} else if (ret > 0) {

Else is not needed here.

Why not? Intendend flow/logic is to get and use refin for devices that have it (currently only one chip has). Except for that chip that has 'refin', other chips have only 'ref' and no internal reference. Also, the chip that has 'refin' doesn't have 'ref' so it uses an internal reference when 'refin' is not found. Anyways, the driver can use 'refin' if there is one available, no?

P.S. Will separate regulator logic according to chip type.

Quoted 12 lines

drivers/iio/adc/ltc2378.c line 282

Quoted 9 lines
drivers/iio/adc/ltc2378.c new file +8 -0
+		st->ref_uV = ret;
+		return 0;
+	}
+
+	if (st->info->internal_ref_uv) {
+		st->ref_uV = st->info->internal_ref_uv;
+		return 0;
+	}

I would be tempted to have two separate functions here and only call one depending on the chip. Otherwise, it allows incorrect devicetree.

Hmm, I see. Drat, I was hoping to avoid checking for specific chip parts to handle regulators. dtbs_check should error if a chip other than ltc2338 has 'refin'. Anyways, I see maintainers will probably prefer the chip specific init flow.

Quoted 11 lines

drivers/iio/adc/ltc2378.c line 292

Quoted 11 lines
drivers/iio/adc/ltc2378.c new file +10 -0
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "ref");
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "failed to read ref regulator\n");
+
+	st->ref_uV = ret;
+
+	return 0;
+}
+

Will adjust according to other comments as well.

Thanks,
Marcelo

reviewer
#7 lore raw

Re: [PATCH v4 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

It helps if you don't trim the "On ... wrote:" part of the reply so that we know who wrote what. :-)

Quoted 26 lines · Marcelo Schmitt
Quoted 17 lines
Quoted 17 lines
drivers/iio/adc/ltc2378.c new file +17 -0
+{										\
+	.type = IIO_VOLTAGE,							\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |				\
+			      BIT(IIO_CHAN_INFO_SCALE) |			\
+			      (_offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0),	\
+	.info_mask_separate_available = _offl ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0,\
+	.scan_index = 0,							\
+	.scan_type = {								\
+		.format = _sign ? IIO_SCAN_FORMAT_SIGNED_INT :			\
+				  IIO_SCAN_FORMAT_UNSIGNED_INT,			\
+		.realbits = _real_bits,						\
+		.storagebits = _storage_bits,					\
+		.shift = (_offl ? 0 : _storage_bits - _real_bits),		\
+		.endianness = _offl ? IIO_CPU : IIO_BE				\
+	},									\
+}
+
Quoted 9 lines

drivers/iio/adc/ltc2378.c line 47

Quoted 5 lines
drivers/iio/adc/ltc2378.c new file +5 -0
+#define LTC2378_BIPOLAR_DIFF_CHANNEL(_real_bits)				\
+	__LTC2378_DIFF_CHANNEL(1, _real_bits, (((_real_bits) > 16) ? 32 : 16), 0)
+
+#define LTC2378_UNIPOLAR_DIFF_CHANNEL(_real_bits)				\
+	__LTC2378_DIFF_CHANNEL(0, _real_bits, (((_real_bits) > 16) ? 32 : 16), 0)

Why not move the (((_real_bits) > 16) ? 32 : 16) into the __LTC2378_DIFF_CHANNEL() macro to avoid repeating it?

Quoted 2 lines

Because that would go wrong for LTC2378_OFFLOAD_BIPOLAR_DIFF_CHANNEL() in patch 3.

It could use _offl ? ... in the macro for that. Not a big deal to me though.

Quoted 16 lines
Quoted 4 lines
Quoted 4 lines
drivers/iio/adc/ltc2378.c new file +4 -0
+
+struct ltc2378_chip_info {
+	const char *name;
+	unsigned int internal_ref_uv;

...

Quoted 11 lines

drivers/iio/adc/ltc2378.c line 274

Quoted 9 lines
drivers/iio/adc/ltc2378.c new file +8 -0
+static int ltc2378_regulator_setup(struct device *dev, struct ltc2378_state *st)
+{
+	int ret;
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+	if (ret < 0 && ret != -ENODEV) {
+		return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+	} else if (ret > 0) {

Else is not needed here.

Quoted 1 line

Why not?

The if returns unconditionally, so else is not needed. This is just a general principal.

#8 lore raw

Re: [PATCH v4 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

Quoted 1 line · David Lechner

On 6/29/26 9:57 AM, Marcelo Schmitt wrote:

...

Quoted 1 line

It could use _offl ? ... in the macro for that. Not a big deal to me though.

Personally I am for a full spelling here.

...

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

drivers/iio/adc/ltc2378.c line 274

Quoted 8 lines
drivers/iio/adc/ltc2378.c new file +8 -0
+static int ltc2378_regulator_setup(struct device *dev, struct ltc2378_state *st)
+{
+	int ret;
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+	if (ret < 0 && ret != -ENODEV) {
+		return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+	} else if (ret > 0) {

Else is not needed here.

Quoted 4 lines
Quoted 1 line

Why not?

The if returns unconditionally, so else is not needed. This is just a general principal.

Unfortunately in this case ENODEV will go further, I don't see full context, but it may lead to something unexpected.

Personally I prefer the ladder of

	if (ret == -ENODEV)
		...
	else if (ret < 0)
		...
	else
		...
Signature · 2 lines
With Best Regards,
Andy Shevchenko
#9 lore raw

Re: [PATCH v4 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs

Quoted 18 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

drivers/iio/adc/ltc2378.c line 274

Quoted 8 lines
drivers/iio/adc/ltc2378.c new file +8 -0
+static int ltc2378_regulator_setup(struct device *dev, struct ltc2378_state *st)
+{
+	int ret;
+
+	ret = devm_regulator_get_enable_read_voltage(dev, "refin");
+	if (ret < 0 && ret != -ENODEV) {
+		return dev_err_probe(dev, ret, "failed to read refin regulator\n");
+	} else if (ret > 0) {

Else is not needed here.

Quoted 4 lines
Quoted 1 line

Why not?

The if returns unconditionally, so else is not needed. This is just a general principal.

Unfortunately in this case ENODEV will go further, I don't see full context, but it may lead to something unexpected.

Quoted 9 lines

Personally I prefer the ladder of

	if (ret == -ENODEV)
		...
	else if (ret < 0)
		...
	else
		...

After splitting regulator availability check on a chip specific basis it will probably simplify to something like

static int ltc2378_refin_setup(struct device *dev, struct ltc2378_state *st)

{
	int ret;

	ret = devm_regulator_get_enable_read_voltage(dev, "refin");
	if (ret == -ENODEV) /* refin is optional */
		st->ref_uV = st->info->internal_ref_uv * 2; /* ref buf amplifies REFIN by a factor of 2 */
	else if (ret < 0)
		return dev_err_probe(...);
	else
		st->ref_uV = ret * 2; /* ref buf amplifies REFIN by a factor of 2 */
	
	return 0;
}

For 'ref' only parts it will probably look like previous versions

static int ltc2378_ref_setup(struct device *dev, struct ltc2378_state *st)

{
	ret = devm_regulator_get_enable_read_voltage(dev, "ref"); /* ref is required */
	if (ret < 0)
		return dev_err_probe(dev, ret, "failed to read ref regulator\n");
	
	st->ref_uV = ret;

	return 0;
}

Calling the appropriate ref setup function for each part shall then (hopefully) error out incorrect device trees.

author maintainer
#10 lore raw

PATCH v4 3/4 iio: adc: ltc2378: Enable high-speed data capture

In reply to Marcelo Schmitt

Make use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 2 MSPS).

---
Change log v3 -> v4:
- Squashed offload support into main driver since selecting SPI_OFFLOAD and depending on PWM is acceptable.

 drivers/iio/adc/Kconfig   |   6 +
 drivers/iio/adc/ltc2378.c | 359 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 362 insertions(+), 3 deletions(-)
drivers/iio/adc/Kconfig +6 -0
index 07a8a5911a09..8d2fadbf74b1 100644
@@ -946,6 +946,12 @@ config LTC2378
 	depends on SPI
 	depends on REGULATOR || COMPILE_TEST
 	depends on GPIOLIB
+	depends on PWM
+	select IIO_BUFFER
+	select IIO_BUFFER_DMA
+	select IIO_BUFFER_DMAENGINE
+	select SPI_OFFLOAD
+	select SPI_OFFLOAD_TRIGGER_PWM
 	help
 	  Say yes here to build support for Analog Devices LTC2378-20 and
 	  similar analog to digital converters.
drivers/iio/adc/ltc2378.c +356 -3

520 lines not on this page · raw message

Signature · 1 line
2.53.0
#11 lore raw

Re: [PATCH v4 3/4] iio: adc: ltc2378: Enable high-speed data capture

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [High] Missing verification of SPI offload trigger offset after validation loop in…

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Missing verification of SPI offload trigger offset after validation loop in ltc2378_update_conversion_rate() allows silent data corruption.
- [Medium] In-out parameters accumulate drift across iterative rounding loops in ltc2378_update_conversion_rate().
- [Medium] Torn state on error path in ltc2378_update_conversion_rate() due to partially updated st->offload_trigger_config.
- [Low] Data race on st->cnv_Hz between sysfs readers and writers.
--

commit 57c4b9b3af36aafb3888623014ea397ad6cf0cd6
Author: Marcelo Schmitt <[email protected]>

iio: adc: ltc2378: Enable high-speed data capture

Make use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 2 MSPS).

Quoted 3 lines
drivers/iio/adc/ltc2378.c +0 -0

[ ... ]

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 349

Quoted 6 lines
drivers/iio/adc/ltc2378.c +2 -0
@@ -254,7 +345,126 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev,
 			*val2 = chan->scan_type.realbits;
 
 		return IIO_VAL_FRACTIONAL_LOG2;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = st->cnv_Hz;

[Severity: Low]
When reading the frequency via ltc2378_read_raw(), st->cnv_Hz is read without acquiring the direct mode lock. Since it can be concurrently modified by ltc2378_update_conversion_rate() during a write, does this cause a data race? Should this be protected or accessed via READ_ONCE()?

Quoted 5 lines
drivers/iio/adc/ltc2378.c +5 -0
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}

[ ... ]

Quoted 2 lines
drivers/iio/adc/ltc2378.c +2 -0
+static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz)
+{

[ ... ]

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 418

Quoted 11 lines
drivers/iio/adc/ltc2378.c +10 -0
+	/* Configure CNV PWM waveform */
+	cnv_wf.period_length_ns = DIV_ROUND_CLOSEST(NSEC_PER_SEC, freq_Hz);
 
+	/*
+	 * Ensure CNV high time meets minimum requirement (20ns). The PWM
+	 * hardware may round the duty cycle, so iterate until we get at least
+	 * the minimum required high time.
+	 */
+	do {
+		cnv_wf.duty_length_ns = target;
+		ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf);

[Severity: Medium]
The pwm_round_waveform_might_sleep() function can modify the passed cnv_wf structure in place. Since the base period (cnv_wf.period_length_ns) is set before the loop and not reset inside, does this cause the period to drift across the iterations if the hardware constraints cause it to be repeatedly rounded?

Quoted 15 lines
drivers/iio/adc/ltc2378.c +15 -0
+		if (ret)
+			return ret;
+		target += 10;  /* Increment by PWM duty cycle period */
+	} while (count++ < 100 && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS);
+
+	/* Double check the minimum CNV high time is met */
+	if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS)
+		return -EIO;
+
+	/*
+	 * Configure SPI offload PWM trigger.
+	 * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL.
+	 * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns)
+	 *
+	 * Use the same period as CNV PWM to avoid timing issues.

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 443

Quoted 10 lines
drivers/iio/adc/ltc2378.c +10 -0
+	 * Convert back from period to frequency for the SPI offload API.
+	 */
+	offload_period_ns = cnv_wf.period_length_ns;
+	config->periodic.frequency_hz = DIV_ROUND_UP(HZ_PER_GHZ, offload_period_ns);
+	min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS;
+	offload_offset_ns = min_read_offset;
+	count = 0;
+	do {
+		config->periodic.offset_ns = offload_offset_ns;
+		ret = spi_offload_trigger_validate(st->offload_trigger, config);

[Severity: Medium]
The config pointer aliases st->offload_trigger_config. If this validation fails, the function returns the error directly below without rolling back the st->offload_trigger_config state. Could this leave the device in an inconsistent state where the old st->cnv_wf configuration is paired with a newly modified but rejected SPI trigger configuration upon the next postenable?

Additionally, similar to the PWM loop above, spi_offload_trigger_validate() modifies config in place. Does config->periodic.frequency_hz drift across iterations since it is not reset inside the loop?

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 447

Quoted 4 lines
drivers/iio/adc/ltc2378.c +4 -0
+		if (ret)
+			return ret;
+		offload_offset_ns += 10;
+	} while (count++ < 100 && config->periodic.offset_ns < min_read_offset);

[Severity: High]
If the loop exhausts its 100 retries without meeting the minimum offset requirement, won't it silently proceed here without returning an error?

Unlike the preceding PWM loop which explicitly checks the bounds afterward, could this missing check cause silent data corruption if the SPI controller ends up reading while the ADC is still converting?

Quoted 6 lines
drivers/iio/adc/ltc2378.c +6 -0
+
+	st->cnv_wf = cnv_wf;
+	st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns);
+
+	return 0;
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#12 lore raw

Re: [PATCH v4 3/4] iio: adc: ltc2378: Enable high-speed data capture

In reply to Marcelo Schmitt
Quoted 2 lines · Marcelo Schmitt

Make use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 2 MSPS).

...

drivers/iio/adc/ltc2378.c line 16

Quoted 2 lines
drivers/iio/adc/ltc2378.c +2 -0
+#include <linux/math.h>
+#include <linux/math64.h>

The latter is superior, drop math.h

...

drivers/iio/adc/ltc2378.c line 73

Quoted 6 lines

struct ltc2378_chip_info {

drivers/iio/adc/ltc2378.c +2 -0
 	const char *name;
 	unsigned int internal_ref_uv;
 	struct iio_chan_spec chan;
+	struct iio_chan_spec offload_chan;
+	unsigned int max_sample_rate_hz;

Perhaps _Hz for the consistency. I dunno in this case, because it's quite widespread (in small letters).

Quoted 2 lines
drivers/iio/adc/ltc2378.c +1 -0
+	unsigned int tconv_ns;
 };

...

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 349

Quoted 1 line
drivers/iio/adc/ltc2378.c +1 -0
+		*val = st->cnv_Hz;

Ah, you already use that, so please do in the above.

...

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 405

Quoted 1 line
drivers/iio/adc/ltc2378.c +1 -0
+	if (freq_Hz < 1 || freq_Hz > st->info->max_sample_rate_hz)

Can we use in_range()?

Quoted 1 line
drivers/iio/adc/ltc2378.c +1 -0
+		return -ERANGE;

...

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 422

Quoted 7 lines
drivers/iio/adc/ltc2378.c +7 -0
+	do {
+		cnv_wf.duty_length_ns = target;
+		ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf);
+		if (ret)
+			return ret;
+		target += 10;  /* Increment by PWM duty cycle period */
+	} while (count++ < 100 && cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS);

Seems count is not used directly, can be while (--count ...) as usual pattern.

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 426

Quoted 3 lines
drivers/iio/adc/ltc2378.c +3 -0
+	/* Double check the minimum CNV high time is met */
+	if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS)
+		return -EIO;

Hmm... How is this IO issue?

Quoted 10 lines
drivers/iio/adc/ltc2378.c +10 -0
+	/*
+	 * Configure SPI offload PWM trigger.
+	 * The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL.
+	 * Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns)
+	 *
+	 * Use the same period as CNV PWM to avoid timing issues.
+	 * Convert back from period to frequency for the SPI offload API.
+	 */
+	offload_period_ns = cnv_wf.period_length_ns;
+	config->periodic.frequency_hz = DIV_ROUND_UP(HZ_PER_GHZ, offload_period_ns);

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 447

Quoted 10 lines
drivers/iio/adc/ltc2378.c +10 -0
+	min_read_offset = LTC2378_TBUSYLH_NS + st->info->tconv_ns + LTC2378_TDSDOBUSYL_NS;
+	offload_offset_ns = min_read_offset;
+	count = 0;
+	do {
+		config->periodic.offset_ns = offload_offset_ns;
+		ret = spi_offload_trigger_validate(st->offload_trigger, config);
+		if (ret)
+			return ret;
+		offload_offset_ns += 10;
+	} while (count++ < 100 && config->periodic.offset_ns < min_read_offset);

Same comment against count.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
#13 lore raw

Re: [PATCH v4 3/4] iio: adc: ltc2378: Enable high-speed data capture

Quoted 5 lines

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 426

Quoted 3 lines
drivers/iio/adc/ltc2378.c +3 -0
+	/* Double check the minimum CNV high time is met */
+	if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS)
+		return -EIO;

Hmm... How is this IO issue?

Okay, what about EDOM for PWM setup failures? Any error code suggestion otherwise? Will follow the other suggestions.

Thanks,
Marcelo

#14 lore raw

Re: [PATCH v4 3/4] iio: adc: ltc2378: Enable high-speed data capture

Quoted 8 lines · Marcelo Schmitt
Quoted 5 lines

drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 426

Quoted 3 lines
drivers/iio/adc/ltc2378.c +3 -0
+	/* Double check the minimum CNV high time is met */
+	if (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS)
+		return -EIO;

Hmm... How is this IO issue?

Okay, what about EDOM for PWM setup failures? Any error code suggestion otherwise? Will follow the other suggestions.

Seems -EDOM is in use in some cases of PWM. I haven't checked those cases, and if fits I have no objection.

"-EDOM if setting failed due to the exact waveform not being possible" (from PWM code comments).

Signature · 2 lines
With Best Regards,
Andy Shevchenko
author maintainer
#15 lore raw

PATCH v4 4/4 iio: adc: ltc2378: Enable triggered buffer data capture

In reply to Marcelo Schmitt

Enable users to run triggered data captures with LTC2378 and similar ADCs.

---
Change log v3 -> v4:
- Squashed triggered buffer support into main driver.

 drivers/iio/adc/Kconfig   |  1 +
 drivers/iio/adc/ltc2378.c | 76 +++++++++++++++++++++++++++------------
 2 files changed, 54 insertions(+), 23 deletions(-)
drivers/iio/adc/Kconfig +1 -0
index 8d2fadbf74b1..ac162425dc91 100644
@@ -950,6 +950,7 @@ config LTC2378
 	select IIO_BUFFER
 	select IIO_BUFFER_DMA
 	select IIO_BUFFER_DMAENGINE
+	select IIO_TRIGGERED_BUFFER
 	select SPI_OFFLOAD
 	select SPI_OFFLOAD_TRIGGER_PWM
 	help
drivers/iio/adc/ltc2378.c +53 -23

226 lines not on this page · raw message

Signature · 1 line
2.53.0
#16 lore raw

Re: [PATCH v4 4/4] iio: adc: ltc2378: Enable triggered buffer data capture

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Single reads via sysfs (in_voltage0_raw) return scrambled/garbage data when the SPI offload engine is configured, because they incorrectly use the offload channel's endianness and shift properties. --

commit 5a2926870e368170637e86d31e6346617e37a6b1
Author: Marcelo Schmitt <[email protected]>

iio: adc: ltc2378: Enable triggered buffer data capture

Enable users to run triggered data captures with LTC2378 and similar ADCs.

Quoted 4 lines
drivers/iio/adc/ltc2378.c +0 -0
index 5b28630003b18..fcccd27745490 100644

[ ... ]

Quoted 9 lines
drivers/iio/adc/ltc2378.c +3 -2
@@ -640,8 +662,16 @@ static int ltc2378_probe(struct spi_device *spi)
 	/* Fall back to low speed usage when no SPI offload is available. */
 	if (ret == -ENODEV) {
 		indio_dev->info = &ltc2378_iio_info;
-		indio_dev->channels = &st->info->chan;
-		indio_dev->num_channels = 1;
+		indio_dev->channels = st->info->chan;
+		indio_dev->num_channels = ARRAY_SIZE(st->info->chan);
+

drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 677

Quoted 10 lines
drivers/iio/adc/ltc2378.c +7 -0
+		ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+						      iio_pollfunc_store_time,
+						      ltc2378_trigger_handler,
+						      NULL);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to setup triggered buffer\n");
 	} else if (ret) {
 		return dev_err_probe(dev, ret, "failed to get offload\n");
 	} else {

[Severity: High]
This is a pre-existing issue, but do single reads via sysfs (in_voltage0_raw) return scrambled data when the SPI offload engine is configured?

When offload is configured, indio_dev->channels is assigned to the offload channel just after this else block in ltc2378_probe():

		indio_dev->channels = &st->info->offload_chan;

This channel specification is later passed to ltc2378_channel_single_read(). However, the single read function relies on the channel's endianness and shift properties to decode the raw SPI data:

static int ltc2378_channel_single_read(const struct iio_chan_spec *chan, ...)

{
	...
	if (chan->scan_type.endianness == IIO_BE) {
		if (chan->scan_type.realbits > 16)
			sample = be32_to_cpu(st->scan.data.sample_buf32_be);
		else
			sample = be16_to_cpu(st->scan.data.sample_buf16_be);
	} else {
		if (chan->scan_type.realbits > 16)
			sample = st->scan.data.sample_buf32;
		else
			sample = st->scan.data.sample_buf16;
	}

	sample >>= chan->scan_type.shift;
	...
}

Because the offload_chan specification defines data as CPU-endian with a 0 shift, the else branch is taken. This treats the natively big-endian, MSB-aligned SPI data as unshifted CPU-endian data, scrambling the values on little-endian architectures.

Could this be updated to properly handle manual sysfs reads when offload is enabled?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
#17 lore raw

Re: [PATCH v4 4/4] iio: adc: ltc2378: Enable triggered buffer data capture

In reply to Marcelo Schmitt
Quoted 1 line · Marcelo Schmitt

Enable users to run triggered data captures with LTC2378 and similar ADCs.

...

drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 674

Quoted 10 lines
drivers/iio/adc/ltc2378.c +10 -0
+		indio_dev->channels = st->info->chan;
+		indio_dev->num_channels = ARRAY_SIZE(st->info->chan);
+
+		ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+						      iio_pollfunc_store_time,
+						      ltc2378_trigger_handler,
+						      NULL);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to setup triggered buffer\n");

In 99.99% of error cases this will be -ENOMEM, so this is a dead code effectively. Please, drop the message, return ret is enough.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
reviewer Reviewed-by on the v4 series
#18 lore raw
In reply to Marcelo Schmitt
Quoted 5 lines · Marcelo Schmitt

This patch series adds support for LTC2378 and similar low noise, low power, high speed, successive approximation register (SAR) ADCs. These ADCs are similar among each other, varying mainly on the amount of precision bits, maximum sample rate, and input configuration (either fully differential or pseudo-differential).

I had a few minor comments and I suspect Sashiko found a few legitimate things. But this is looking really nice now.

maintainer
#19 lore raw
Quoted 6 lines · On Sat, 27 Jun 2026 16:39:32 -0500 David Lechner <[email protected]> wrote:
Quoted 5 lines · Marcelo Schmitt

This patch series adds support for LTC2378 and similar low noise, low power, high speed, successive approximation register (SAR) ADCs. These ADCs are similar among each other, varying mainly on the amount of precision bits, maximum sample rate, and input configuration (either fully differential or pseudo-differential).

Quoted 6 lines

I had a few minor comments and I suspect Sashiko found a few legitimate things. But this is looking really nice now.

FWIW I took a quick look as well and didn't have anything to add. Indeed do check Sashiko though. I didn't plough through figuring out if the comments are valid or not.
https://sashiko.dev/#/patchset/cover.1782397418.git.marcelo.schmitt%40analog.com