* [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378
2026-06-17 2:02 [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs Marcelo Schmitt
@ 2026-06-17 2:03 ` Marcelo Schmitt
2026-06-17 16:05 ` Conor Dooley
2026-06-17 22:04 ` David Lechner
2026-06-17 2:03 ` [PATCH v3 2/5] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs Marcelo Schmitt
` (4 subsequent siblings)
5 siblings, 2 replies; 17+ messages in thread
From: Marcelo Schmitt @ 2026-06-17 2:03 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel, marcelo.schmitt1
Document how to describe LTC2378-20 and similar ADCs in device tree.
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v2 -> v3:
- Re-added device tree fallback compatibles for LTC2378 chips, now with options
to provide a single compatible string or a pair of single compatible string
plus a fallback string to a slower sample rate spec in case a driver for the
specific part is not found.
.../bindings/iio/adc/adi,ltc2378.yaml | 160 ++++++++++++++++++
MAINTAINERS | 7 +
2 files changed, 167 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
new file mode 100644
index 000000000000..7d30a2cade8f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
@@ -0,0 +1,160 @@
+# 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 <marcelo.schmitt@analog.com>
+
+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:
+ A 2.5V to 5.1V supply for the reference input (REF).
+
+ cnv-gpios:
+ description:
+ When provided, this property indicates the GPIO that is 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
+ - ref-supply
+
+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>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index b051eccafa60..205acb4b0789 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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 <marcelo.schmitt@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,ltc2378.yaml
+
LTC2664 IIO DAC DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
M: Kim Seer Paller <kimseer.paller@analog.com>
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378
2026-06-17 2:03 ` [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378 Marcelo Schmitt
@ 2026-06-17 16:05 ` Conor Dooley
2026-06-17 17:14 ` Marcelo Schmitt
2026-06-17 22:04 ` David Lechner
1 sibling, 1 reply; 17+ messages in thread
From: Conor Dooley @ 2026-06-17 16:05 UTC (permalink / raw)
To: Marcelo Schmitt
Cc: linux-iio, devicetree, linux-kernel, jic23, nuno.sa,
Michael.Hennerich, dlechner, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
[-- Attachment #1: Type: text/plain, Size: 6691 bytes --]
On Tue, Jun 16, 2026 at 11:03:11PM -0300, Marcelo Schmitt wrote:
> Document how to describe LTC2378-20 and similar ADCs in device tree.
>
> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> ---
> Change log v2 -> v3:
> - Re-added device tree fallback compatibles for LTC2378 chips, now with options
> to provide a single compatible string or a pair of single compatible string
> plus a fallback string to a slower sample rate spec in case a driver for the
> specific part is not found.
>
> .../bindings/iio/adc/adi,ltc2378.yaml | 160 ++++++++++++++++++
> MAINTAINERS | 7 +
> 2 files changed, 167 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> new file mode 100644
> index 000000000000..7d30a2cade8f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> @@ -0,0 +1,160 @@
> +# 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 <marcelo.schmitt@analog.com>
> +
> +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)
Your driver still matches on ltc2370-16, which makes me question the
value of these fallbacks. That said, the chip info struct contains no
information about sampling rate. What actually is the impact of the
sample rate on the programming model?
Is there actually a benefit to matching on ltc2370-16, or can you just
match on the fallback?
+static const struct ltc2378_chip_info ltc2370_16_chip_info = {
+ .name = "ltc2370-16",
+ .resolution = 16,
+ .bipolar = false,
+};
+static const struct ltc2378_chip_info ltc2368_16_chip_info = {
+ .name = "ltc2368-16",
+ .resolution = 16,
+ .bipolar = false,
+};
+static const struct ltc2378_chip_info ltc2367_16_chip_info = {
+ .name = "ltc2367-16",
+ .resolution = 16,
+ .bipolar = false,
+};
+static const struct ltc2378_chip_info ltc2364_16_chip_info = {
+ .name = "ltc2364-16",
+ .resolution = 16,
+ .bipolar = false,
+};
All the devices have the same match data, other than the name, as the
fallback.
> +
> + # 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)
I didn't check these, but I assume they are the same.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378
2026-06-17 16:05 ` Conor Dooley
@ 2026-06-17 17:14 ` Marcelo Schmitt
2026-06-17 21:16 ` Conor Dooley
0 siblings, 1 reply; 17+ messages in thread
From: Marcelo Schmitt @ 2026-06-17 17:14 UTC (permalink / raw)
To: Conor Dooley
Cc: Marcelo Schmitt, linux-iio, devicetree, linux-kernel, jic23,
nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel
On 06/17, Conor Dooley wrote:
> On Tue, Jun 16, 2026 at 11:03:11PM -0300, Marcelo Schmitt wrote:
> > Document how to describe LTC2378-20 and similar ADCs in device tree.
> >
> > Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> > ---
> > Change log v2 -> v3:
> > - Re-added device tree fallback compatibles for LTC2378 chips, now with options
> > to provide a single compatible string or a pair of single compatible string
> > plus a fallback string to a slower sample rate spec in case a driver for the
> > specific part is not found.
> >
> > .../bindings/iio/adc/adi,ltc2378.yaml | 160 ++++++++++++++++++
> > MAINTAINERS | 7 +
> > 2 files changed, 167 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> > new file mode 100644
> > index 000000000000..7d30a2cade8f
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> > @@ -0,0 +1,160 @@
> > +# 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 <marcelo.schmitt@analog.com>
> > +
> > +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)
>
> Your driver still matches on ltc2370-16, which makes me question the
> value of these fallbacks. That said, the chip info struct contains no
> information about sampling rate.
The info about sample rate is added in patch 4.
> What actually is the impact of the sample rate on the programming model?
If the user tries to set a sample rate beyond the maximum supported, software
can throw an error to indicate that.
> Is there actually a benefit to matching on ltc2370-16, or can you just
> match on the fallback?
The benefit is telling software that it is safe to go up to 2 MSPS. It is also
okay to match on the fallback, but software may restrain operation to a slower
sample rate.
With the code being proposed in this patch set, there is no benefit in matching
the ltc2364-16 fallback if there is a faster ltc2370-16 device connected because
device driver supports that. Though, other operating systems and/or platforms
might not support all device variants and so the fallback might be useful.
> +static const struct ltc2378_chip_info ltc2370_16_chip_info = {
> + .name = "ltc2370-16",
> + .resolution = 16,
In a later patch ...
+ .max_sample_rate_hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
> + .bipolar = false,
> +};
>
> +static const struct ltc2378_chip_info ltc2368_16_chip_info = {
> + .name = "ltc2368-16",
> + .resolution = 16,
+ .max_sample_rate_hz = HZ_PER_MHZ,
+ .tconv_ns = 527
> + .bipolar = false,
> +};
>
> +static const struct ltc2378_chip_info ltc2367_16_chip_info = {
> + .name = "ltc2367-16",
> + .resolution = 16,
+ .max_sample_rate_hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
> + .bipolar = false,
> +};
>
> +static const struct ltc2378_chip_info ltc2364_16_chip_info = {
> + .name = "ltc2364-16",
> + .resolution = 16,
+ .max_sample_rate_hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
> + .bipolar = false,
> +};
>
...
> > + # 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)
>
> I didn't check these, but I assume they are the same.
Yes, except for small variations on input (unipolar/bipolar) and precision bits,
these devices are pretty much equal.
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378
2026-06-17 17:14 ` Marcelo Schmitt
@ 2026-06-17 21:16 ` Conor Dooley
0 siblings, 0 replies; 17+ messages in thread
From: Conor Dooley @ 2026-06-17 21:16 UTC (permalink / raw)
To: Marcelo Schmitt
Cc: Marcelo Schmitt, linux-iio, devicetree, linux-kernel, jic23,
nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel
[-- Attachment #1: Type: text/plain, Size: 8059 bytes --]
On Wed, Jun 17, 2026 at 02:14:32PM -0300, Marcelo Schmitt wrote:
> On 06/17, Conor Dooley wrote:
> > On Tue, Jun 16, 2026 at 11:03:11PM -0300, Marcelo Schmitt wrote:
> > > Document how to describe LTC2378-20 and similar ADCs in device tree.
> > >
> > > Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> > > ---
> > > Change log v2 -> v3:
> > > - Re-added device tree fallback compatibles for LTC2378 chips, now with options
> > > to provide a single compatible string or a pair of single compatible string
> > > plus a fallback string to a slower sample rate spec in case a driver for the
> > > specific part is not found.
> > >
> > > .../bindings/iio/adc/adi,ltc2378.yaml | 160 ++++++++++++++++++
> > > MAINTAINERS | 7 +
> > > 2 files changed, 167 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> > > new file mode 100644
> > > index 000000000000..7d30a2cade8f
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
> > > @@ -0,0 +1,160 @@
> > > +# 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 <marcelo.schmitt@analog.com>
> > > +
> > > +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)
> >
> > Your driver still matches on ltc2370-16, which makes me question the
> > value of these fallbacks. That said, the chip info struct contains no
> > information about sampling rate.
> The info about sample rate is added in patch 4.
Ah, I missed that.
> > What actually is the impact of the sample rate on the programming model?
> If the user tries to set a sample rate beyond the maximum supported, software
> can throw an error to indicate that.
I think you have misunderstood this. I was asking how the driver
interacts with the hardware. Do all devices come out of reset with the
minimum sampling rate? Or a per-device default rate? Do the faster
devices support all slower rates? If they do, do the registers have the
same meaning and the same value sets 2370-16 device to 1 MSPS as
2368-16.
> > Is there actually a benefit to matching on ltc2370-16, or can you just
> > match on the fallback?
> The benefit is telling software that it is safe to go up to 2 MSPS. It is also
> okay to match on the fallback, but software may restrain operation to a slower
> sample rate.
My whole review here is shit cos I didn't see patch 4, so the use of
fallbacks seems appropriate (provided your answers to my programming
model questions are sane!).
>
> With the code being proposed in this patch set, there is no benefit in matching
> the ltc2364-16 fallback if there is a faster ltc2370-16 device connected because
> device driver supports that. Though, other operating systems and/or platforms
> might not support all device variants and so the fallback might be useful.
>
> > +static const struct ltc2378_chip_info ltc2370_16_chip_info = {
> > + .name = "ltc2370-16",
> > + .resolution = 16,
> In a later patch ...
> + .max_sample_rate_hz = 2 * HZ_PER_MHZ,
> + .tconv_ns = 322,
> > + .bipolar = false,
> > +};
> >
> > +static const struct ltc2378_chip_info ltc2368_16_chip_info = {
> > + .name = "ltc2368-16",
> > + .resolution = 16,
> + .max_sample_rate_hz = HZ_PER_MHZ,
> + .tconv_ns = 527
> > + .bipolar = false,
> > +};
> >
> > +static const struct ltc2378_chip_info ltc2367_16_chip_info = {
> > + .name = "ltc2367-16",
> > + .resolution = 16,
> + .max_sample_rate_hz = 500 * HZ_PER_KHZ,
> + .tconv_ns = 1500,
> > + .bipolar = false,
> > +};
> >
> > +static const struct ltc2378_chip_info ltc2364_16_chip_info = {
> > + .name = "ltc2364-16",
> > + .resolution = 16,
> + .max_sample_rate_hz = 250 * HZ_PER_KHZ,
> + .tconv_ns = 3000,
> > + .bipolar = false,
> > +};
> >
> ...
> > > + # 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)
> >
> > I didn't check these, but I assume they are the same.
>
> Yes, except for small variations on input (unipolar/bipolar) and precision bits,
> these devices are pretty much equal.
What I meant here was that 2378-20, 2377-20 and 2376-20 would follow the
same pattern of having identical match data (pre patch 4).
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378
2026-06-17 2:03 ` [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378 Marcelo Schmitt
2026-06-17 16:05 ` Conor Dooley
@ 2026-06-17 22:04 ` David Lechner
1 sibling, 0 replies; 17+ messages in thread
From: David Lechner @ 2026-06-17 22:04 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
On 6/16/26 9:03 PM, Marcelo Schmitt wrote:
> Document how to describe LTC2378-20 and similar ADCs in device tree.
>
...
> + 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:
> + A 2.5V to 5.1V supply for the reference input (REF).
> +
> + cnv-gpios:
> + description:
> + When provided, this property indicates the GPIO that is connected to the
> + CNV pin.
> + maxItems: 1
Missing pwms property for when CNV pin is connected to PWM.
> +
> + 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
> + - ref-supply
I only looked at LTC2338-18, but it has a REFIN, which is optional.
Nothing named REF that is required.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 2/5] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
2026-06-17 2:02 [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs Marcelo Schmitt
2026-06-17 2:03 ` [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378 Marcelo Schmitt
@ 2026-06-17 2:03 ` Marcelo Schmitt
2026-06-17 22:18 ` David Lechner
2026-06-17 2:03 ` [RFC PATCH v3 3/5] iio: buffer: Extend DMAengine buffer interfaces to take extra sysfs attributes Marcelo Schmitt
` (3 subsequent siblings)
5 siblings, 1 reply; 17+ messages in thread
From: Marcelo Schmitt @ 2026-06-17 2:03 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel, marcelo.schmitt1
Support for LTC2378-20 and similar analog-to-digital converters.
Co-developed-by: Ioan-Daniel Pop <pop.ioan-daniel@analog.com>
Signed-off-by: Ioan-Daniel Pop <pop.ioan-daniel@analog.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v2 -> v3:
- Restricted LTC2378 dependency list to require GPIO
- Use datasheet terminology to indicate polarity/output code.
- Added missing block scope to IIO_DEV_ACQUIRE_DIRECT_MODE.
MAINTAINERS | 1 +
drivers/iio/adc/Kconfig | 11 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/ltc2378.c | 338 ++++++++++++++++++++++++++++++++++++++
drivers/iio/adc/ltc2378.h | 63 +++++++
5 files changed, 414 insertions(+)
create mode 100644 drivers/iio/adc/ltc2378.c
create mode 100644 drivers/iio/adc/ltc2378.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 205acb4b0789..a11c00a78c13 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15226,6 +15226,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
+F: drivers/iio/adc/ltc2378*
LTC2664 IIO DAC DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index a3a93a47b43d..2b8203451367 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -940,6 +940,17 @@ 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 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
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 707dd708912f..1814fb78dde3 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -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
diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
new file mode 100644
index 000000000000..88582bdcd6a6
--- /dev/null
+++ b/drivers/iio/adc/ltc2378.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices LTC2378 ADC series driver
+ *
+ * Copyright (C) 2026 Analog Devices Inc.
+ * Author: Ioan-Daniel Pop <pop.ioan-daniel@analog.com>
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+#include "ltc2378.h"
+
+static const struct ltc2378_chip_info ltc2338_18_chip_info = {
+ .name = "ltc2338-18",
+ .resolution = 18,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2364_16_chip_info = {
+ .name = "ltc2364-16",
+ .resolution = 16,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2364_18_chip_info = {
+ .name = "ltc2364-18",
+ .resolution = 18,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2367_16_chip_info = {
+ .name = "ltc2367-16",
+ .resolution = 16,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2367_18_chip_info = {
+ .name = "ltc2367-18",
+ .resolution = 18,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2368_16_chip_info = {
+ .name = "ltc2368-16",
+ .resolution = 16,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2368_18_chip_info = {
+ .name = "ltc2368-18",
+ .resolution = 18,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2369_18_chip_info = {
+ .name = "ltc2369-18",
+ .resolution = 18,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2370_16_chip_info = {
+ .name = "ltc2370-16",
+ .resolution = 16,
+ .bipolar = false,
+};
+
+static const struct ltc2378_chip_info ltc2376_16_chip_info = {
+ .name = "ltc2376-16",
+ .resolution = 16,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2376_18_chip_info = {
+ .name = "ltc2376-18",
+ .resolution = 18,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2376_20_chip_info = {
+ .name = "ltc2376-20",
+ .resolution = 20,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2377_16_chip_info = {
+ .name = "ltc2377-16",
+ .resolution = 16,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2377_18_chip_info = {
+ .name = "ltc2377-18",
+ .resolution = 18,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2377_20_chip_info = {
+ .name = "ltc2377-20",
+ .resolution = 20,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2378_16_chip_info = {
+ .name = "ltc2378-16",
+ .resolution = 16,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2378_18_chip_info = {
+ .name = "ltc2378-18",
+ .resolution = 18,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2378_20_chip_info = {
+ .name = "ltc2378-20",
+ .resolution = 20,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2379_18_chip_info = {
+ .name = "ltc2379-18",
+ .resolution = 18,
+ .bipolar = true,
+};
+
+static const struct ltc2378_chip_info ltc2380_16_chip_info = {
+ .name = "ltc2380-16",
+ .resolution = 16,
+ .bipolar = true,
+};
+
+static int ltc2378_channel_single_read(const struct iio_chan_spec *chan,
+ struct ltc2378_state *st, int *val)
+{
+ const struct iio_scan_type *scan_type = &chan->scan_type;
+ u32 sample;
+ int ret;
+
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret)
+ return ret;
+
+ if (scan_type->realbits > 16)
+ sample = st->scan.data.sample_buf32;
+ else
+ sample = st->scan.data.sample_buf16;
+
+ if (scan_type->format == IIO_SCAN_FORMAT_SIGNED_INT)
+ *val = sign_extend32(sample, scan_type->realbits - 1);
+ else
+ *val = sample;
+
+ return 0;
+}
+
+static int ltc2378_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long info)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW: {
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = ltc2378_channel_single_read(chan, st, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->ref_uV / MILLI;
+ /*
+ * For all LTC2378-like devices, the amount of bits that express
+ * voltage magnitude depend on the polarity / output code format:
+ * - straight binary: All precision/resolution bits are used.
+ * - 2's complement: One of the precision bits is used for sign.
+ */
+ if (st->info->bipolar)
+ *val2 = st->info->resolution - 1;
+ else
+ *val2 = st->info->resolution;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ltc2378_iio_info = {
+ .read_raw = <c2378_read_raw,
+};
+
+static int ltc2378_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct iio_dev *indio_dev;
+ struct ltc2378_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ 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;
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -EINVAL;
+
+ indio_dev->name = st->info->name;
+ indio_dev->info = <c2378_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW);
+ if (IS_ERR(st->cnv_gpio))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
+ "failed to get CNV GPIO");
+
+ st->num_iio_chans = 0;
+ st->chans[st->num_iio_chans++] = (struct iio_chan_spec) {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .format = st->info->bipolar ? IIO_SCAN_FORMAT_SIGNED_INT :
+ IIO_SCAN_FORMAT_UNSIGNED_INT,
+ .realbits = st->info->resolution,
+ /*
+ * Buffer elements could be 16-bit for low precision
+ * parts. Though, using more storage bits allows keeping
+ * the same scan_type configuration for both types of
+ * buffer support.
+ */
+ .storagebits = 32,
+ },
+ };
+
+ st->xfer.rx_buf = &st->scan.data;
+ st->xfer.len = st->info->resolution > 16 ? 4 : 2;
+ st->xfer.bits_per_word = st->info->resolution;
+
+ indio_dev->channels = st->chans;
+ indio_dev->num_channels = st->num_iio_chans;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct of_device_id ltc2378_of_match[] = {
+ { .compatible = "adi,ltc2338-18", .data = <c2338_18_chip_info },
+ { .compatible = "adi,ltc2364-16", .data = <c2364_16_chip_info },
+ { .compatible = "adi,ltc2364-18", .data = <c2364_18_chip_info },
+ { .compatible = "adi,ltc2367-16", .data = <c2367_16_chip_info },
+ { .compatible = "adi,ltc2367-18", .data = <c2367_18_chip_info },
+ { .compatible = "adi,ltc2368-16", .data = <c2368_16_chip_info },
+ { .compatible = "adi,ltc2368-18", .data = <c2368_18_chip_info },
+ { .compatible = "adi,ltc2369-18", .data = <c2369_18_chip_info },
+ { .compatible = "adi,ltc2370-16", .data = <c2370_16_chip_info },
+ { .compatible = "adi,ltc2376-16", .data = <c2376_16_chip_info },
+ { .compatible = "adi,ltc2376-18", .data = <c2376_18_chip_info },
+ { .compatible = "adi,ltc2376-20", .data = <c2376_20_chip_info },
+ { .compatible = "adi,ltc2377-16", .data = <c2377_16_chip_info },
+ { .compatible = "adi,ltc2377-18", .data = <c2377_18_chip_info },
+ { .compatible = "adi,ltc2377-20", .data = <c2377_20_chip_info },
+ { .compatible = "adi,ltc2378-16", .data = <c2378_16_chip_info },
+ { .compatible = "adi,ltc2378-18", .data = <c2378_18_chip_info },
+ { .compatible = "adi,ltc2378-20", .data = <c2378_20_chip_info },
+ { .compatible = "adi,ltc2379-18", .data = <c2379_18_chip_info },
+ { .compatible = "adi,ltc2380-16", .data = <c2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ltc2378_of_match);
+
+static const struct spi_device_id ltc2378_spi_id[] = {
+ { .name = "ltc2338-18", .driver_data = (kernel_ulong_t)<c2338_18_chip_info },
+ { .name = "ltc2364-16", .driver_data = (kernel_ulong_t)<c2364_16_chip_info },
+ { .name = "ltc2364-18", .driver_data = (kernel_ulong_t)<c2364_18_chip_info },
+ { .name = "ltc2367-16", .driver_data = (kernel_ulong_t)<c2367_16_chip_info },
+ { .name = "ltc2367-18", .driver_data = (kernel_ulong_t)<c2367_18_chip_info },
+ { .name = "ltc2368-16", .driver_data = (kernel_ulong_t)<c2368_16_chip_info },
+ { .name = "ltc2368-18", .driver_data = (kernel_ulong_t)<c2368_18_chip_info },
+ { .name = "ltc2369-18", .driver_data = (kernel_ulong_t)<c2369_18_chip_info },
+ { .name = "ltc2370-16", .driver_data = (kernel_ulong_t)<c2370_16_chip_info },
+ { .name = "ltc2376-16", .driver_data = (kernel_ulong_t)<c2376_16_chip_info },
+ { .name = "ltc2376-18", .driver_data = (kernel_ulong_t)<c2376_18_chip_info },
+ { .name = "ltc2376-20", .driver_data = (kernel_ulong_t)<c2376_20_chip_info },
+ { .name = "ltc2377-16", .driver_data = (kernel_ulong_t)<c2377_16_chip_info },
+ { .name = "ltc2377-18", .driver_data = (kernel_ulong_t)<c2377_18_chip_info },
+ { .name = "ltc2377-20", .driver_data = (kernel_ulong_t)<c2377_20_chip_info },
+ { .name = "ltc2378-16", .driver_data = (kernel_ulong_t)<c2378_16_chip_info },
+ { .name = "ltc2378-18", .driver_data = (kernel_ulong_t)<c2378_18_chip_info },
+ { .name = "ltc2378-20", .driver_data = (kernel_ulong_t)<c2378_20_chip_info },
+ { .name = "ltc2379-18", .driver_data = (kernel_ulong_t)<c2379_18_chip_info },
+ { .name = "ltc2380-16", .driver_data = (kernel_ulong_t)<c2380_16_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ltc2378_spi_id);
+
+static struct spi_driver ltc2378_driver = {
+ .driver = {
+ .name = "ltc2378",
+ .of_match_table = ltc2378_of_match
+ },
+ .probe = ltc2378_probe,
+ .id_table = ltc2378_spi_id,
+};
+module_spi_driver(ltc2378_driver);
+
+MODULE_AUTHOR("Ioan-Daniel Pop <pop.ioan-daniel@analog.com>");
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ltc2378.h b/drivers/iio/adc/ltc2378.h
new file mode 100644
index 000000000000..a3a69351de6c
--- /dev/null
+++ b/drivers/iio/adc/ltc2378.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Analog Devices LTC2378 and similar ADCs common definitions and properties
+ * Copyright (C) 2026 Analog Devices, Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#ifndef __DRIVERS_IIO_ADC_LTC2378_H__
+#define __DRIVERS_IIO_ADC_LTC2378_H__
+
+#include <linux/iio/iio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/units.h>
+
+#define LTC2378_TDSDOBUSYL_NS 5
+#define LTC2378_TBUSYLH_NS 13
+#define LTC2378_TCNV_HIGH_NS 20
+
+struct ltc2378_chip_info {
+ const char *name;
+ int resolution;
+ bool bipolar;
+};
+
+struct ltc2378_state {
+ const struct ltc2378_chip_info *info;
+ struct gpio_desc *cnv_gpio;
+ struct spi_device *spi;
+ struct spi_transfer xfer;
+ unsigned int num_iio_chans;
+ struct iio_chan_spec chans[2]; /* 1 physical chan + 1 timestamp chan */
+ int ref_uV;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ struct {
+ union {
+ u16 sample_buf16;
+ u32 sample_buf32;
+ } data;
+ aligned_s64 timestamp;
+ } scan __aligned(IIO_DMA_MINALIGN);
+};
+
+static inline int ltc2378_convert_and_acquire(struct ltc2378_state *st)
+{
+ int ret;
+
+ /* Cause a rising edge of CNV to initiate a new ADC conversion */
+ gpiod_set_value_cansleep(st->cnv_gpio, 1);
+ fsleep(4);
+ ret = spi_sync_transfer(st->spi, &st->xfer, 1);
+ gpiod_set_value_cansleep(st->cnv_gpio, 0);
+
+ return ret;
+}
+
+#endif /* __DRIVERS_IIO_ADC_LTC2378_H__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v3 2/5] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
2026-06-17 2:03 ` [PATCH v3 2/5] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs Marcelo Schmitt
@ 2026-06-17 22:18 ` David Lechner
0 siblings, 0 replies; 17+ messages in thread
From: David Lechner @ 2026-06-17 22:18 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
On 6/16/26 9:03 PM, Marcelo Schmitt wrote:
> Support for LTC2378-20 and similar analog-to-digital converters.
>
...
> +static int ltc2378_probe(struct spi_device *spi)
> +{
> + struct device *dev = &spi->dev;
> + struct iio_dev *indio_dev;
> + struct ltc2378_state *st;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + st = iio_priv(indio_dev);
> + st->spi = spi;
> +
> + 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;
add blank line here
> + st->info = spi_get_device_match_data(spi);
> + if (!st->info)
> + return -EINVAL;
> +
> + indio_dev->name = st->info->name;
> + indio_dev->info = <c2378_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + st->cnv_gpio = devm_gpiod_get(dev, "cnv", GPIOD_OUT_LOW);
> + if (IS_ERR(st->cnv_gpio))
> + return dev_err_probe(dev, PTR_ERR(st->cnv_gpio),
> + "failed to get CNV GPIO");
> +
> + st->num_iio_chans = 0;
> + st->chans[st->num_iio_chans++] = (struct iio_chan_spec) {
Why can't we make this static const (part of chip info) like we do in most
drivers?
> + .type = IIO_VOLTAGE,
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> + BIT(IIO_CHAN_INFO_SCALE),
> + .scan_index = 0,
> + .scan_type = {
> + .format = st->info->bipolar ? IIO_SCAN_FORMAT_SIGNED_INT :
> + IIO_SCAN_FORMAT_UNSIGNED_INT,
> + .realbits = st->info->resolution,
> + /*
> + * Buffer elements could be 16-bit for low precision
> + * parts. Though, using more storage bits allows keeping
> + * the same scan_type configuration for both types of
> + * buffer support.
> + */
Won't this make non-SPI offload buffered reads more complicated later?
I.e. have to shift the value or not before pushing to buffers depending
on CPU endianness.
> + .storagebits = 32,
> + },
> + };
> +
> + st->xfer.rx_buf = &st->scan.data;
> + st->xfer.len = st->info->resolution > 16 ? 4 : 2;
Can use spi_bpw_to_bytes() here.
> + st->xfer.bits_per_word = st->info->resolution;
> +
> + indio_dev->channels = st->chans;
> + indio_dev->num_channels = st->num_iio_chans;
> +
> + return devm_iio_device_register(&spi->dev, indio_dev);
> +}
> +
...
> diff --git a/drivers/iio/adc/ltc2378.h b/drivers/iio/adc/ltc2378.h
> new file mode 100644
> index 000000000000..a3a69351de6c
> --- /dev/null
> +++ b/drivers/iio/adc/ltc2378.h
> @@ -0,0 +1,63 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Analog Devices LTC2378 and similar ADCs common definitions and properties
> + * Copyright (C) 2026 Analog Devices, Inc.
> + * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
> + */
> +
> +#ifndef __DRIVERS_IIO_ADC_LTC2378_H__
> +#define __DRIVERS_IIO_ADC_LTC2378_H__
> +
> +#include <linux/iio/iio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/delay.h>
> +#include <linux/spi/spi.h>
> +#include <linux/types.h>
> +#include <linux/units.h>
> +
> +#define LTC2378_TDSDOBUSYL_NS 5
> +#define LTC2378_TBUSYLH_NS 13
> +#define LTC2378_TCNV_HIGH_NS 20
> +
> +struct ltc2378_chip_info {
> + const char *name;
> + int resolution;
> + bool bipolar;
> +};
> +
> +struct ltc2378_state {
> + const struct ltc2378_chip_info *info;
> + struct gpio_desc *cnv_gpio;
> + struct spi_device *spi;
> + struct spi_transfer xfer;
> + unsigned int num_iio_chans;
> + struct iio_chan_spec chans[2]; /* 1 physical chan + 1 timestamp chan */
> + int ref_uV;
> +
> + /*
> + * DMA (thus cache coherency maintenance) requires the
> + * transfer buffers to live in their own cache lines.
> + */
> + struct {
> + union {
> + u16 sample_buf16;
> + u32 sample_buf32;
> + } data;
> + aligned_s64 timestamp;
> + } scan __aligned(IIO_DMA_MINALIGN);
> +};
> +
> +static inline int ltc2378_convert_and_acquire(struct ltc2378_state *st)
> +{
> + int ret;
> +
> + /* Cause a rising edge of CNV to initiate a new ADC conversion */
> + gpiod_set_value_cansleep(st->cnv_gpio, 1);
> + fsleep(4);
> + ret = spi_sync_transfer(st->spi, &st->xfer, 1);
> + gpiod_set_value_cansleep(st->cnv_gpio, 0);
> +
> + return ret;
> +}
> +
> +#endif /* __DRIVERS_IIO_ADC_LTC2378_H__ */
Why do we need a header for this stuff? If there is a good reason
the commit message should explain it. Otherwise, it makes the driver
harder to read.
^ permalink raw reply [flat|nested] 17+ messages in thread
* [RFC PATCH v3 3/5] iio: buffer: Extend DMAengine buffer interfaces to take extra sysfs attributes
2026-06-17 2:02 [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs Marcelo Schmitt
2026-06-17 2:03 ` [PATCH v3 1/5] dt-bindings: iio: adc: Add ltc2378 Marcelo Schmitt
2026-06-17 2:03 ` [PATCH v3 2/5] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs Marcelo Schmitt
@ 2026-06-17 2:03 ` Marcelo Schmitt
2026-06-17 21:43 ` David Lechner
2026-06-17 2:04 ` [PATCH v3 4/5] iio: adc: ltc2378: Enable high-speed data capture Marcelo Schmitt
` (2 subsequent siblings)
5 siblings, 1 reply; 17+ messages in thread
From: Marcelo Schmitt @ 2026-06-17 2:03 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel, marcelo.schmitt1
Some devices using DMAengine buffers are connected to extra hardware that
allows setting how fast data is transferred to/from the buffer. However,
those extra pieces of harwdware are external to the sensor chip such that
supporting the transfer speed as a sensor property is a bit of an
inaccuracy. Expand IIO DMAengine buffer interfaces to take arguments for
extra sysfs attributes, enabling the transfer speed to be configured
through the buffer interface.
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
New patch.
Now that I've come to this buffer "solution", I have pretty much convinced
myself it would be better to instead have some sort of IIO trigger to control
the signal source connected to SPI offloading trigger module.
drivers/iio/adc/ad4000.c | 3 ++-
drivers/iio/adc/ad4030.c | 3 ++-
drivers/iio/adc/ad4691.c | 3 ++-
drivers/iio/adc/ad4695.c | 2 +-
drivers/iio/adc/ad7380.c | 2 +-
drivers/iio/adc/ad7606_spi.c | 2 +-
drivers/iio/adc/ad7768-1.c | 3 ++-
drivers/iio/adc/ad7944.c | 2 +-
drivers/iio/adc/ad_sigma_delta.c | 2 +-
.../buffer/industrialio-buffer-dmaengine.c | 19 ++++++++++++-------
drivers/iio/dac/ad5791.c | 2 +-
drivers/iio/dac/ad8460.c | 2 +-
drivers/iio/dac/adi-axi-dac.c | 2 +-
include/linux/iio/buffer-dmaengine.h | 16 ++++++++++------
14 files changed, 38 insertions(+), 25 deletions(-)
diff --git a/drivers/iio/adc/ad4000.c b/drivers/iio/adc/ad4000.c
index fd3d79fca785..b79b627310af 100644
--- a/drivers/iio/adc/ad4000.c
+++ b/drivers/iio/adc/ad4000.c
@@ -871,7 +871,8 @@ static int ad4000_spi_offload_setup(struct iio_dev *indio_dev,
"Failed to get offload RX DMA\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
- IIO_BUFFER_DIRECTION_IN);
+ IIO_BUFFER_DIRECTION_IN,
+ NULL);
if (ret)
return dev_err_probe(dev, ret, "Failed to setup DMA buffer\n");
diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
index 9c5f19321e3b..713fe95176c0 100644
--- a/drivers/iio/adc/ad4030.c
+++ b/drivers/iio/adc/ad4030.c
@@ -1400,7 +1400,8 @@ static int ad4030_spi_offload_setup(struct iio_dev *indio_dev,
"failed to get offload RX DMA\n");
return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
- IIO_BUFFER_DIRECTION_IN);
+ IIO_BUFFER_DIRECTION_IN,
+ NULL);
}
static int ad4030_setup_pga(struct device *dev, struct iio_dev *indio_dev,
diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
index 548678adc2a4..9845fe9c4725 100644
--- a/drivers/iio/adc/ad4691.c
+++ b/drivers/iio/adc/ad4691.c
@@ -1980,7 +1980,8 @@ static int ad4691_setup_offload(struct iio_dev *indio_dev,
indio_dev->setup_ops = &ad4691_cnv_burst_offload_buffer_setup_ops;
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
- IIO_BUFFER_DIRECTION_IN);
+ IIO_BUFFER_DIRECTION_IN,
+ NULL);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c
index 53642de7330d..78faeade19f5 100644
--- a/drivers/iio/adc/ad4695.c
+++ b/drivers/iio/adc/ad4695.c
@@ -1779,7 +1779,7 @@ static int ad4695_probe_spi_offload(struct iio_dev *indio_dev,
}
return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
- rx_dma, IIO_BUFFER_DIRECTION_IN);
+ rx_dma, IIO_BUFFER_DIRECTION_IN, NULL);
}
static const struct spi_offload_config ad4695_spi_offload_config = {
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index 9f77990a03f9..415b8e1be920 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -1874,7 +1874,7 @@ static int ad7380_probe_spi_offload(struct iio_dev *indio_dev,
"failed to get offload RX DMA\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
- rx_dma, IIO_BUFFER_DIRECTION_IN);
+ rx_dma, IIO_BUFFER_DIRECTION_IN, NULL);
if (ret)
return dev_err_probe(dev, ret, "cannot setup dma buffer\n");
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index 7e17ccbcedd0..760b54bfe208 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -317,7 +317,7 @@ static int ad7606_spi_offload_probe(struct device *dev,
"failed to get offload RX DMA\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
- rx_dma, IIO_BUFFER_DIRECTION_IN);
+ rx_dma, IIO_BUFFER_DIRECTION_IN, NULL);
if (ret)
return dev_err_probe(dev, ret,
"failed to setup offload RX DMA\n");
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 598936e47fd2..a13ac31281d8 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -1792,7 +1792,8 @@ static int ad7768_spi_offload_probe(struct iio_dev *indio_dev,
return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
- IIO_BUFFER_DIRECTION_IN);
+ IIO_BUFFER_DIRECTION_IN,
+ NULL);
if (ret)
return dev_err_probe(dev, ret, "failed to setup offload RX DMA\n");
diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c
index 7722cf9e8214..63852a6c2acc 100644
--- a/drivers/iio/adc/ad7944.c
+++ b/drivers/iio/adc/ad7944.c
@@ -843,7 +843,7 @@ static int ad7944_probe(struct spi_device *spi)
*/
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev,
- indio_dev, rx_dma, IIO_BUFFER_DIRECTION_IN);
+ indio_dev, rx_dma, IIO_BUFFER_DIRECTION_IN, NULL);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index a955556f9ec8..4b88e1c72c87 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -820,7 +820,7 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi
"Failed to get RX DMA channel\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
- rx_dma, IIO_BUFFER_DIRECTION_IN);
+ rx_dma, IIO_BUFFER_DIRECTION_IN, NULL);
if (ret)
return dev_err_probe(dev, ret, "Cannot setup DMA buffer\n");
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 98acce909854..343dbb6d1446 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -289,7 +289,8 @@ EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_teardown, "IIO_DMAENGINE_BUFFER");
static struct iio_buffer
*__iio_dmaengine_buffer_setup_ext(struct iio_dev *indio_dev,
struct dma_chan *chan,
- enum iio_buffer_direction dir)
+ enum iio_buffer_direction dir,
+ const struct iio_dev_attr **buffer_attrs)
{
struct iio_buffer *buffer;
int ret;
@@ -301,6 +302,7 @@ static struct iio_buffer
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
buffer->direction = dir;
+ buffer->attrs = buffer_attrs;
ret = iio_device_attach_buffer(indio_dev, buffer);
if (ret) {
@@ -329,7 +331,8 @@ static struct iio_buffer
struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
struct iio_dev *indio_dev,
const char *channel,
- enum iio_buffer_direction dir)
+ enum iio_buffer_direction dir,
+ const struct iio_dev_attr **buffer_attrs)
{
struct dma_chan *chan;
struct iio_buffer *buffer;
@@ -338,7 +341,7 @@ struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
if (IS_ERR(chan))
return ERR_CAST(chan);
- buffer = __iio_dmaengine_buffer_setup_ext(indio_dev, chan, dir);
+ buffer = __iio_dmaengine_buffer_setup_ext(indio_dev, chan, dir, buffer_attrs);
if (IS_ERR(buffer))
dma_release_channel(chan);
@@ -366,11 +369,12 @@ static void devm_iio_dmaengine_buffer_teardown(void *buffer)
int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
struct iio_dev *indio_dev,
const char *channel,
- enum iio_buffer_direction dir)
+ enum iio_buffer_direction dir,
+ const struct iio_dev_attr **buffer_attrs)
{
struct iio_buffer *buffer;
- buffer = iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, dir);
+ buffer = iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, dir, buffer_attrs);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
@@ -403,11 +407,12 @@ static void devm_iio_dmaengine_buffer_free(void *buffer)
int devm_iio_dmaengine_buffer_setup_with_handle(struct device *dev,
struct iio_dev *indio_dev,
struct dma_chan *chan,
- enum iio_buffer_direction dir)
+ enum iio_buffer_direction dir,
+ const struct iio_dev_attr **buffer_attrs)
{
struct iio_buffer *buffer;
- buffer = __iio_dmaengine_buffer_setup_ext(indio_dev, chan, dir);
+ buffer = __iio_dmaengine_buffer_setup_ext(indio_dev, chan, dir, buffer_attrs);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c
index ae7297f08398..024607adb915 100644
--- a/drivers/iio/dac/ad5791.c
+++ b/drivers/iio/dac/ad5791.c
@@ -448,7 +448,7 @@ static int ad5791_offload_setup(struct iio_dev *indio_dev)
"failed to get offload TX DMA\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(&spi->dev,
- indio_dev, tx_dma, IIO_BUFFER_DIRECTION_OUT);
+ indio_dev, tx_dma, IIO_BUFFER_DIRECTION_OUT, NULL);
if (ret)
return ret;
diff --git a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c
index 6e45686902dd..3c5fd71d5add 100644
--- a/drivers/iio/dac/ad8460.c
+++ b/drivers/iio/dac/ad8460.c
@@ -921,7 +921,7 @@ static int ad8460_probe(struct spi_device *spi)
indio_dev->setup_ops = &ad8460_buffer_setup_ops;
ret = devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, "tx",
- IIO_BUFFER_DIRECTION_OUT);
+ IIO_BUFFER_DIRECTION_OUT, NULL);
if (ret)
return dev_err_probe(dev, ret,
"Failed to get DMA buffer\n");
diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c
index 451fad34e7ee..23028a92ceba 100644
--- a/drivers/iio/dac/adi-axi-dac.c
+++ b/drivers/iio/dac/adi-axi-dac.c
@@ -162,7 +162,7 @@ static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
dma_name = "tx";
return iio_dmaengine_buffer_setup_ext(st->dev, indio_dev, dma_name,
- IIO_BUFFER_DIRECTION_OUT);
+ IIO_BUFFER_DIRECTION_OUT, NULL);
}
static void axi_dac_free_buffer(struct iio_backend *back,
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
index 37f27545f69f..bb7a348db72d 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -10,6 +10,7 @@
#include <linux/iio/buffer.h>
struct iio_dev;
+struct iio_dev_attr;
struct device;
struct dma_chan;
@@ -17,23 +18,26 @@ void iio_dmaengine_buffer_teardown(struct iio_buffer *buffer);
struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
struct iio_dev *indio_dev,
const char *channel,
- enum iio_buffer_direction dir);
+ enum iio_buffer_direction dir,
+ const struct iio_dev_attr **buffer_attrs);
#define iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
- iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
- IIO_BUFFER_DIRECTION_IN)
+ iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
+ IIO_BUFFER_DIRECTION_IN, NULL)
int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
struct iio_dev *indio_dev,
const char *channel,
- enum iio_buffer_direction dir);
+ enum iio_buffer_direction dir,
+ const struct iio_dev_attr **buffer_attrs);
int devm_iio_dmaengine_buffer_setup_with_handle(struct device *dev,
struct iio_dev *indio_dev,
struct dma_chan *chan,
- enum iio_buffer_direction dir);
+ enum iio_buffer_direction dir,
+ const struct iio_dev_attr **buffer_attrs);
#define devm_iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
- IIO_BUFFER_DIRECTION_IN)
+ IIO_BUFFER_DIRECTION_IN, NULL)
#endif
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [RFC PATCH v3 3/5] iio: buffer: Extend DMAengine buffer interfaces to take extra sysfs attributes
2026-06-17 2:03 ` [RFC PATCH v3 3/5] iio: buffer: Extend DMAengine buffer interfaces to take extra sysfs attributes Marcelo Schmitt
@ 2026-06-17 21:43 ` David Lechner
2026-06-18 10:38 ` Nuno Sá
0 siblings, 1 reply; 17+ messages in thread
From: David Lechner @ 2026-06-17 21:43 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
On 6/16/26 9:03 PM, Marcelo Schmitt wrote:
> Some devices using DMAengine buffers are connected to extra hardware that
> allows setting how fast data is transferred to/from the buffer. However,
> those extra pieces of harwdware are external to the sensor chip such that
> supporting the transfer speed as a sensor property is a bit of an
> inaccuracy. Expand IIO DMAengine buffer interfaces to take arguments for
> extra sysfs attributes, enabling the transfer speed to be configured
> through the buffer interface.
This message is a bit confusing. It sounds like it is attempting to
control something about the DMA controller itself. But based on the
later patches, it looks like this is just so we can add arbitrary
sysfs attributes to the bufferX directory. And in this specific case,
a sampling_frequency attribute.
>
> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> ---
> New patch.
>
> Now that I've come to this buffer "solution", I have pretty much convinced
> myself it would be better to instead have some sort of IIO trigger to control
> the signal source connected to SPI offloading trigger module.
>
In the other chips with SPI offload we've done already, we just used
IIO_CHAN_INFO_SAMP_FREQ to control the SPI offload trigger rate.
Any reason why we can't do that here? In the original SPI offload
discussions, IIRC the general consensus was that adding a trigger
just to control that was overkill when I suggested the same.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC PATCH v3 3/5] iio: buffer: Extend DMAengine buffer interfaces to take extra sysfs attributes
2026-06-17 21:43 ` David Lechner
@ 2026-06-18 10:38 ` Nuno Sá
0 siblings, 0 replies; 17+ messages in thread
From: Nuno Sá @ 2026-06-18 10:38 UTC (permalink / raw)
To: David Lechner
Cc: Marcelo Schmitt, linux-iio, devicetree, linux-kernel, jic23,
nuno.sa, Michael.Hennerich, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
On Wed, Jun 17, 2026 at 04:43:18PM -0500, David Lechner wrote:
> On 6/16/26 9:03 PM, Marcelo Schmitt wrote:
> > Some devices using DMAengine buffers are connected to extra hardware that
> > allows setting how fast data is transferred to/from the buffer. However,
> > those extra pieces of harwdware are external to the sensor chip such that
> > supporting the transfer speed as a sensor property is a bit of an
> > inaccuracy. Expand IIO DMAengine buffer interfaces to take arguments for
> > extra sysfs attributes, enabling the transfer speed to be configured
> > through the buffer interface.
>
> This message is a bit confusing. It sounds like it is attempting to
> control something about the DMA controller itself. But based on the
> later patches, it looks like this is just so we can add arbitrary
> sysfs attributes to the bufferX directory. And in this specific case,
> a sampling_frequency attribute.
Agreed. Seems like rate control comes from the buffer.
>
> >
> > Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> > ---
> > New patch.
> >
> > Now that I've come to this buffer "solution", I have pretty much convinced
> > myself it would be better to instead have some sort of IIO trigger to control
> > the signal source connected to SPI offloading trigger module.
> >
> In the other chips with SPI offload we've done already, we just used
> IIO_CHAN_INFO_SAMP_FREQ to control the SPI offload trigger rate.
> Any reason why we can't do that here? In the original SPI offload
> discussions, IIRC the general consensus was that adding a trigger
> just to control that was overkill when I suggested the same.
>
I tend to agree with David. Even if we come to a conclusion that we
can't use IIO_CHAN_INFO_SAMP_FREQ to control the trigger rate, I'm not
really convinced sysfs interfaces on the buffer itself are the place for
this.
- Nuno Sá
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 4/5] iio: adc: ltc2378: Enable high-speed data capture
2026-06-17 2:02 [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs Marcelo Schmitt
` (2 preceding siblings ...)
2026-06-17 2:03 ` [RFC PATCH v3 3/5] iio: buffer: Extend DMAengine buffer interfaces to take extra sysfs attributes Marcelo Schmitt
@ 2026-06-17 2:04 ` Marcelo Schmitt
2026-06-17 16:26 ` Julian Braha
2026-06-17 22:33 ` David Lechner
2026-06-17 2:04 ` [PATCH v3 5/5] iio: adc: ltc2378: Enable triggered buffer " Marcelo Schmitt
2026-06-17 22:46 ` [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs David Lechner
5 siblings, 2 replies; 17+ messages in thread
From: Marcelo Schmitt @ 2026-06-17 2:04 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel, marcelo.schmitt1
Make use of SPI transfer offloading to speed up data capture, enabling data
acquisition at faster sample rates (up to 2 MSPS).
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v2 -> v3:
- Fixed the evaluation loop conditions for CNV PWM and SPI Engine trigger PWM,
avoiding potential infinite loop if and CPU stall.
- Added comment to about initial PWM disable.
- Adjusted SPI offload setup initialization to not print error on a valid condition.
- Fully initialize IIO channel scan_type.
- Reworked to make offload support not imply all dependencies to be built in.
- Made sampling_frequency a buffer attribute.
- Made offload support not require DMA and other features to be built in.
- Now using same scan_type configuration for all use cases.
drivers/iio/adc/Kconfig | 19 ++
drivers/iio/adc/Makefile | 6 +
drivers/iio/adc/ltc2378-lib-core.c | 35 +++
drivers/iio/adc/ltc2378-offload-buffer.c | 305 +++++++++++++++++++++++
drivers/iio/adc/ltc2378.c | 46 ++++
drivers/iio/adc/ltc2378.h | 42 ++++
6 files changed, 453 insertions(+)
create mode 100644 drivers/iio/adc/ltc2378-lib-core.c
create mode 100644 drivers/iio/adc/ltc2378-offload-buffer.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 2b8203451367..f96d9262b891 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -944,6 +944,9 @@ config LTC2378
tristate "Analog Devices LTC2378 ADC driver"
depends on SPI
depends on GPIOLIB
+ select LTC2378_LIB
+ select LTC2378_LIB_OFFLOAD_BUFFER if SPI_OFFLOAD && PWM && SPI_OFFLOAD_TRIGGER_PWM && IIO_BUFFER && IIO_BUFFER_DMAENGINE
+ select LTC2378_LIB_TRIGGERED_BUFFER if IIO_BUFFER
help
Say yes here to build support for Analog Devices LTC2378-20 and
similar analog to digital converters.
@@ -2027,3 +2030,19 @@ config XILINX_AMS
xilinx-ams.
endmenu
+
+config LTC2378_LIB
+ tristate
+ help
+ Say yes here to build support for buffered data capture with LTC2378
+
+config LTC2378_LIB_OFFLOAD_BUFFER
+ bool
+ help
+ Say yes here to build support for high speed data capture with LTC2378
+
+config LTC2378_LIB_TRIGGERED_BUFFER
+ bool
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for buffered data capture with LTC2378
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 1814fb78dde3..109cd39237c9 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -82,6 +82,12 @@ obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
obj-$(CONFIG_LTC2309) += ltc2309.o
obj-$(CONFIG_LTC2378) += ltc2378.o
+
+ltc2378_lib-y += ltc2378-lib-core.o
+ltc2378_lib-$(CONFIG_LTC2378_LIB_OFFLOAD_BUFFER) += ltc2378-offload-buffer.o
+ltc2378_lib-$(CONFIG_LTC2378_LIB_TRIGGERED_BUFFER) += ltc2378-triggered-buffer.o
+obj-$(CONFIG_LTC2378_LIB) += ltc2378_lib.o
+
obj-$(CONFIG_LTC2471) += ltc2471.o
obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
diff --git a/drivers/iio/adc/ltc2378-lib-core.c b/drivers/iio/adc/ltc2378-lib-core.c
new file mode 100644
index 000000000000..1160f4324d01
--- /dev/null
+++ b/drivers/iio/adc/ltc2378-lib-core.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Analog Devices LTC2378 ADC series driver
+ *
+ * Copyright (C) 2026 Analog Devices Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+
+#include "ltc2378.h"
+
+int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ int ret;
+
+ ret = __ltc2378_set_offload_ops(st);
+ if (ret == -EOPNOTSUPP)
+ return 0; /* Let device setup complete without buffer support */
+
+ if (!ret)
+ ret = st->ops->buffer_setup(indio_dev, st);
+
+ if (ret)
+ return dev_err_probe(dev, ret, "error on SPI offload setup\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ltc2378_lib_buffer_setup, "IIO_LTC2378");
+
+MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
+MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ltc2378-offload-buffer.c b/drivers/iio/adc/ltc2378-offload-buffer.c
new file mode 100644
index 000000000000..3e8c7ab64a54
--- /dev/null
+++ b/drivers/iio/adc/ltc2378-offload-buffer.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Analog Devices, Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/err.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/pwm.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/offload/consumer.h>
+#include <linux/spi/offload/types.h>
+#include <linux/time64.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/buffer-dmaengine.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/types.h>
+
+#include "ltc2378.h"
+
+/*
+ * SPI offload wiring schema
+ *
+ * +-------------+ +-------------+
+ * | CNV |<-----+--| GPIO |
+ * | | +--| PWM0 |
+ * | | | |
+ * | | +--| PWM1 |
+ * | | | +-------------+
+ * | | +->| TRIGGER |
+ * | | | |
+ * | ADC | | SPI |
+ * | | | controller |
+ * | | | |
+ * | SDI |<--------| SDO |
+ * | SDO |-------->| SDI |
+ * | SCLK |<--------| SCLK |
+ * +-------------+ +-------------+
+ *
+ */
+static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz)
+{
+ struct spi_offload_trigger_config *config = &st->offload_trigger_config;
+ unsigned int min_read_offset, offload_period_ns;
+ struct pwm_waveform cnv_wf = { };
+ u64 target = LTC2378_TCNV_HIGH_NS;
+ unsigned int count = 0;
+ u64 offload_offset_ns;
+ int ret;
+
+ if (freq_Hz == 0)
+ return -EINVAL;
+
+ if (freq_Hz < 1 || freq_Hz > st->info->max_sample_rate_hz)
+ return -ERANGE;
+
+ /* 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);
+ 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.
+ * 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);
+ if (ret)
+ return ret;
+ offload_offset_ns += 10;
+ } while (count++ < 100 && config->periodic.offset_ns < min_read_offset);
+
+ st->cnv_wf = cnv_wf;
+ st->cnv_Hz = DIV_ROUND_CLOSEST_ULL(HZ_PER_GHZ, cnv_wf.period_length_ns);
+
+ return 0;
+}
+
+static ssize_t sampling_frequency_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ltc2378_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%u\n", st->cnv_Hz);
+}
+
+static ssize_t sampling_frequency_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+ if (IIO_DEV_ACQUIRE_FAILED(claim))
+ return -EBUSY;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ ret = ltc2378_update_conversion_rate(st, val);
+
+ return ret ?: len;
+}
+
+static ssize_t sampling_frequency_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ return sysfs_emit(buf, "[%u %u %u]\n",
+ 1, 1, st->info->max_sample_rate_hz);
+}
+
+static IIO_DEVICE_ATTR_RO(sampling_frequency_available, 0);
+static IIO_DEVICE_ATTR(sampling_frequency, 0644,
+ sampling_frequency_show, sampling_frequency_store, 0);
+
+static const struct iio_dev_attr *ltc2378_offload_buffer_attrs[] = {
+ &iio_dev_attr_sampling_frequency_available,
+ &iio_dev_attr_sampling_frequency,
+ NULL
+};
+
+static int ltc2378_prepare_offload_message(struct device *dev,
+ struct ltc2378_state *st)
+{
+ st->offload_xfer.bits_per_word = st->info->resolution;
+ st->offload_xfer.len = st->info->resolution > 16 ? 4 : 2;
+ st->offload_xfer.offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
+
+ /* Initialize message with offload */
+ spi_message_init_with_transfers(&st->offload_msg, &st->offload_xfer, 1);
+ st->offload_msg.offload = st->offload;
+
+ return devm_spi_optimize_message(dev, st->spi, &st->offload_msg);
+}
+
+static int ltc2378_offload_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pwm_set_waveform_might_sleep(st->cnv_trigger, &st->cnv_wf, false);
+ if (ret)
+ return ret;
+
+ ret = spi_offload_trigger_enable(st->offload, st->offload_trigger,
+ &st->offload_trigger_config);
+ if (ret)
+ goto out_pwm_disable;
+
+ return 0;
+
+out_pwm_disable:
+ pwm_disable(st->cnv_trigger);
+ return ret;
+}
+
+static int ltc2378_offload_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ltc2378_state *st = iio_priv(indio_dev);
+
+ spi_offload_trigger_disable(st->offload, st->offload_trigger);
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ltc2378_offload_buffer_ops = {
+ .postenable = <c2378_offload_buffer_postenable,
+ .predisable = <c2378_offload_buffer_predisable,
+};
+
+static int ltc2378_spi_offload_setup(struct iio_dev *indio_dev,
+ struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ struct dma_chan *rx_dma;
+
+ indio_dev->setup_ops = <c2378_offload_buffer_ops;
+
+ st->offload_trigger = devm_spi_offload_trigger_get(dev, st->offload,
+ SPI_OFFLOAD_TRIGGER_PERIODIC);
+ if (IS_ERR(st->offload_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->offload_trigger),
+ "failed to get offload trigger\n");
+
+ st->offload_trigger_config.type = SPI_OFFLOAD_TRIGGER_PERIODIC;
+
+ rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev, st->offload);
+ if (IS_ERR(rx_dma))
+ return dev_err_probe(dev, PTR_ERR(rx_dma), "failed to get offload RX DMA\n");
+
+ return devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev, rx_dma,
+ IIO_BUFFER_DIRECTION_IN,
+ ltc2378_offload_buffer_attrs);
+}
+
+static int ltc2378_pwm_get(struct ltc2378_state *st)
+{
+ struct device *dev = &st->spi->dev;
+
+ st->cnv_trigger = devm_pwm_get(dev, NULL);
+ if (IS_ERR(st->cnv_trigger))
+ return dev_err_probe(dev, PTR_ERR(st->cnv_trigger),
+ "failed to get cnv pwm\n");
+
+ /*
+ * Disable the PWM connected to CNV in case it was left running by
+ * something else.
+ */
+ pwm_disable(st->cnv_trigger);
+
+ return 0;
+}
+
+static const struct spi_offload_config ltc2378_offload_config = {
+ .capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
+ SPI_OFFLOAD_CAP_RX_STREAM_DMA,
+};
+
+static int ltc2378_offload_buffer_setup(struct iio_dev *indio_dev,
+ struct ltc2378_state *st)
+{
+ struct spi_device *spi = st->spi;
+ struct device *dev = &spi->dev;
+ int ret;
+
+ st->offload = devm_spi_offload_get(dev, spi, <c2378_offload_config);
+ ret = PTR_ERR_OR_ZERO(st->offload);
+ if (ret)
+ return ret;
+
+ ret = ltc2378_spi_offload_setup(indio_dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to setup SPI offload\n");
+
+ ret = ltc2378_pwm_get(st);
+ if (ret)
+ return ret;
+
+ /*
+ * Start with a slower sampling rate so there is some room for
+ * adjusting the sampling frequency without hitting the maximum
+ * conversion rate.
+ */
+ ret = ltc2378_update_conversion_rate(st, st->info->max_sample_rate_hz >> 4);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to sampling frequency\n");
+
+ ret = ltc2378_prepare_offload_message(&spi->dev, st);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to optimize SPI message\n");
+
+ return 0;
+}
+
+static const struct ltc2378_ops ltc2378_offload_ops = {
+ .buffer_setup = ltc2378_offload_buffer_setup,
+};
+
+int ltc2378_set_offload_ops(struct ltc2378_state *st)
+{
+ st->ops = <c2378_offload_ops;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ltc2378_set_offload_ops, "IIO_LTC2378");
+
+MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");
diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
index 88582bdcd6a6..bf17b202230b 100644
--- a/drivers/iio/adc/ltc2378.c
+++ b/drivers/iio/adc/ltc2378.c
@@ -17,6 +17,7 @@
#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>
@@ -26,120 +27,160 @@
static const struct ltc2378_chip_info ltc2338_18_chip_info = {
.name = "ltc2338-18",
.resolution = 18,
+ .max_sample_rate_hz = HZ_PER_MHZ,
+ .tconv_ns = 527,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2364_16_chip_info = {
.name = "ltc2364-16",
.resolution = 16,
+ .max_sample_rate_hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2364_18_chip_info = {
.name = "ltc2364-18",
.resolution = 18,
+ .max_sample_rate_hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2367_16_chip_info = {
.name = "ltc2367-16",
.resolution = 16,
+ .max_sample_rate_hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2367_18_chip_info = {
.name = "ltc2367-18",
.resolution = 18,
+ .max_sample_rate_hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2368_16_chip_info = {
.name = "ltc2368-16",
.resolution = 16,
+ .max_sample_rate_hz = HZ_PER_MHZ,
+ .tconv_ns = 527,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2368_18_chip_info = {
.name = "ltc2368-18",
.resolution = 18,
+ .max_sample_rate_hz = HZ_PER_MHZ,
+ .tconv_ns = 527,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2369_18_chip_info = {
.name = "ltc2369-18",
.resolution = 18,
+ .max_sample_rate_hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2370_16_chip_info = {
.name = "ltc2370-16",
.resolution = 16,
+ .max_sample_rate_hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
.bipolar = false,
};
static const struct ltc2378_chip_info ltc2376_16_chip_info = {
.name = "ltc2376-16",
.resolution = 16,
+ .max_sample_rate_hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2376_18_chip_info = {
.name = "ltc2376-18",
.resolution = 18,
+ .max_sample_rate_hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2376_20_chip_info = {
.name = "ltc2376-20",
.resolution = 20,
+ .max_sample_rate_hz = 250 * HZ_PER_KHZ,
+ .tconv_ns = 3000,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2377_16_chip_info = {
.name = "ltc2377-16",
.resolution = 16,
+ .max_sample_rate_hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2377_18_chip_info = {
.name = "ltc2377-18",
.resolution = 18,
+ .max_sample_rate_hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2377_20_chip_info = {
.name = "ltc2377-20",
.resolution = 20,
+ .max_sample_rate_hz = 500 * HZ_PER_KHZ,
+ .tconv_ns = 1500,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2378_16_chip_info = {
.name = "ltc2378-16",
.resolution = 16,
+ .max_sample_rate_hz = HZ_PER_MHZ,
+ .tconv_ns = 527,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2378_18_chip_info = {
.name = "ltc2378-18",
.resolution = 18,
+ .max_sample_rate_hz = HZ_PER_MHZ,
+ .tconv_ns = 527,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2378_20_chip_info = {
.name = "ltc2378-20",
.resolution = 20,
+ .max_sample_rate_hz = HZ_PER_MHZ,
+ .tconv_ns = 675,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2379_18_chip_info = {
.name = "ltc2379-18",
.resolution = 18,
+ .max_sample_rate_hz = 1600 * HZ_PER_KHZ,
+ .tconv_ns = 412,
.bipolar = true,
};
static const struct ltc2378_chip_info ltc2380_16_chip_info = {
.name = "ltc2380-16",
.resolution = 16,
+ .max_sample_rate_hz = 2 * HZ_PER_MHZ,
+ .tconv_ns = 322,
.bipolar = true,
};
@@ -266,6 +307,10 @@ static int ltc2378_probe(struct spi_device *spi)
st->xfer.len = st->info->resolution > 16 ? 4 : 2;
st->xfer.bits_per_word = st->info->resolution;
+ ret = ltc2378_lib_buffer_setup(indio_dev, st);
+ if (ret)
+ return ret;
+
indio_dev->channels = st->chans;
indio_dev->num_channels = st->num_iio_chans;
@@ -336,3 +381,4 @@ MODULE_AUTHOR("Ioan-Daniel Pop <pop.ioan-daniel@analog.com>");
MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
MODULE_DESCRIPTION("Analog Devices LTC2378 ADC series driver");
MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_LTC2378");
diff --git a/drivers/iio/adc/ltc2378.h b/drivers/iio/adc/ltc2378.h
index a3a69351de6c..f6e10f9a83e0 100644
--- a/drivers/iio/adc/ltc2378.h
+++ b/drivers/iio/adc/ltc2378.h
@@ -8,10 +8,14 @@
#ifndef __DRIVERS_IIO_ADC_LTC2378_H__
#define __DRIVERS_IIO_ADC_LTC2378_H__
+#include <linux/errno.h>
#include <linux/iio/iio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
+#include <linux/pwm.h>
#include <linux/spi/spi.h>
+#include <linux/spi/offload/consumer.h>
+#include <linux/spi/offload/types.h>
#include <linux/types.h>
#include <linux/units.h>
@@ -19,12 +23,24 @@
#define LTC2378_TBUSYLH_NS 13
#define LTC2378_TCNV_HIGH_NS 20
+struct ltc2378_state;
+
struct ltc2378_chip_info {
const char *name;
int resolution;
+ unsigned int max_sample_rate_hz;
+ unsigned int tconv_ns;
bool bipolar;
};
+/**
+ * struct ltc2378_ops: Setup specific procedures for ltc2378 devices.
+ * @ltc2378_buffer_setup: Custom buffer setup implementation.
+ */
+struct ltc2378_ops {
+ int (*buffer_setup)(struct iio_dev *indio_dev, struct ltc2378_state *st);
+};
+
struct ltc2378_state {
const struct ltc2378_chip_info *info;
struct gpio_desc *cnv_gpio;
@@ -33,6 +49,15 @@ struct ltc2378_state {
unsigned int num_iio_chans;
struct iio_chan_spec chans[2]; /* 1 physical chan + 1 timestamp chan */
int ref_uV;
+ const struct ltc2378_ops *ops;
+ unsigned int cnv_Hz;
+ struct pwm_waveform cnv_wf;
+ struct spi_offload *offload;
+ struct spi_offload_trigger *offload_trigger;
+ struct spi_message offload_msg;
+ struct spi_transfer offload_xfer;
+ struct spi_offload_trigger_config offload_trigger_config;
+ struct pwm_device *cnv_trigger;
/*
* DMA (thus cache coherency maintenance) requires the
@@ -60,4 +85,21 @@ static inline int ltc2378_convert_and_acquire(struct ltc2378_state *st)
return ret;
}
+int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st);
+
+#define __ltc2378_set_offload_ops(st) ltc2378_set_offload_ops((st))
+
+#ifdef CONFIG_LTC2378_LIB_OFFLOAD_BUFFER
+
+int ltc2378_set_offload_ops(struct ltc2378_state *st);
+
+#else
+
+static inline int ltc2378_set_offload_ops(struct ltc2378_state *st)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_LTC2378_LIB_OFFLOAD_BUFFER */
+
#endif /* __DRIVERS_IIO_ADC_LTC2378_H__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v3 4/5] iio: adc: ltc2378: Enable high-speed data capture
2026-06-17 2:04 ` [PATCH v3 4/5] iio: adc: ltc2378: Enable high-speed data capture Marcelo Schmitt
@ 2026-06-17 16:26 ` Julian Braha
2026-06-17 22:33 ` David Lechner
1 sibling, 0 replies; 17+ messages in thread
From: Julian Braha @ 2026-06-17 16:26 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel, marcelo.schmitt1
Hi Marcelo,
On 6/17/26 03:04, Marcelo Schmitt wrote:
> + select LTC2378_LIB_OFFLOAD_BUFFER if SPI_OFFLOAD && PWM && SPI_OFFLOAD_TRIGGER_PWM && IIO_BUFFER && IIO_BUFFER_DMAENGINE
The PWM in this expression is unnecessary since you're && it with
SPI_OFFLOAD_TRIGGER_PWM.
- Julian Braha
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 4/5] iio: adc: ltc2378: Enable high-speed data capture
2026-06-17 2:04 ` [PATCH v3 4/5] iio: adc: ltc2378: Enable high-speed data capture Marcelo Schmitt
2026-06-17 16:26 ` Julian Braha
@ 2026-06-17 22:33 ` David Lechner
1 sibling, 0 replies; 17+ messages in thread
From: David Lechner @ 2026-06-17 22:33 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
On 6/16/26 9:04 PM, Marcelo Schmitt wrote:
> Make use of SPI transfer offloading to speed up data capture, enabling data
> acquisition at faster sample rates (up to 2 MSPS).
>
> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> ---
> Change log v2 -> v3:
> - Fixed the evaluation loop conditions for CNV PWM and SPI Engine trigger PWM,
> avoiding potential infinite loop if and CPU stall.
> - Added comment to about initial PWM disable.
> - Adjusted SPI offload setup initialization to not print error on a valid condition.
> - Fully initialize IIO channel scan_type.
> - Reworked to make offload support not imply all dependencies to be built in.
> - Made sampling_frequency a buffer attribute.
> - Made offload support not require DMA and other features to be built in.
> - Now using same scan_type configuration for all use cases.
>
> drivers/iio/adc/Kconfig | 19 ++
> drivers/iio/adc/Makefile | 6 +
> drivers/iio/adc/ltc2378-lib-core.c | 35 +++
> drivers/iio/adc/ltc2378-offload-buffer.c | 305 +++++++++++++++++++++++
> drivers/iio/adc/ltc2378.c | 46 ++++
> drivers/iio/adc/ltc2378.h | 42 ++++
> 6 files changed, 453 insertions(+)
> create mode 100644 drivers/iio/adc/ltc2378-lib-core.c
> create mode 100644 drivers/iio/adc/ltc2378-offload-buffer.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 2b8203451367..f96d9262b891 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -944,6 +944,9 @@ config LTC2378
> tristate "Analog Devices LTC2378 ADC driver"
> depends on SPI
> depends on GPIOLIB
> + select LTC2378_LIB
> + select LTC2378_LIB_OFFLOAD_BUFFER if SPI_OFFLOAD && PWM && SPI_OFFLOAD_TRIGGER_PWM && IIO_BUFFER && IIO_BUFFER_DMAENGINE
> + select LTC2378_LIB_TRIGGERED_BUFFER if IIO_BUFFER
> help
> Say yes here to build support for Analog Devices LTC2378-20 and
> similar analog to digital converters.
> @@ -2027,3 +2030,19 @@ config XILINX_AMS
> xilinx-ams.
>
> endmenu
> +
> +config LTC2378_LIB
> + tristate
> + help
> + Say yes here to build support for buffered data capture with LTC2378
> +
> +config LTC2378_LIB_OFFLOAD_BUFFER
> + bool
> + help
> + Say yes here to build support for high speed data capture with LTC2378
> +
> +config LTC2378_LIB_TRIGGERED_BUFFER
> + bool
> + select IIO_TRIGGERED_BUFFER
> + help
Why do these need to be compile-time options to only select one or the other?
In all other SPI offload ADCs we've done so far, they always support both and
gets selected at runtime based on devicetree config.
> + Say yes here to build support for buffered data capture with LTC2378
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 1814fb78dde3..109cd39237c9 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -82,6 +82,12 @@ obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
> obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
> obj-$(CONFIG_LTC2309) += ltc2309.o
> obj-$(CONFIG_LTC2378) += ltc2378.o
> +
> +ltc2378_lib-y += ltc2378-lib-core.o
> +ltc2378_lib-$(CONFIG_LTC2378_LIB_OFFLOAD_BUFFER) += ltc2378-offload-buffer.o
> +ltc2378_lib-$(CONFIG_LTC2378_LIB_TRIGGERED_BUFFER) += ltc2378-triggered-buffer.o
> +obj-$(CONFIG_LTC2378_LIB) += ltc2378_lib.o
Why do these need to be split into separate files? The driver isn't that
long, so seems better to just do it all in one file to make it easier to
read.
> +
> obj-$(CONFIG_LTC2471) += ltc2471.o
> obj-$(CONFIG_LTC2485) += ltc2485.o
> obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
> diff --git a/drivers/iio/adc/ltc2378-lib-core.c b/drivers/iio/adc/ltc2378-lib-core.c
> new file mode 100644
> index 000000000000..1160f4324d01
> --- /dev/null
> +++ b/drivers/iio/adc/ltc2378-lib-core.c
> @@ -0,0 +1,35 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Analog Devices LTC2378 ADC series driver
> + *
> + * Copyright (C) 2026 Analog Devices Inc.
> + * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
> + */
> +
> +#include <linux/err.h>
> +#include <linux/iio/iio.h>
> +
> +#include "ltc2378.h"
> +
> +int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st)
> +{
> + struct device *dev = &st->spi->dev;
> + int ret;
> +
> + ret = __ltc2378_set_offload_ops(st);
> + if (ret == -EOPNOTSUPP)
> + return 0; /* Let device setup complete without buffer support */
> +
> + if (!ret)
> + ret = st->ops->buffer_setup(indio_dev, st);
> +
> + if (ret)
> + return dev_err_probe(dev, ret, "error on SPI offload setup\n");
Would be better to just return early instead of doing the !ret check.
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(ltc2378_lib_buffer_setup, "IIO_LTC2378");
> +
...
> diff --git a/drivers/iio/adc/ltc2378.c b/drivers/iio/adc/ltc2378.c
> index 88582bdcd6a6..bf17b202230b 100644
> --- a/drivers/iio/adc/ltc2378.c
> +++ b/drivers/iio/adc/ltc2378.c
> @@ -17,6 +17,7 @@
> #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>
> @@ -26,120 +27,160 @@
> static const struct ltc2378_chip_info ltc2338_18_chip_info = {
> .name = "ltc2338-18",
> .resolution = 18,
> + .max_sample_rate_hz = HZ_PER_MHZ,
1 * HZ_PER_MHZ would make a bit more sense
> + .tconv_ns = 527,
> .bipolar = true,
> };
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 5/5] iio: adc: ltc2378: Enable triggered buffer data capture
2026-06-17 2:02 [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs Marcelo Schmitt
` (3 preceding siblings ...)
2026-06-17 2:04 ` [PATCH v3 4/5] iio: adc: ltc2378: Enable high-speed data capture Marcelo Schmitt
@ 2026-06-17 2:04 ` Marcelo Schmitt
2026-06-17 22:39 ` David Lechner
2026-06-17 22:46 ` [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs David Lechner
5 siblings, 1 reply; 17+ messages in thread
From: Marcelo Schmitt @ 2026-06-17 2:04 UTC (permalink / raw)
To: linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, dlechner, andy, robh, krzk+dt,
conor+dt, pop.ioan-daniel, marcelo.schmitt1
Enable users to run triggered data captures with LTC2378 and similar ADCs.
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
Change log v2 -> v3:
- Extracted from main driver file into a separate buffer-specific containment.
drivers/iio/adc/ltc2378-lib-core.c | 17 +++++++-
drivers/iio/adc/ltc2378-triggered-buffer.c | 49 ++++++++++++++++++++++
drivers/iio/adc/ltc2378.h | 15 +++++++
3 files changed, 80 insertions(+), 1 deletion(-)
create mode 100644 drivers/iio/adc/ltc2378-triggered-buffer.c
diff --git a/drivers/iio/adc/ltc2378-lib-core.c b/drivers/iio/adc/ltc2378-lib-core.c
index 1160f4324d01..ec83e9f2ae81 100644
--- a/drivers/iio/adc/ltc2378-lib-core.c
+++ b/drivers/iio/adc/ltc2378-lib-core.c
@@ -18,7 +18,7 @@ int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st
ret = __ltc2378_set_offload_ops(st);
if (ret == -EOPNOTSUPP)
- return 0; /* Let device setup complete without buffer support */
+ goto trigger_buf_setup;
if (!ret)
ret = st->ops->buffer_setup(indio_dev, st);
@@ -27,6 +27,21 @@ int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st
return dev_err_probe(dev, ret, "error on SPI offload setup\n");
return 0;
+
+trigger_buf_setup:
+ ret = __ltc2378_set_triggered_buf_ops(st);
+ if (ret == -EOPNOTSUPP)
+ return 0; /* Let device setup complete without buffer support */
+
+ if (!ret)
+ ret = st->ops->buffer_setup(indio_dev, st);
+
+ if (ret)
+ return dev_err_probe(dev, ret, "error on buffer setup\n");
+
+ st->chans[st->num_iio_chans++] = IIO_CHAN_SOFT_TIMESTAMP(1);
+
+ return 0;
}
EXPORT_SYMBOL_NS_GPL(ltc2378_lib_buffer_setup, "IIO_LTC2378");
diff --git a/drivers/iio/adc/ltc2378-triggered-buffer.c b/drivers/iio/adc/ltc2378-triggered-buffer.c
new file mode 100644
index 000000000000..d1d788fb5cb4
--- /dev/null
+++ b/drivers/iio/adc/ltc2378-triggered-buffer.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 Analog Devices, Inc.
+ * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
+ */
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include <ltc2378.h>
+
+static irqreturn_t ltc2378_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ltc2378_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = ltc2378_convert_and_acquire(st);
+ if (ret < 0)
+ goto err_out;
+
+ iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
+ pf->timestamp);
+
+err_out:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int ltc2378_triggered_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st)
+{
+ return devm_iio_triggered_buffer_setup(&st->spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ <c2378_trigger_handler,
+ NULL);
+}
+
+static const struct ltc2378_ops ltc2378_triggered_buf_ops = {
+ .buffer_setup = ltc2378_triggered_buffer_setup,
+};
+
+int ltc2378_set_triggered_buf_ops(struct ltc2378_state *st)
+{
+ st->ops = <c2378_triggered_buf_ops;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ltc2378_set_triggered_buf_ops, "IIO_LTC2378");
diff --git a/drivers/iio/adc/ltc2378.h b/drivers/iio/adc/ltc2378.h
index f6e10f9a83e0..4241b1e79ac0 100644
--- a/drivers/iio/adc/ltc2378.h
+++ b/drivers/iio/adc/ltc2378.h
@@ -89,6 +89,8 @@ int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st
#define __ltc2378_set_offload_ops(st) ltc2378_set_offload_ops((st))
+#define __ltc2378_set_triggered_buf_ops(st) ltc2378_set_triggered_buf_ops((st))
+
#ifdef CONFIG_LTC2378_LIB_OFFLOAD_BUFFER
int ltc2378_set_offload_ops(struct ltc2378_state *st);
@@ -102,4 +104,17 @@ static inline int ltc2378_set_offload_ops(struct ltc2378_state *st)
#endif /* CONFIG_LTC2378_LIB_OFFLOAD_BUFFER */
+#ifdef CONFIG_LTC2378_LIB_TRIGGERED_BUFFER
+
+int ltc2378_set_triggered_buf_ops(struct ltc2378_state *st);
+
+#else
+
+static inline int ltc2378_set_triggered_buf_ops(struct ltc2378_state *st)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_LTC2378_LIB_TRIGGERED_BUFFER */
+
#endif /* __DRIVERS_IIO_ADC_LTC2378_H__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v3 5/5] iio: adc: ltc2378: Enable triggered buffer data capture
2026-06-17 2:04 ` [PATCH v3 5/5] iio: adc: ltc2378: Enable triggered buffer " Marcelo Schmitt
@ 2026-06-17 22:39 ` David Lechner
0 siblings, 0 replies; 17+ messages in thread
From: David Lechner @ 2026-06-17 22:39 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
On 6/16/26 9:04 PM, Marcelo Schmitt wrote:
> Enable users to run triggered data captures with LTC2378 and similar ADCs.
>
> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> ---
> Change log v2 -> v3:
> - Extracted from main driver file into a separate buffer-specific containment.
>
> drivers/iio/adc/ltc2378-lib-core.c | 17 +++++++-
> drivers/iio/adc/ltc2378-triggered-buffer.c | 49 ++++++++++++++++++++++
> drivers/iio/adc/ltc2378.h | 15 +++++++
> 3 files changed, 80 insertions(+), 1 deletion(-)
> create mode 100644 drivers/iio/adc/ltc2378-triggered-buffer.c
>
> diff --git a/drivers/iio/adc/ltc2378-lib-core.c b/drivers/iio/adc/ltc2378-lib-core.c
> index 1160f4324d01..ec83e9f2ae81 100644
> --- a/drivers/iio/adc/ltc2378-lib-core.c
> +++ b/drivers/iio/adc/ltc2378-lib-core.c
> @@ -18,7 +18,7 @@ int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st
>
> ret = __ltc2378_set_offload_ops(st);
> if (ret == -EOPNOTSUPP)
> - return 0; /* Let device setup complete without buffer support */
> + goto trigger_buf_setup;
>
> if (!ret)
> ret = st->ops->buffer_setup(indio_dev, st);
> @@ -27,6 +27,21 @@ int ltc2378_lib_buffer_setup(struct iio_dev *indio_dev, struct ltc2378_state *st
> return dev_err_probe(dev, ret, "error on SPI offload setup\n");
>
> return 0;
> +
> +trigger_buf_setup:
> + ret = __ltc2378_set_triggered_buf_ops(st);
> + if (ret == -EOPNOTSUPP)
> + return 0; /* Let device setup complete without buffer support */
> +
> + if (!ret)
> + ret = st->ops->buffer_setup(indio_dev, st);
> +
> + if (ret)
> + return dev_err_probe(dev, ret, "error on buffer setup\n");
This is repeating the code above. Seems like it would be better without
the goto.
> +
> + st->chans[st->num_iio_chans++] = IIO_CHAN_SOFT_TIMESTAMP(1);
Adding a channel here seems messy. I still think static const channel
data would be better. But at least would be better if this was moved
to the same function as the other channel setup.
> +
> + return 0;
> }
> EXPORT_SYMBOL_NS_GPL(ltc2378_lib_buffer_setup, "IIO_LTC2378");
>
> diff --git a/drivers/iio/adc/ltc2378-triggered-buffer.c b/drivers/iio/adc/ltc2378-triggered-buffer.c
> new file mode 100644
> index 000000000000..d1d788fb5cb4
> --- /dev/null
> +++ b/drivers/iio/adc/ltc2378-triggered-buffer.c
> @@ -0,0 +1,49 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2026 Analog Devices, Inc.
> + * Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
> + */
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#include <ltc2378.h>
> +
> +static irqreturn_t ltc2378_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct ltc2378_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + ret = ltc2378_convert_and_acquire(st);
> + if (ret < 0)
> + goto err_out;
As mentioned elsewhere, SPI xfer size may be 2 or 4 bytes, but
we are always pushing 4 bytes, so this only works on little-endian
architecture.
I think best would be to have storagesize = 16 when appropriate to
avoid having to manually handle this.
> +
> + iio_push_to_buffers_with_ts(indio_dev, &st->scan, sizeof(st->scan),
> + pf->timestamp);
> +
> +err_out:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
> +
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs
2026-06-17 2:02 [PATCH v3 0/5] iio: adc: Add support for LTC2378 and similar ADCs Marcelo Schmitt
` (4 preceding siblings ...)
2026-06-17 2:04 ` [PATCH v3 5/5] iio: adc: ltc2378: Enable triggered buffer " Marcelo Schmitt
@ 2026-06-17 22:46 ` David Lechner
5 siblings, 0 replies; 17+ messages in thread
From: David Lechner @ 2026-06-17 22:46 UTC (permalink / raw)
To: Marcelo Schmitt, linux-iio, devicetree, linux-kernel
Cc: jic23, nuno.sa, Michael.Hennerich, andy, robh, krzk+dt, conor+dt,
pop.ioan-daniel, marcelo.schmitt1
On 6/16/26 9:02 PM, Marcelo Schmitt wrote:
> 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).
>
> The first patch adds device tree documentation for LTC2378.
>
> The second patch enables single-shot sample read with a GPIO connected
> to the LTC2378 CNV pin.
>
> The third patch extends IIO DMAengine buffer interface to make
> sampling_frequency and sampling_frequency_available buffer attributes.
>
> The fourth patch 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.
>
> The last support patch 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:
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf
> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf
Putting `Link:` tags like this in the cover letter will cause b4 to apply all of these
to all patches in the series, so we don't want to do that. I think it is enough that
they are already in the code, we don't need to list them again here.
^ permalink raw reply [flat|nested] 17+ messages in thread