iio: adc: Add support for LTC2378 and similar ADCs
Marcelo Schmitt
maintainerv6 cover iio: adc: Add support for LTC2378 and similar ADCs
This patch series adds support for LTC2378 and similar low noise, low power, high speed, successive approximation register (SAR) ADCs. These ADCs are similar among each other, varying mainly on the amount of precision bits, maximum sample rate, and input configuration (either fully differential or pseudo-differential).
Patch 1 adds device tree documentation for LTC2378.
Patch 2 enables single-shot sample read with a GPIO connected to the LTC2378 CNV pin.
Patch 3 enables high-speed data captures with SPI offloading.
The setup is similar to AD4030, with a specialized PWM generator being used both for SPI offload triggering and conversion start signaling.
Patch 4 enables running buffered data captures without SPI offloading.
Even though these parts are somewhat similar to AD4000, the wiring configuration for LTC parts is different as well as the available HDL for high speed sample rate mode. Because of that, I propose creating a new device driver for supporting LTC2378-like devices.
Specifications can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf
Previous submissions:
v5: https://lore.kernel.org/linux-iio/[email protected]/
v4: https://lore.kernel.org/linux-iio/[email protected]/
v3: https://lore.kernel.org/linux-iio/[email protected]/
v2: https://lore.kernel.org/linux-iio/[email protected]/
v1: https://lore.kernel.org/linux-iio/[email protected]/
Change log v5 -> v6:
[DT]
- Dropped single compatible strings for parts that have a fall back so dt-schema can enforces the fallback compliance.
[IIO]
- Added a comment to clarify IIO buffer storagebits and endianness for the offload use case.
- Added comment to clarify CPU endianness is used when device native BE is not.
- Protected single sample read procedure with a mutex.
- Dropped mod_devicetable.h, include device-id/spi.h device-id/of.h instead.
- Used iwyu to add missing #includes and dropped superfluous ones.
- Used pahole to minimize memory holes in LTC2378 data structures.
With best regards,
Marcelo
Marcelo Schmitt (4):
dt-bindings: iio: adc: Add ltc2378
iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
iio: adc: ltc2378: Enable high-speed data capture
iio: adc: ltc2378: Enable triggered buffer data capture
.../bindings/iio/adc/adi,ltc2378.yaml | 170 ++++ MAINTAINERS | 8 + drivers/iio/adc/Kconfig | 19 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2378.c | 821 ++++++++++++++++++ 5 files changed, 1019 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml create mode 100644 drivers/iio/adc/ltc2378.c
base-commit: 093239070573637ad2b4cb56abc9c4c7ee109294
Signature · 1 line
2.53.0
Marcelo Schmitt
maintainerPATCH v6 1/4 dt-bindings: iio: adc: Add ltc2378
Document how to describe LTC2378-20 and similar ADCs in device tree.
---
Change log v5 -> v6:
- Dropped single compatible strings for parts that hava a fall back so dt-schema can enforces the fallback compliance.
.../bindings/iio/adc/adi,ltc2378.yaml | 170 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 177 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +170 -0
@@ -0,0 +1,170 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ltc2378.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices LTC2378 and similar Analog to Digital Converters + +maintainers: + - Marcelo Schmitt <[email protected]> + +description: | + Analog Devices LTC2378 series of ADCs. + Specifications can be found at: + https://www.analog.com/media/en/technical-documentation/data-sheets/233818fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236416fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236418f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236716fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236718f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236816f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236818f.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/236918fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237016fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237616fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237618fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237620fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237716fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237718fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237720fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237816fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237818fa.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237820fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/237918fb.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/238016fb.pdf + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + oneOf: + # Single compatible string match. + - enum: + - adi,ltc2338-18 + - adi,ltc2364-16 + - adi,ltc2364-18 + - adi,ltc2376-16 + - adi,ltc2376-18 + - adi,ltc2376-20 + + # 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,ltc2378-18 # 1 MSPS + - adi,ltc2377-18 # 500 kSPS + - const: adi,ltc2376-18 # fallback (250 kSPS) + + # Low sample rate fallback for 20-bit bipolar sensors. + - items: + - enum: + - adi,ltc2378-20 # 1 MSPS + - adi,ltc2377-20 # 500 kSPS + - const: adi,ltc2376-20 # fallback (250 kSPS) + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 100000000 + + vdd-supply: + description: A 2.5V supply that powers the chip (VDD). + + ovdd-supply: + description: + A 1.71V to 5.25V supply that sets the logic level for digital interface. + + ref-supply: + description: + Voltage reference input that determines the scale of ADC conversions. + + refin-supply: + description: + Alternative voltage reference input. + + cnv-gpios: + description: + When provided, this property indicates the GPIO that is connected to the + CNV pin. + maxItems: 1 + + pwms: + description: PWM signal connected to the CNV pin. + maxItems: 1 + + interrupts: + description: + Interrupt for signaling the completion of conversion results. The active + low signal provided on the BUSY pin asserts when ADC conversions finish. + maxItems: 1 + +required: + - compatible + - reg + - vdd-supply + - ovdd-supply + +allOf: + # Except for LTC2338, all designs require a voltage reference input + - if: + properties: + compatible: + not: + contains: + enum: + - adi,ltc2338-18 + then: + required: + - ref-supply + properties: + refin-supply: false + else: + properties: + ref-supply: false + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + adc@0 { + compatible = "adi,ltc2378-20", "adi,ltc2376-20"; + reg = <0>; + spi-max-frequency = <71000000>; + vdd-supply = <&supply_2_5V>; + ovdd-supply = <&supply_3_3V>; + ref-supply = <&supply_5V>; + cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + }; + };
MAINTAINERS +7 -0
@@ -15343,6 +15343,13 @@ S: Maintained F: Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml F: drivers/iio/dac/ltc1660.c +LTC2378 IIO ADC DRIVER +M: Marcelo Schmitt <[email protected]> +L: [email protected] +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml + LTC2664 IIO DAC DRIVER M: Michael Hennerich <[email protected]> M: Kim Seer Paller <[email protected]>
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v6 1/4] dt-bindings: iio: adc: Add ltc2378
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The binding incorrectly forces `ref-supply` to be mandatory (while forbidding `refin-supply`)…
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The binding incorrectly forces ref-supply to be mandatory (while forbidding refin-supply) for LTC2378 and most other devices, and wrongly forbids ref-supply for LTC2338-18. This contradicts the physical hardware capabilities. --
--- Patch [1]: [PATCH v6 1/4] dt-bindings: iio: adc: Add ltc2378 --- commit 5b94ffdebbdd7e1b8c34a651dee8aae08509770e
Author: Marcelo Schmitt <[email protected]>
dt-bindings: iio: adc: Add ltc2378
This commit adds device tree bindings for the Analog Devices LTC2378 and similar ADCs. It defines the required properties, compatible strings with fallback enforcement, and constraints for voltage supplies.
Quoted 4 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +0
-0
[ ... ]
Quoted 7 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +7
-0+allOf: + # Except for LTC2338, all designs require a voltage reference input + - if: + properties: + compatible: + not: + contains:
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml line 148
Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +10
-0+ enum: + - adi,ltc2338-18 + then: + required: + - ref-supply + properties: + refin-supply: false + else: + properties: + ref-supply: false
[Severity: Medium]
Could these constraints cause a validation regression for valid hardware configurations?
For example, if the LTC2378 uses an internal reference without an external supply, forcing ref-supply to be mandatory (while forbidding refin-supply) might cause valid device trees to fail validation unless a dummy regulator is instantiated.
Similarly, does the LTC2338-18 support overdriving the internal reference buffer? If so, forbidding ref-supply for adi,ltc2338-18 could prevent accurate hardware descriptions when an external reference is applied.
Should these constraints be adjusted to fully reflect the physical hardware capabilities and internal reference features described in the datasheets?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
Re: [PATCH v6 1/4] dt-bindings: iio: adc: Add ltc2378
Quoted 18 lines · On 07/09, [email protected] wrote:
Quoted 7 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +7
-0+allOf: + # Except for LTC2338, all designs require a voltage reference input + - if: + properties: + compatible: + not: + contains:Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml line 148
Quoted 10 lines
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +10
-0+ enum: + - adi,ltc2338-18 + then: + required: + - ref-supply + properties: + refin-supply: false + else: + properties: + ref-supply: false
Quoted 5 lines
[Severity: Medium]
Could these constraints cause a validation regression for valid hardware configurations?For example, if the LTC2378 uses an internal reference without an external
LTC2378 doesn't have internal reference and requires an external one.
Quoted 7 lines
supply, forcing ref-supply to be mandatory (while forbidding refin-supply) might cause valid device trees to fail validation unless a dummy regulator is instantiated.
Similarly, does the LTC2338-18 support overdriving the internal reference buffer? If so, forbidding ref-supply for adi,ltc2338-18 could prevent accurate hardware descriptions when an external reference is applied.
LTC2338-18 has no 'ref' pin. LTC2338-18 external reference supply is provided through refin pin.
Marcelo Schmitt
maintainerPATCH v6 2/4 iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
In reply to Marcelo SchmittSupport for LTC2378-20 and similar analog-to-digital converters.
---
Change log v5 -> v6:
- Added comment to clarify CPU endianness is used when device native BE is not.
- Protected single sample read procedure with a mutex.
- Dropped mod_devicetable.h, include device-id/spi.h device-id/of.h instead.
- Used iwyu to add missing #includes and dropped superfluous ones.
- Used pahole to minimize memory holes in LTC2378 data structures.
MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 12 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2378.c | 414 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 428 insertions(+) create mode 100644 drivers/iio/adc/ltc2378.c
MAINTAINERS +1 -0
@@ -15349,6 +15349,7 @@ L: [email protected] S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml +F: drivers/iio/adc/ltc2378.c LTC2664 IIO DAC DRIVER M: Michael Hennerich <[email protected]>
drivers/iio/adc/Kconfig +12 -0
@@ -943,6 +943,18 @@ config LTC2309 This driver can also be built as a module. If so, the module will be called ltc2309. +config LTC2378 + tristate "Analog Devices LTC2378 ADC driver" + depends on SPI + depends on REGULATOR || COMPILE_TEST + depends on GPIOLIB + help + Say yes here to build support for Analog Devices LTC2378-20 and + similar analog to digital converters. + + This driver can also be built as a module. If so, the module will + be called ltc2378. + config LTC2471 tristate "Linear Technology LTC2471 and LTC2473 ADC driver" depends on I2C
drivers/iio/adc/Makefile +1 -0
@@ -81,6 +81,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o obj-$(CONFIG_LTC2309) += ltc2309.o +obj-$(CONFIG_LTC2378) += ltc2378.o obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
drivers/iio/adc/ltc2378.c new file +414 -0
414 lines not on this page · raw message
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v6 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The internal mutex `st->lock` is released before reading the shared SPI transfer buffer, failing…
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The internal mutex st->lock is released before reading the shared SPI transfer buffer, failing to protect the single-sample read procedure. --
--- Patch [2]: [PATCH v6 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs --- commit 30c9caf90bdc7e619336a9deac3d3aa86c429417
Author: Marcelo Schmitt <[email protected]>
iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
Support for LTC2378-20 and similar analog-to-digital converters.
Quoted 4 lines
drivers/iio/adc/ltc2378.c new file +0
-0
[ ... ]
drivers/iio/adc/ltc2378.c line 187
Quoted 5 lines
drivers/iio/adc/ltc2378.c new file +5
-0+static int ltc2378_convert_and_acquire (struct ltc2378_state *st) +{ +int ret; + +guard (mutex)(&st->lock);
[Severity: Medium]
Does the use of guard(mutex) here release the lock before the data is safely extracted?
The lock is automatically released when this function returns, which means st->lock drops before ltc2378_channel_single_read() can consume the shared st->scan.data buffer.
Quoted 13 lines
drivers/iio/adc/ltc2378.c new file +13
-0+/* 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; +} + +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;
drivers/iio/adc/ltc2378.c line 210
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+u32 sample; +int ret; + + ret =ltc2378_convert_and_acquire (st); +if (ret) +return ret; + +if (chan->scan_type.endianness ==IIO_BE ) { +if (chan->scan_type.realbits >16 ) + sample =be32_to_cpu (st->scan.data.sample_buf32_be);
[Severity: Medium]
Is this read of st->scan.data properly protected against concurrent access?
Since ltc2378_convert_and_acquire() uses guard(mutex) to manage the lock, the lock is dropped before returning. This leaves the access to st->scan.data here unprotected by st->lock.
While the IIO core's direct mode mlock currently serializes these calls, this early release seems to break the intent stated in the commit message where the single sample read procedure is protected with a mutex.
Quoted 2 lines
drivers/iio/adc/ltc2378.c new file +2
-0+else + sample =be16_to_cpu (st->scan.data.sample_buf16_be);
[ ... ]
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Jonathan Cameron
maintainerRe: [PATCH v6 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
In reply to Marcelo SchmittQuoted 3 lines · On Thu, 9 Jul 2026 17:49:53 -0300 Marcelo Schmitt <[email protected]>…
Support for LTC2378-20 and similar analog-to-digital converters.
Signed-off-by Marcelo Schmitt <[email protected]>
https://sashiko.dev/#/patchset/cover.1783629101.git.marcelo.schmitt%40analog.com
The comment about not holding the local lock long enough looks right to me. You should never rely on the internal locks of IIO for modes to serialise. Maybe that implementation will change one day (probably not but it's a layering mess to rely on that).
I'll take another look at the rest in next few days. (other sashiko comments are fine I think)
David Lechner
reviewerRe: [PATCH v6 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
In reply to Marcelo SchmittQuoted 2 lines · Marcelo Schmitt
Support for LTC2378-20 and similar analog-to-digital converters.
...
drivers/iio/adc/ltc2378.c line 189
Quoted 7 lines
drivers/iio/adc/ltc2378.c new file +7
-0+static int ltc2378_convert_and_acquire (struct ltc2378_state *st) +{ +int ret; + +guard (mutex)(&st->lock); +/* Cause a rising edge of CNV to initiate a new ADC conversion */ +gpiod_set_value_cansleep (st->cnv_gpio,1 );
Wouldn't mind a comment here explaining where 4 comes from.
Quoted 7 lines
drivers/iio/adc/ltc2378.c new file +7
-0+fsleep (4 ); + ret =spi_sync_transfer (st->spi, &st->xfer,1 ); +gpiod_set_value_cansleep (st->cnv_gpio,0 ); + +return ret; +} +
Jonathan Cameron
maintainerRe: [PATCH v6 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
In reply to Marcelo SchmittQuoted 10 lines · On Thu, 9 Jul 2026 17:49:53 -0300 Marcelo Schmitt <[email protected]>…
Support for LTC2378-20 and similar analog-to-digital converters.
Signed-off-by Marcelo Schmitt <[email protected]>---
Change log v5 -> v6:
- Added comment to clarify CPU endianness is used when device native BE is not.
- Protected single sample read procedure with a mutex.
- Droppedmod_devicetable.h, includedevice-id/spi.hdevice-id/of.hinstead.
- Used iwyu to add missing #includes and dropped superfluous ones.
- Used pahole to minimize memory holes in LTC2378 data structures.
A small comment about maybe splitting the channel macro into offload and non offload variants. That would also let you only bring the offload version in as part of the next patch.
Jonathan
Quoted 11 lines
drivers/iio/adc/ltc2378.c new file +5
-0+#define LTC2378_DIFF_CHANNEL (_sign, _real_bits, _storage_bits, _offload) \ +{ \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \
drivers/iio/adc/ltc2378.c line 43
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+ (_offload ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0), \ + .info_mask_separate_available = _offload ? BIT(IIO_CHAN_INFO_SAMP_FREQ) : 0,\ + .scan_index = 0, \ + .scan_type = { \ + .format = _sign ?IIO_SCAN_FORMAT_SIGNED_INT : \ + IIO_SCAN_FORMAT_UNSIGNED_INT, \ + .realbits = _real_bits, \ + .storagebits = _storage_bits, \ + .shift = (_offload ?0 : _storage_bits - _real_bits), \ + .endianness = _offload ? IIO_CPU : IIO_BE \
As below. I think, in my ideal world the offload parts of this would have been in patch 3.
Given how much changes in here to me it's a bit marginal on whether having LTC2378_DIFF_CHANNEL() and LTC2378_DIFF_CHANNEL_OFFLOAD() would have been simpler.
Quoted 5 lines
drivers/iio/adc/ltc2378.c new file +4
-0+ }, \ +} + +struct ltc2378_state {
drivers/iio/adc/ltc2378.c line 69
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+const struct ltc2378_chip_info *info; +struct gpio_desc *cnv_gpio; +struct spi_device *spi; +struct mutex lock;/* Protect data acquisition cycle */ +int ref_uV; +struct spi_transfer xfer; + +/* +* DMA (thus cache coherency maintenance) requires the +* transfer buffers to live in their own cache lines.
Trivial but I'd wrap nearer 80 chars.
drivers/iio/adc/ltc2378.c line 76
Quoted 7 lines
drivers/iio/adc/ltc2378.c new file +7
-0+*/ +struct { +union { +__be16 sample_buf16_be; +__be32 sample_buf32_be; +u16 sample_buf16; +u32 sample_buf32;
I guess for churn reduction it is fine to introduce some stuff for offload before the patch that does the majority of it..
Quoted 8 lines
drivers/iio/adc/ltc2378.c new file +5
-0+ } data; +aligned_s64 timestamp; + } scan__aligned(IIO_DMA_MINALIGN) ; +}; +static int ltc2378_refin_setup (struct device *dev,struct ltc2378_state *st)
drivers/iio/adc/ltc2378.c line 283
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+{ +int ret; + +/* +* The internal reference buffer amplifies both the internal reference +* and REFIN by a factor of 2. +*/ + ret =devm_regulator_get_enable_read_voltage (dev,"refin" ); +if (ret == -ENODEV )/* refin is optional */ + st->ref_uV = st->info->internal_ref_uV *2 ;
Maybe
return 0;
as done
drivers/iio/adc/ltc2378.c line 284
Quoted 1 line
drivers/iio/adc/ltc2378.c new file +1
-0+else if (ret <0 )
if (ret < 0) return
drivers/iio/adc/ltc2378.c
Quoted 2 lines
drivers/iio/adc/ltc2378.c new file +2
-0+return dev_err_probe (dev, ret,"failed to read refin regulator\n" ); + else
and then no else needed? It's a marginal improvement so I don't mind if you ignore this suggestion.
Quoted 4 lines
drivers/iio/adc/ltc2378.c new file +4
-0+ st->ref_uV = ret *2 ; + +return 0 ; +}
Marcelo Schmitt
maintainerPATCH v6 3/4 iio: adc: ltc2378: Enable high-speed data capture
In reply to Marcelo SchmittMake use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 2 MSPS).
---
Change log v5 -> v6:
- Added a comment to clarify IIO buffer storagebits and endianness for the offload use case.
- Used iwyu to add missing #includes and dropped superfluous ones.
- Used pahole to minimize memory holes in LTC2378 data structures.
drivers/iio/adc/Kconfig | 6 + drivers/iio/adc/ltc2378.c | 384 +++++++++++++++++++++++++++++++++++++- 2 files changed, 387 insertions(+), 3 deletions(-)
drivers/iio/adc/Kconfig +6 -0
@@ -948,6 +948,12 @@ config LTC2378 depends on SPI depends on REGULATOR || COMPILE_TEST depends on GPIOLIB + depends on PWM + select IIO_BUFFER + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE + select SPI_OFFLOAD + select SPI_OFFLOAD_TRIGGER_PWM help Say yes here to build support for Analog Devices LTC2378-20 and similar analog to digital converters.
drivers/iio/adc/ltc2378.c +381 -3
545 lines not on this page · raw message
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v6 3/4] iio: adc: ltc2378: Enable high-speed data capture
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] The `st->offload_trigger_config` structure is partially mutated on the error path, leading to…
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The st->offload_trigger_config structure is partially mutated on the error path, leading to out-of-sync hardware configuration.
- [Medium] Spurious failure in sample frequency configuration due to asymmetric period-to-frequency mathematical conversions.
- [Low] Missing MODULE_IMPORT_NS for the SPI_OFFLOAD namespace.
--
--- Patch [3]: [PATCH v6 3/4] iio: adc: ltc2378: Enable high-speed data capture --- commit 3324fe468bed00820abda81953b558c8f8c9f5e4
Author: Marcelo Schmitt <[email protected]>
iio: adc: ltc2378: Enable high-speed data capture
This commit updates the ltc2378 ADC driver to support high-speed data capture. It utilizes SPI transfer offloading to increase data acquisition rates up to 2 MSPS.
Quoted 4 lines
drivers/iio/adc/ltc2378.c +0
-0
[ ... ]
Quoted 1 line
drivers/iio/adc/ltc2378.c +0
-0@@ -260,7 +359,136 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev,
[ ... ]
Quoted 19 lines
drivers/iio/adc/ltc2378.c +19
-0+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; +u64 offload_offset_ns; +int ret; + +if (freq_Hz ==0 ) +return -EINVAL ; + +if (!in_range (freq_Hz,1 , 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); +
[ ... ]
Quoted 8 lines
drivers/iio/adc/ltc2378.c +8
-0+/* +* Configure SPI offload PWM trigger. +* The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL. +* Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns) +* +* Use the same period as CNV PWM to avoid timing issues. +* Convert back from period to frequency for the SPI offload API. +*/
drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 460
Quoted 10 lines
drivers/iio/adc/ltc2378.c +10
-0+ 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 =100 ; + do { + config->periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, config); + if (ret) + return ret;
[Severity: High]
Does modifying config directly leave the device state out of sync if validation fails?
Here config aliases to &st->offload_trigger_config, and its frequency_hz and offset_ns fields are mutated. If spi_offload_trigger_validate() returns an error, the function returns without restoring the old configuration.
However, st->cnv_wf is only updated at the end of the function. If the buffer is later enabled, could this result in the PWM running with the old frequency while the SPI offload engine tries to use the new, rejected frequency?
drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 471
Quoted 11 lines
drivers/iio/adc/ltc2378.c +11
-0+ offload_offset_ns +=10 ; + }while (count-- && config->periodic.offset_ns < min_read_offset); + +/* Check the minimum CNV to SCLK delay is met */ +if (config->periodic.offset_ns < min_read_offset) +return -EDOM ; + +/* Check the PWM periods remain the same */ + offload_period_ns =div64_u64 (HZ_PER_GHZ , config->periodic.frequency_hz); +if (cnv_wf.period_length_ns != offload_period_ns) +return -EDOM ;
[Severity: Medium]
Could this strict equality check unintentionally reject valid sample frequencies due to integer math rounding?
If a requested frequency produces a period (cnv_wf.period_length_ns) that does not perfectly divide 1,000,000,000, DIV_ROUND_UP() might round the frequency up. Converting that frequency back to a period via div64_u64() would yield a slightly smaller period.
For example, 133,333 Hz yields a 7500 ns period. 10^9 / 7500 = 133333.33, which rounds up to 133334. Then 10^9 / 133334 = 7499 ns. Since 7500 != 7499, this check would fail and return -EDOM.
Quoted 6 lines
drivers/iio/adc/ltc2378.c +6
-0+ + st->cnv_wf = cnv_wf; + st->cnv_Hz =DIV_ROUND_CLOSEST_ULL (HZ_PER_GHZ , cnv_wf.period_length_ns); + +return 0 ; +}
[ ... ]
drivers/iio/adc/ltc2378.c module_spi_driver(ltc2378_driver); · line 792
Quoted 5 lines
drivers/iio/adc/ltc2378.c +1
-0@@ -412,3 +789,4 @@ module_spi_driver(ltc2378_driver);MODULE_AUTHOR ("Marcelo Schmitt <[email protected]>" );MODULE_DESCRIPTION ("Analog Devices LTC2378 ADC series driver" );MODULE_LICENSE ("GPL" ); +MODULE_IMPORT_NS ("IIO_DMAENGINE_BUFFER" );
[Severity: Low]
Does this driver also need to import the SPI_OFFLOAD namespace?
Since the driver uses symbols from the SPI offload core (like devm_spi_offload_get), it seems it should also include MODULE_IMPORT_NS("SPI_OFFLOAD"); to avoid a modpost warning during strict module builds.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Re: [PATCH v6 3/4] iio: adc: ltc2378: Enable high-speed data capture
Quoted 23 lines · On 07/09, [email protected] wrote:
Quoted 8 lines
drivers/iio/adc/ltc2378.c +8
-0+/* +* Configure SPI offload PWM trigger. +* The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL. +* Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns) +* +* Use the same period as CNV PWM to avoid timing issues. +* Convert back from period to frequency for the SPI offload API. +*/ drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 460
Quoted 10 lines
drivers/iio/adc/ltc2378.c +10
-0+ 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 =100 ; + do { + config->periodic.offset_ns = offload_offset_ns; + ret = spi_offload_trigger_validate(st->offload_trigger, config); + if (ret) + return ret;[Severity: High]
Does modifying config directly leave the device state out of sync if validation fails?
Quoted 9 lines
Here config aliases to
&st->offload_trigger_config, and itsfrequency_hzandoffset_nsfields are mutated. Ifspi_offload_trigger_validate()returns an error, the function returns without restoring the old configuration.However,
st->cnv_wfis only updated at the end of the function. If the buffer is later enabled, could this result in the PWM running with the old frequency while the SPI offload engine tries to use the new, rejected frequency?
Ok, v7 will come with a local variable to hold offload trigger configuration until all checks pass and both st->offload_trigger_config and st->cnv_wf can be updated.
Quoted 16 lines
drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 471
Quoted 11 lines
drivers/iio/adc/ltc2378.c +11
-0+ offload_offset_ns +=10 ; + }while (count-- && config->periodic.offset_ns < min_read_offset); + +/* Check the minimum CNV to SCLK delay is met */ +if (config->periodic.offset_ns < min_read_offset) +return -EDOM ; + +/* Check the PWM periods remain the same */ + offload_period_ns =div64_u64 (HZ_PER_GHZ , config->periodic.frequency_hz); +if (cnv_wf.period_length_ns != offload_period_ns) +return -EDOM ;[Severity: Medium]
Could this strict equality check unintentionally reject valid sample frequencies due to integer math rounding?
Quoted 8 lines
If a requested frequency produces a period (cnv_wf.period_length_ns) that does not perfectly divide 1,000,000,000,
DIV_ROUND_UP()might round the frequency up. Converting that frequency back to a period viadiv64_u64()would yield a slightly smaller period.For example, 133,333 Hz yields a 7500 ns period. 10^9 / 7500 = 133333.33, which rounds up to 133334. Then 10^9 / 133334 = 7499 ns. Since 7500 != 7499, this check would fail and return
-EDOM.
The setup for SPI offload support uses two PWM sources. One PWM drives CNV to periodically start new conversions, the other signals to the SPI controller when it can start fetching data. CNV and trigger PWMs must be offset by some ns. In addition to that, the PWM sources must be synchronized such that the PWM connected to the controller doesn't fire too early nor too late. And, finally, the user might want to run the data capture for an arbitrarily long period of time, requiring the PWM sources to remain synchronized to be able to consistently read valid samples.
Those requirements pretty much imply the PWM sources to have the same period. Though, our peripheral device driver only knows about the SPI controller PWM through the offload_trigger abstraction. After spi_offload_trigger_validate(), the client driver has no way to know whether the new frequency value is a result of integer rounding or PWM hardware not supporting the requested period/frequency. Even though the divergence in the provided example seems to be caused by integer rounding, it might not always be the case.
Quoted 7 lines
drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 477
Quoted 7 lines
drivers/iio/adc/ltc2378.c +6
-0+ + st->cnv_wf = cnv_wf; + st->cnv_Hz =DIV_ROUND_CLOSEST_ULL (HZ_PER_GHZ , cnv_wf.period_length_ns); + +return 0 ; +}
The other comments seem to make sense.
David Lechner
reviewerRe: [PATCH v6 3/4] iio: adc: ltc2378: Enable high-speed data capture
In reply to Marcelo SchmittQuoted 3 lines · Marcelo Schmitt
Make use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 2 MSPS).
...
Quoted 5 lines
drivers/iio/adc/ltc2378.c +5
-0+static int ltc2378_prepare_offload_message (struct device *dev, +struct ltc2378_state *st) +{ +unsigned int resolution = st->info->offload_chan.scan_type.realbits; +
drivers/iio/adc/ltc2378.c static const struct iio_info ltc2378_iio_info = { · line 521
Quoted 10 lines
drivers/iio/adc/ltc2378.c +10
-0+ st->offload_xfer.bits_per_word = resolution; + st->offload_xfer.len =spi_bpw_to_bytes (resolution); + 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); +}
Would be more logical to move this function after buffer stuff.
Quoted 27 lines
drivers/iio/adc/ltc2378.c +27
-0+ +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, true); +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);
drivers/iio/adc/ltc2378.c static const struct iio_info ltc2378_iio_info = { · line 558
Quoted 10 lines
drivers/iio/adc/ltc2378.c +10
-0+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, +}; +
... to here or even below somewhere.
Quoted 24 lines
drivers/iio/adc/ltc2378.c +24
-0+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 ); +} +
...
Quoted 39 lines
drivers/iio/adc/ltc2378.c +33
-2@@ -340,8 +672,53 @@ static int ltc2378_probe(struct spi_device *spi)return dev_err_probe (dev,PTR_ERR (st->cnv_gpio),"failed to get CNV GPIO" ); - indio_dev->channels = &st->info->chan; - indio_dev->num_channels =1 ; + st->offload =devm_spi_offload_get (dev, spi, <c2378_offload_config); + ret =PTR_ERR_OR_ZERO (st->offload); +/* Fall back to low speed usage when no SPI offload is available. */ +if (ret == -ENODEV ) { + indio_dev->info = <c2378_iio_info; + indio_dev->channels = &st->info->chan; + indio_dev->num_channels =1 ; + }else if (ret) { +return dev_err_probe (dev, ret,"failed to get offload\n" ); + }else { + indio_dev->info = <c2378_offload_iio_info; + indio_dev->channels = &st->info->offload_chan; + indio_dev->num_channels =1 ; + 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 dev_err_probe (dev, ret,"failed to get PWM\n" ); + + st->sample_freq_range[0 ] =1 ;/* min */ + st->sample_freq_range[1 ] =1 ;/* step */ + st->sample_freq_range[2 ] = st->info->max_sample_rate_Hz;/* max */ + +/* +* Start with a slower sampling rate so there is some room for +* adjusting the sample averaging and the sampling frequency +* without hitting the maximum conversion rate. +*/ + ret =ltc2378_update_conversion_rate (st, st->info->max_sample_rate_Hz >>4 ); +if (ret)
drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 717
Quoted 10 lines
drivers/iio/adc/ltc2378.c +10
-0+return dev_err_probe (dev, ret, +"failed to set offload samp freq\n" ); + + ret =ltc2378_prepare_offload_message (&spi->dev, st); +if (ret) +return dev_err_probe (dev, ret,"failed to optimize SPI message\n" ); + +/* +* Set single-read transfer bits_per_word so the SPI subsystem +* rearanges data to CPU endianness, enabling us to reuse
s/rearanges/rearranges/
Quoted 12 lines
drivers/iio/adc/ltc2378.c +5
-0+* offload_chan specifications for single-shot reads. +*/ + st->xfer.bits_per_word = st->info->offload_chan.scan_type.realbits; + } st->xfer.rx_buf = &st->scan.data; st->xfer.len =spi_bpw_to_bytes (indio_dev->channels[0 ].scan_type.realbits);@@ -412,3 +789,4 @@ module_spi_driver(ltc2378_driver);MODULE_AUTHOR ("Marcelo Schmitt <[email protected]>" );MODULE_DESCRIPTION ("Analog Devices LTC2378 ADC series driver" );MODULE_LICENSE ("GPL" ); +MODULE_IMPORT_NS ("IIO_DMAENGINE_BUFFER" );
Marcelo Schmitt
maintainerPATCH v6 4/4 iio: adc: ltc2378: Enable triggered buffer data capture
In reply to Marcelo SchmittEnable users to run triggered data captures with LTC2378 and similar ADCs.
---
No changes in v6.
drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ltc2378.c | 75 +++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 23 deletions(-)
drivers/iio/adc/Kconfig +1 -0
@@ -952,6 +952,7 @@ config LTC2378 select IIO_BUFFER select IIO_BUFFER_DMA select IIO_BUFFER_DMAENGINE + select IIO_TRIGGERED_BUFFER select SPI_OFFLOAD select SPI_OFFLOAD_TRIGGER_PWM help
drivers/iio/adc/ltc2378.c +52 -23
225 lines not on this page · raw message
Signature · 1 line
2.53.0
Quoted 7 lines · Marcelo Schmitt
This patch series adds support for LTC2378 and similar low noise, low power, high speed, successive approximation register (SAR) ADCs. These ADCs are similar among each other, varying mainly on the amount of precision bits, maximum sample rate, and input configuration (either fully differential or pseudo-differential).
Patch 1 adds device tree documentation for LTC2378.
Quoted 8 lines
Patch 2 enables single-shot sample read with a GPIO connected to the LTC2378 CNV pin.
Patch 3 enables high-speed data captures with SPI offloading.
The setup is similar to AD4030, with a specialized PWM generator being used both for SPI offload triggering and conversion start signaling.Patch 4 enables running buffered data captures without SPI offloading.
Nice to have this split into reasonable-sized patches. Makes it easier/faster to review.