iio: adc: Add support for LTC2378 and similar ADCs
Marcelo Schmitt
maintainerv2 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).
The initial support patch enables single-shot sample read with a GPIO connected to the CNV pin.
The second support 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.
Some structures and variables are introduced earlier to reduce diff in latter patches.
The initial version of the LTC2378 driver was developed by Ioan-Daniel. Though, despite of the many changes I made to the code, I've kept him as module author to provide credit for his work.
Link to v1: https://lore.kernel.org/linux-iio/[email protected]/
Change log v1 -> v2:
[DT]
- Use unevaluatedProperties:false with spi-peripheral-props.yaml reference/include.
- Use unique compatibles (no fallbacks).
[IIO]
- Added missing includes for LTC2378 driver.
- Added waiting time required before reading back LTC2378 conversion data.
- Fixed voltage regulator read error path.
- Properly right-aligned sample data.
- Set loop count to prevent CPU stall when calculating PWM parameters.
- Hardcode set specific offload supported amount of data element bits.
- Fixed devm_spi_offload_get() error path.
- Dropped device ID table and simplified chip specific information keeping.
- Using named device_id data initializers.
- Use bool for output code type distinction.
- Initialize init scan_type according to buffer selection.
- Added scope for IIO_DEV_ACQUIRE_DIRECT_MODE usage.
- Offload attributes now are channel based instead of device based.
- Use IIO_CHAN_SOFT_TIMESTAMP directly on right hand of assignment.
- Wrapped comments close to 80 columns.
- Organized includes.
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 | 118 +++++ MAINTAINERS | 8 + drivers/iio/adc/Kconfig | 22 + drivers/iio/adc/Makefile | 2 + drivers/iio/adc/ltc2378-offload-buffer.c | 296 ++++++++++++ drivers/iio/adc/ltc2378.c | 454 ++++++++++++++++++ drivers/iio/adc/ltc2378.h | 113 +++++ 7 files changed, 1013 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml create mode 100644 drivers/iio/adc/ltc2378-offload-buffer.c create mode 100644 drivers/iio/adc/ltc2378.c create mode 100644 drivers/iio/adc/ltc2378.h
base-commit: 6f15decff24dac42895c0f01f0c2143f9b8961a6
Signature · 1 line
2.53.0
Marcelo Schmitt
maintainerPATCH v2 1/4 dt-bindings: iio: adc: Add ltc2378
Document how to describe LTC2378-20 and similar ADCs in device tree.
---
Change log v1 -> v2:
- Use unevaluatedProperties:false with spi-peripheral-props.yaml reference/include.
- Use unique compatibles (no fallbacks).
.../bindings/iio/adc/adi,ltc2378.yaml | 118 ++++++++++++++++++ MAINTAINERS | 7 ++ 2 files changed, 125 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml
Documentation/devicetree/bindings/iio/adc/adi,ltc2378.yaml new file +118 -0
@@ -0,0 +1,118 @@ +# 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: + 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 + + 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"; + 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
@@ -15203,6 +15203,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
Re: [PATCH v2 1/4] dt-bindings: iio: adc: Add ltc2378
Quoted 8 lines · Marcelo Schmitt
Document how to describe LTC2378-20 and similar ADCs in device tree.
Signed-off-by Marcelo Schmitt <[email protected]>---
Change log v1 -> v2:
- Use unevaluatedProperties:false withspi-peripheral-props.yamlreference/include.- Use unique compatibles (no fallbacks).
Please note why these do not use fallbacks in your commit message. With that explanation,
pw-bot: changes-requested
Marcelo Schmitt
maintainerPATCH v2 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 v1 -> v2:
- Added missing includes and sorted the include section.
- Added waiting time required before reading back LTC2378 conversion data.
- Fixed voltage regulator read error path.
- Properly right-aligned sample data.
- Dropped device ID table and simplified chip specific information keeping.
- Using named device_id data initializers.
- Use bool for output code type distinction.
- Added scope for IIO_DEV_ACQUIRE_DIRECT_MODE usage.
MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 12 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2378.c | 342 ++++++++++++++++++++++++++++++++++++++ drivers/iio/adc/ltc2378.h | 47 ++++++ 5 files changed, 403 insertions(+) create mode 100644 drivers/iio/adc/ltc2378.c create mode 100644 drivers/iio/adc/ltc2378.h
MAINTAINERS +1 -0
@@ -15209,6 +15209,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* LTC2664 IIO DAC DRIVER M: Michael Hennerich <[email protected]>
drivers/iio/adc/Kconfig +12 -0
@@ -939,6 +939,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 GPIOLIB || PWM + select IIO_BUFFER + 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 +342 -0
342 lines not on this page · raw message
drivers/iio/adc/ltc2378.h new file +47 -0
47 lines not on this page · raw message
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v2 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: - [High] The driver fails to right-shift >16-bit ADC data read via SPI into a 32-bit word, corrupting both…
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] The driver fails to right-shift >16-bit ADC data read via SPI into a 32-bit word, corrupting both the raw value and its sign extension.
- [Medium] The Kconfig specifies a spurious dependency on PWM, which allows the driver to compile without GPIOLIB and silently fail to trigger ADC conversions in direct mode.
- [Medium] The driver explicitly forces bits_per_word to 32 or 16, which arbitrarily restricts compatibility with many SPI controllers.
- [Low] Using the IIO_DEV_ACQUIRE_DIRECT_MODE cleanup macro inside a switch case without a block scope causes a compiler error.
--
commit ffe34f72dbe517b28d007ae50c98ce8f89d8b64c
Author: Marcelo Schmitt <[email protected]>
iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
This commit adds support for the Analog Devices LTC2378-20 and similar analog-to-digital converters to the IIO subsystem.
drivers/iio/adc/Kconfig config LTC2309 · line 943
Quoted 9 lines
drivers/iio/adc/Kconfig +4
-0@@ -939,6 +939,18 @@ config LTC2309 +config LTC2378 + tristate "Analog Devices LTC2378 ADC driver" + depends on SPI + depends on GPIOLIB || PWM
[Severity: Medium]
Does this spurious dependency on PWM allow the driver to compile without GPIOLIB?
If GPIOLIB is disabled, devm_gpiod_get_optional() will return NULL. When ltc2378_convert_and_acquire() subsequently calls gpiod_set_value_cansleep(), it will silently do nothing.
Will this prevent the ADC conversions from being initiated in direct mode, causing the SPI transfer to silently read stale data or zeroes?
[ ... ]
Quoted 3 lines
drivers/iio/adc/ltc2378.c new file +0
-0
[ ... ]
Quoted 8 lines
drivers/iio/adc/ltc2378.c new file +8
-0+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);
drivers/iio/adc/ltc2378.c line 178
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+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 );
[Severity: High]
Are we missing a right-shift for >16-bit ADCs?
When bits_per_word is set to 32, the SPI controller clocks in 32 bits MSB-first and stores them in CPU native endianness. The valid 18 or 20 bits will occupy the upper bits of the 32-bit word, leaving trailing zeroes.
Doesn't passing this unshifted sample directly to sign_extend32() evaluate the wrong bit for the sign extension, and also magnify the returned value by a factor of 2^14?
Should we also populate scan_type.shift in the probe function so userspace buffer consumers don't end up reading unshifted garbage?
Quoted 6 lines
drivers/iio/adc/ltc2378.c new file +6
-0+else + *val = sample; + +return 0 ; +} +
drivers/iio/adc/ltc2378.c line 194
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-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);
[Severity: Low]
Does this missing block scope cause a compiler error?
The IIO_DEV_ACQUIRE_DIRECT_MODE macro expands to a cleanup variable declaration. Because the switch case lacks an enclosing block, jumping to case IIO_CHAN_INFO_SCALE skips the variable's initialization, which compilers like GCC reject.
While I noticed this is fixed in a later patch in the series, doesn't this break git bisectability by causing this specific commit to fail compilation?
Quoted 4 lines
drivers/iio/adc/ltc2378.c new file +4
-0+if (IIO_DEV_ACQUIRE_FAILED (claim)) +return -EBUSY ; + + ret =ltc2378_channel_single_read (chan, st, val);
[ ... ]
Quoted 2 lines
drivers/iio/adc/ltc2378.c new file +2
-0+static int ltc2378_probe (struct spi_device *spi) +{
[ ... ]
drivers/iio/adc/ltc2378.c line 271
Quoted 6 lines
drivers/iio/adc/ltc2378.c new file +6
-0+ st->chans[0 ].scan_type.realbits = st->info->resolution; + st->chans[0 ].scan_type.storagebits = st->info->resolution >16 ?32 :16 ; + + st->xfer.rx_buf = &st->scan.data; + st->xfer.len =BITS_TO_BYTES (st->chans[0 ].scan_type.storagebits); + st->xfer.bits_per_word = st->info->resolution >16 ?32 :16 ;
[Severity: Medium]
Does forcing bits_per_word to 32 or 16 arbitrarily restrict compatibility with standard SPI controllers?
Some standard SPI controllers (like the bcm2835 SPI) only support 8-bit transfers. When 32-bit or 16-bit words are requested, the SPI core's __spi_validate() checks the controller capabilities and fails the transfer with -EINVAL.
Would it be safer to use standard 8-bit transfers and reconstruct the word using get_unaligned_be32()?
Quoted 2 lines
drivers/iio/adc/ltc2378.c new file +2
-0+ + indio_dev->channels = st->chans;
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
Jonathan Cameron
maintainerRe: [PATCH v2 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
In reply to Marcelo SchmittQuoted 3 lines · On Thu, 28 May 2026 12:03:52 -0300 Marcelo Schmitt…
Support for LTC2378-20 and similar analog-to-digital converters.
Signed-off-by Marcelo Schmitt <[email protected]>
As normal, our friendly AI had something to say:
https://sashiko.dev/#/patchset/cover.1779976379.git.marcelo.schmitt%40analog.com
A few other things inline from a fresh read.
drivers/iio/adc/Kconfig config LTC2309 · line 945
Quoted 13 lines
M: Michael Hennerich <[email protected]>
drivers/iio/adc/Kconfig +4
-0@@ -939,6 +939,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 GPIOLIB || PWM
Triggered sashiko:
https://sashiko.dev/#/patchset/cover.1779976379.git.marcelo.schmitt%40analog.com
This dependency is odd enough that I think I'd add a comment on why. Also bring in the dependency on PWM in the patch that needs it not this one.
Will it build without gpiolib? If so I'd prefer that to be a || COMPILE_TEST just to get a little more coverage.
drivers/iio/adc/Kconfig config LTC2309 · line 946
Quoted 1 line
drivers/iio/adc/Kconfig +1
-0+ select IIO_BUFFER
This doesn't seem to be needed yet either.
Quoted 17 lines
drivers/iio/adc/Kconfig +7
-0+ 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. +drivers/iio/adc/ltc2378.c new file +3
-0@@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* +* Analog Devices LTC2378 ADC series driver
drivers/iio/adc/ltc2378.c line 13
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+* +* Copyright (C) 2026 Analog Devices Inc. +* Author: Ioan-Daniel Pop <[email protected]> +* Author: Marcelo Schmitt <[email protected]> +*/ + +#include <linux/bitops.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h>
dev_prink.h missing for dev_err_probe()
Quoted 9 lines
drivers/iio/adc/ltc2378.c new file +9
-0+#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>
drivers/iio/adc/ltc2378.c
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +9
-0+#include <linux/iio/types.h> + +#include "ltc2378.h" +static 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 );
Should we check for errors on setting the gpio?
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+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) +{
drivers/iio/adc/ltc2378.c line 173
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+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;
Sashikio has an interesting theory here that you need a shift. I can't immediately spot why it is wrong. Given we are clocking in 32bits I assume the first res_bits are valid then we get a bunch of garbage after that. Given it's MSB first the SPI controller should put those in the top bits of the resulting u32 with the garbage at the bottom. So shouldn't we shift them down by 32 - resolution or something like that? + in buffered case provide the relevant shift value (in later patches)
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+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 ; +}
drivers/iio/adc/ltc2378.c line 193
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-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 :
case IIO_CHAN_INFO_RAW: {
drivers/iio/adc/ltc2378.c line 203
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+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 ; +
}
As that IIO_DEV_ACQUIRE_DIRECT_MODE() is declaring a local variable so there is something go out of scope and result in cleanup. LLVM moans about this, GCC just does the wrong thing which can in theory at least result in an underflow.
You'll get build bot warnings on this - bug sashiko caught it.
Quoted 14 lines
drivers/iio/adc/ltc2378.c new file +14
-0+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 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->twos_comp) + *val2 = st->info->resolution -1 ; +else + *val2 = st->info->resolution; + +return IIO_VAL_FRACTIONAL_LOG2 ;
drivers/iio/adc/ltc2378.c
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +7
-0+ +default : +return -EINVAL ; + } +} + +MODULE_AUTHOR ("Ioan-Daniel Pop <[email protected]>" );
I'm a bit curious on this - listing an module author but not tags? Maybe provide some context in the patch description or a tag if appropriate.
Quoted 22 lines
drivers/iio/adc/ltc2378.c new file +3
-0+MODULE_AUTHOR ("Marcelo Schmitt <[email protected]>" ); +MODULE_DESCRIPTION ("Analog Devices LTC2378 ADC series driver" ); +MODULE_LICENSE ("GPL" );drivers/iio/adc/ltc2378.h new file +13
-0@@ -0,0 +1,47 @@ +/* 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 <[email protected]> +*/ + +#ifndef __DRIVERS_IIO_ADC_LTC2378_H__ +#define __DRIVERS_IIO_ADC_LTC2378_H__ + +#include <linux/iio/iio.h> +#include <linux/spi/spi.h> +#include <linux/types.h>
drivers/iio/adc/ltc2378.h line 23
Quoted 10 lines
drivers/iio/adc/ltc2378.h new file +10
-0+#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 twos_comp;/* Output code is 2's complement or straight binary */
I think this would be more intuitive if was the terminology of the datasheet so bipolar (vs unipolar). Or is there a case where something more complex is going on like offset binary rather than 2s comp?
Quoted 24 lines
drivers/iio/adc/ltc2378.h new file +24
-0+}; + +struct ltc2378_state { +const struct ltc2378_chip_info *info; +struct gpio_desc *cnv_gpio; +struct spi_device *spi; +struct spi_transfer xfer; +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) ; +}; + +#endif /* __DRIVERS_IIO_ADC_LTC2378_H__ */
Re: [PATCH v2 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
Quoted 7 lines · Jonathan Cameron
Quoted 3 lines · On Thu, 28 May 2026 12:03:52 -0300 Marcelo Schmitt…
Support for LTC2378-20 and similar analog-to-digital converters.
Signed-off-by Marcelo Schmitt <[email protected]>
...
Quoted 11 lines
drivers/iio/adc/Kconfig config LTC2309 · line 945
Quoted 5 lines
drivers/iio/adc/Kconfig +4
-0+config LTC2378 + tristate "Analog Devices LTC2378 ADC driver" + depends on SPI + depends on GPIOLIB || PWMTriggered sashiko:
https://sashiko.dev/#/patchset/cover.1779976379.git.marcelo.schmitt%40analog.com
This dependency is odd enough that I think I'd add a comment on why. Also bring in the dependency on PWM in the patch that needs it not this one.
Quoted 3 lines
Will it build without gpiolib? If so I'd prefer that to be a ||
COMPILE_TESTjust to get a little more coverage.
The intent was to allow using the ADC with offload PWM triggered setup even if the GPIO is not provided/enabled. We would be able to do the buffered reads, but the single-shot read would be debatable since we would very likely trigger many conversions in between a pwm_enable()/pwm_disable(). In the doubt, I left the single-read procedure untouched specially because there's also the case where the GPIO is there and so the single-read would work normally.
+-------------+ +-------------+
| CNV |<-----+--| GPIO |
| | +--| PWM0 |
| | | |
| | +--| PWM1 |
| | | +-------------+
| | +->| TRIGGER |
| | | |
| ADC | | SPI |
| | | controller |
| | | |
| SDI |<--------| SDO |
| SDO |-------->| SDI |
| SCLK |<--------| SCLK |
+-------------+ +-------------+Maybe I'm going too much flexible trying to allow all those combinations? Simplest way to avoid what sashiko points as garbage read is to go straight 'depends on GPIOLIB' and check the GPIO must always be there.
Quoted 1 line
...
Quoted 28 lines
drivers/iio/adc/ltc2378.c line 153
Quoted 6 lines
drivers/iio/adc/ltc2378.c new file +6
-0+static 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 );Should we check for errors on setting the gpio?
Quoted 19 lines
drivers/iio/adc/ltc2378.c new file +19
-0+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; +u32 sample; +int ret; + + ret =ltc2378_convert_and_acquire (st); +if (ret) +return ret; + +if (scan_type->realbits >16 )
Quoted 9 lines
drivers/iio/adc/ltc2378.c line 173
Quoted 1 line
drivers/iio/adc/ltc2378.c new file +1
-0+ sample = st->scan.data.sample_buf32;Sashikio has an interesting theory here that you need a shift. I can't immediately spot why it is wrong. Given we are clocking in 32bits I assume the first
res_bitsare valid then we get a bunch of garbage after that. Given it's MSB first the SPI controller should put those in the top bits of the resulting u32 with the garbage at the bottom. So shouldn't we shift them down by 32 - resolution or something like that? + in buffered case provide the relevant shift value (in later patches)
The only way I'm seing to get sample_buf32 with ADC data in the MSB is if the CPU endianness is BE. Otherwise (LE), the data bits would be put into the lower addresses when SPI puts the word in CPU endianness. But, if we do unconditional shift, won't that break sign_extend32() for LE CPUs? Well, I'll anyway double check the data for all sample_buf16/sample_buf32 buffer/non-buffer use cases.
Quoted 11 lines
Quoted 11 lines
drivers/iio/adc/ltc2378.c new file +11
-0+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 ; +} +
...
Quoted 14 lines
case
IIO_CHAN_INFO_RAW: {drivers/iio/adc/ltc2378.c line 203
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+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 ; +}
Quoted 6 lines
As that
IIO_DEV_ACQUIRE_DIRECT_MODE()is declaring a local variable so there is something go out of scope and result in cleanup. LLVM moans about this, GCC just does the wrong thing which can in theory at least result in an underflow.
You'll get build bot warnings on this - bug sashiko caught it.
Oops I guess I started mis-squashing commits after a certain number of rebases XD Will fix this and all other suggestions.
Thanks
Jonathan Cameron
maintainerRe: [PATCH v2 2/4] iio: adc: ltc2378: Add support for LTC2378-20 and similar ADCs
Quoted 46 lines · On Fri, 29 May 2026 09:37:00 -0300 Marcelo Schmitt…
Quoted 7 lines · Jonathan Cameron
Quoted 3 lines · On Thu, 28 May 2026 12:03:52 -0300 Marcelo Schmitt…
Support for LTC2378-20 and similar analog-to-digital converters.
Signed-off-by Marcelo Schmitt <[email protected]>...
Quoted 11 lines
drivers/iio/adc/Kconfig config LTC2309 · line 945
Quoted 5 lines
drivers/iio/adc/Kconfig +4
-0+config LTC2378 + tristate "Analog Devices LTC2378 ADC driver" + depends on SPI + depends on GPIOLIB || PWMTriggered sashiko:
https://sashiko.dev/#/patchset/cover.1779976379.git.marcelo.schmitt%40analog.com
This dependency is odd enough that I think I'd add a comment on why. Also bring in the dependency on PWM in the patch that needs it not this one.Quoted 3 lines
Will it build without gpiolib? If so I'd prefer that to be a ||
COMPILE_TESTjust to get a little more coverage.The intent was to allow using the ADC with offload PWM triggered setup even if the GPIO is not provided/enabled. We would be able to do the buffered reads, but the single-shot read would be debatable since we would very likely trigger many conversions in between a pwm_enable()/pwm_disable(). In the doubt, I left the single-read procedure untouched specially because there's also the case where the GPIO is there and so the single-read would work normally.
+-------------+ +-------------+ | CNV |<-----+--| GPIO | | | +--| PWM0 | | | | | | | +--| PWM1 | | | | +-------------+ | | +->| TRIGGER | | | | | | ADC | | SPI | | | | controller | | | | | | SDI |<--------| SDO | | SDO |-------->| SDI | | SCLK |<--------| SCLK | +-------------+ +-------------+
Quoted 3 lines
Maybe I'm going too much flexible trying to allow all those combinations? Simplest way to avoid what sashiko points as garbage read is to go straight 'depends on GPIOLIB' and check the GPIO must always be there.
Yes. I'd do that. Almost no one will build without that anyway.
Does the code remove the single read if the gpio isn't available? Sounds like it probably should.
Quoted 40 lines
Quoted 2 lines
...
Quoted 28 lines
drivers/iio/adc/ltc2378.c line 153
Quoted 6 lines
drivers/iio/adc/ltc2378.c new file +6
-0+static 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 );Should we check for errors on setting the gpio?
Quoted 19 lines
drivers/iio/adc/ltc2378.c new file +19
-0+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; +u32 sample; +int ret; + + ret =ltc2378_convert_and_acquire (st); +if (ret) +return ret; + +if (scan_type->realbits >16 )Quoted 9 lines
drivers/iio/adc/ltc2378.c line 173
Quoted 1 line
drivers/iio/adc/ltc2378.c new file +1
-0+ sample = st->scan.data.sample_buf32;Sashikio has an interesting theory here that you need a shift. I can't immediately spot why it is wrong. Given we are clocking in 32bits I assume the first
res_bitsare valid then we get a bunch of garbage after that. Given it's MSB first the SPI controller should put those in the top bits of the resulting u32 with the garbage at the bottom. So shouldn't we shift them down by 32 - resolution or something like that? + in buffered case provide the relevant shift value (in later patches)
Quoted 3 lines
The only way I'm seing to get
sample_buf32with ADC data in the MSB is if the CPU endianness is BE. Otherwise (LE), the data bits would be put into the lower addresses when SPI puts the word in CPU endianness.
We aren't talking addreses here we are talking about handling of a 4 byte s32 (which is also what the sign extend is operating on).
Quoted 3 lines
But, if we do unconditional
shift, won't that breaksign_extend32()for LE CPUs? Well, I'll anyway double check the data for all sample_buf16/sample_buf32 buffer/non-buffer use cases.
It's operating on an s32 so say we extend 0xfabc from bit 15
be is
[0x00] [0x00] [0xfa] [0xbc] => [0xff] [0xff] [0xfa] [0xbc]
le
[0xbc] [0xfa] [0x00] [0x00] => [0xbc] [0xfa] [0xff] [0xff]
Which are both 0xfffffabc as an s32 (in hex obviously)
Arguement in this case is the value is actually in the top few bytes so the ones that are 0 in the example above.
Quoted 28 lines
Quoted 12 lines
Quoted 12 lines
drivers/iio/adc/ltc2378.c new file +11
-0+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 ; +} +...
Quoted 14 lines
case
IIO_CHAN_INFO_RAW: {drivers/iio/adc/ltc2378.c line 203
Quoted 10 lines
drivers/iio/adc/ltc2378.c new file +10
-0+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 ; +}
Quoted 8 lines
Quoted 6 lines
As that
IIO_DEV_ACQUIRE_DIRECT_MODE()is declaring a local variable so there is something go out of scope and result in cleanup. LLVM moans about this, GCC just does the wrong thing which can in theory at least result in an underflow.
You'll get build bot warnings on this - bug sashiko caught it.Oops I guess I started mis-squashing commits after a certain number of rebases XD
Happens to us all. Always do a last sanity check with an LLVM build (it finds way more than GCC currently!) one patch at a time.
Quoted 3 lines
Will fix this and all other suggestions.
Thanks
Marcelo Schmitt
maintainerPATCH v2 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 v1 -> v2:
- Set loop count to prevent CPU stall when calculating PWM parameters.
- Hardcode set specific offload supported amount of data element bits.
- Fixed devm_spi_offload_get() error path.
- Initialize init scan_type according to buffer selection.
- Offload attributes now are channel based instead of device based.
- Wrapped comments close to 80 columns.
I haven't manage to code an is_visible() filter for device attributes specific of offload usage.
Using DEFINE_SYSFS_GROUP_VISIBLE() didn't work out as the sampling_frequency_available attribute would still appear even without offload. Using DEFINE_SYSFS_GROUP_VISIBLE() and sysfs attribute specific visibility function resulted in kernel error (Invalid permissions 01). Instead, I made them channel shared by all attributes.
I tried a lib-like code arrangement like ADIS IMU but didn't manage to get it to work. Separated the code into ltc2378.h, ltc2378.c (lib), ltc2378-core.c (base driver), and ltc2378-offload.c with lib and core built as module and offload.c builtin. Though, I ran into different build and linkage errors depending on how ltc2378-offload.c utilities were encapsulated. Having read through IMU ADIS code, I now think LTC2378 is different and doesn't require a code design that much elaborated. Differently from the separate code for supporting specific adis devices, all ltc2378-like devices can be supported by the same set of functions from ltc2378.c. We can maybe have an ops struct with pointers to buffer specific routines (e.g. offload vs triggered). That might be worth it if we would want to switch between buffer types at runtime. For v2, I'm keeping the module design similar to v1. I appreciate additional examples if a module redesign is still recommended.
drivers/iio/adc/Kconfig | 9 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2378-offload-buffer.c | 296 +++++++++++++++++++++++ drivers/iio/adc/ltc2378.c | 91 ++++++- drivers/iio/adc/ltc2378.h | 66 +++++ 5 files changed, 457 insertions(+), 6 deletions(-) create mode 100644 drivers/iio/adc/ltc2378-offload-buffer.c
drivers/iio/adc/Kconfig +9 -0
@@ -944,6 +944,7 @@ config LTC2378 depends on SPI depends on GPIOLIB || PWM select IIO_BUFFER + select LTC2378_OFFLOAD_BUFFER if SPI_OFFLOAD && PWM && IIO_BUFFER help Say yes here to build support for Analog Devices LTC2378-20 and similar analog to digital converters.
@@ -2027,3 +2028,11 @@ config XILINX_AMS xilinx-ams. endmenu + +config LTC2378_OFFLOAD_BUFFER + bool + select SPI_OFFLOAD_TRIGGER_PWM + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE + help + Say yes here to build support for high speed data capture with LTC2378
drivers/iio/adc/Makefile +1 -0
@@ -82,6 +82,7 @@ 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_LTC2378_OFFLOAD_BUFFER) += ltc2378-offload-buffer.o obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
drivers/iio/adc/ltc2378-offload-buffer.c new file +296 -0
296 lines not on this page · raw message
drivers/iio/adc/ltc2378.c +85 -6
244 lines not on this page · raw message
drivers/iio/adc/ltc2378.h +66 -0
89 lines not on this page · raw message
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v2 3/4] iio: adc: ltc2378: Enable high-speed data capture
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider: - [High] LTC2378_OFFLOAD_BUFFER incorrectly forces tristate dependencies to be built-in and fails to…
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] LTC2378_OFFLOAD_BUFFER incorrectly forces tristate dependencies to be built-in and fails to propagate constraints, causing Kconfig warnings and linker errors.
- [High] Incorrect continuation conditions in hardware configuration loops cause infinite loops or unconditional failures at high sample rates.
- [High] Fallback stub functions in the header contain syntax errors and lack static inline specifiers.
- [High] Direct read SPI transfers execute unintended 32-bit transactions for 16-bit ADCs when offload is supported.
- [High] Uninitialized kernel stack memory is leaked to user-space via the IIO sysfs interface.
- [High] ltc2378_read_avail fails to populate the length parameter for IIO_AVAIL_RANGE, leading to undefined behavior.
- [Medium] dev_err_probe is used to log an error when an optional SPI offload resource is unavailable, polluting kernel logs with false positives. --
commit 1c9aa766f95e380ea9e11f4b2b8e9cbf0718b8f9
Author: Marcelo Schmitt <[email protected]>
Subject: iio: adc: ltc2378: Enable high-speed data capture
Make use of SPI transfer offloading to speed up data capture, enabling data acquisition at faster sample rates (up to 2 MSPS).
drivers/iio/adc/Kconfig +0 -0
[ ... ]
Review
Quoted 8 lines
(quoted diff) +8
-0+ +config LTC2378_OFFLOAD_BUFFER + bool + select SPI_OFFLOAD_TRIGGER_PWM + select IIO_BUFFER_DMA + select IIO_BUFFER_DMAENGINE + help + Say yes here to build support for high speed data capture with LTC2378
[Severity: High]
Should LTC2378_OFFLOAD_BUFFER be a tristate or use depends on instead of select? Since it is a bool, unconditionally selecting tristate options like IIO_BUFFER_DMA can force them to be compiled as built-in even when the parent module is built as a module. Selecting them without inheriting their dependencies can also cause Kconfig warnings.
drivers/iio/adc/ltc2378-offload-buffer.c new file +0 -0
[ ... ]
Quoted 2 lines
(quoted diff) +2
-0+static int ltc2378_update_conversion_rate(struct ltc2378_state *st, int freq_Hz) +{
[ ... ]
Review
Quoted 7 lines
(quoted diff) +7
-0+ do { + cnv_wf.duty_length_ns = target; + ret = pwm_round_waveform_might_sleep(st->cnv_trigger, &cnv_wf); + if (ret) + return ret; + target += 10; /* Increment by PWM duty cycle period */ + } while (cnv_wf.duty_length_ns < LTC2378_TCNV_HIGH_NS || count++ < 100);
[Severity: High]
Does this loop condition evaluate correctly? Due to short-circuit evaluation in C, if the left side of the || is true, count++ is never executed, which might lead to an infinite loop if the PWM hardware caps the value. If the left side is false, the loop will run exactly 100 more times, artificially inflating the target.
[ ... ]
Review
Quoted 7 lines
(quoted diff) +7
-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 (config->periodic.offset_ns < min_read_offset || count++ < 100);
[Severity: High]
This loop condition has the same short-circuit evaluation issue as above.
[ ... ]
Review
Quoted 10 lines
(quoted diff) +10
-0+int ltc2378_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, long mask) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = st->sample_freq_range; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE;
[Severity: High]
Does ltc2378_read_avail need to set the *length pointer here? The IIO core expects the driver to populate it with the number of elements in the returned array. Leaving it unset might lead to out-of-bounds memory accesses when the core processes the range.
[ ... ]
Review
Quoted 10 lines
(quoted diff) +10
-0+int ltc2378_offload_buffer_setup(struct iio_dev *indio_dev, struct spi_device *spi) +{ + struct ltc2378_state *st = iio_priv(indio_dev); + 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 dev_err_probe(dev, ret, "failed to get offload\n");
[Severity: Medium]
Since SPI offload is an optional feature, if devm_spi_offload_get() returns -ENODEV, will dev_err_probe() log a false positive error message to the system log?
drivers/iio/adc/ltc2378.c +0 -0
[ ... ]
Review static int ltc2378_probe(struct spi_device *spi) · line 320
Quoted 12 lines
(quoted diff) +4
-4@@ -261,10 +314,35 @@ static int ltc2378_probe(struct spi_device *spi) st->chans[0].type = IIO_VOLTAGE; st->chans[0].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); - st->chans[0].scan_type.format = st->info->twos_comp ? IIO_SCAN_FORMAT_SIGNED_INT : - IIO_SCAN_FORMAT_UNSIGNED_INT; - st->chans[0].scan_type.realbits = st->info->resolution; - st->chans[0].scan_type.storagebits = st->info->resolution > 16 ? 32 : 16; + + struct iio_scan_type ltc2378_scan; + + ret = ltc2378_offload_buffer_setup(indio_dev, spi);
[Severity: High]
Could this leak uninitialized stack memory to user-space? The ltc2378_scan structure is allocated on the stack and only partially initialized before being assigned to the IIO channel. Uninitialized fields and padding might retain garbage data that gets exposed via sysfs attributes.
[ ... ]
Review static int ltc2378_probe(struct spi_device *spi)
Quoted 5 lines
(quoted diff) +1
-0+ st->chans[0].scan_type = ltc2378_scan; st->xfer.rx_buf = &st->scan.data; st->xfer.len = BITS_TO_BYTES(st->chans[0].scan_type.storagebits); st->xfer.bits_per_word = st->info->resolution > 16 ? 32 : 16;
[Severity: High]
When SPI offload is available, storagebits is forced to 32, meaning st->xfer.len is set to 4 bytes. Since xfer.bits_per_word remains 16 for a 16-bit ADC, could this cause non-offloaded direct reads to execute two 16-bit word reads per transaction, violating the SPI hardware protocol?
drivers/iio/adc/ltc2378.h +0 -0
[ ... ]
Quoted 19 lines
(quoted diff) +19
-0+#else /* CONFIG_IIO_LTC2378_LIB_OFFLOAD_BUFFER */ + +#define LTC2378_WRITE_RAW_PTR (NULL) + +#define LTC2378_READ_AVAIL_PTR (NULL) + +static inline int ltc2378_offload_buffer_setup(struct iio_dev *indio_dev, + struct spi_device *spi) +{ + return -ENODEV; +} + +int ltc2378_get_sampling_frequency(struct ltc2378_state *st, int *val) +{ + return -EOPNOTSUPP; +} + +int ltc2378_set_sampling_frequency(struct ltc2378_state *st, int freq_Hz); +{
Review
Quoted 10 lines
(quoted diff) +10
-0+ return -EOPNOTSUPP; +} + +int ltc2378_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + return -EOPNOTSUPP; +} + +#endif
[Severity: High]
Are there build issues with these fallback functions? The function ltc2378_set_sampling_frequency has a trailing semicolon after its parameter list. Additionally, these functions lack the static inline qualifier, which might cause multiple definition linker errors if the header is included in multiple files.
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
Jonathan Cameron
maintainerRe: [PATCH v2 3/4] iio: adc: ltc2378: Enable high-speed data capture
In reply to Marcelo SchmittQuoted 13 lines · On Thu, 28 May 2026 12:04:12 -0300 Marcelo Schmitt…
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 <[email protected]>---
Change log v1 -> v2:
- Set loop count to prevent CPU stall when calculating PWM parameters.
- Hardcode set specific offload supported amount of data element bits.
- Fixeddevm_spi_offload_get()error path.
- Initialize initscan_typeaccording to buffer selection.
- Offload attributes now are channel based instead of device based.
- Wrapped comments close to 80 columns.
Quoted 7 lines
I haven't manage to code an
is_visible()filter for device attributes specific of offload usage.
UsingDEFINE_SYSFS_GROUP_VISIBLE()didn't work out as thesampling_frequency_availableattribute would still appear even without offload. UsingDEFINE_SYSFS_GROUP_VISIBLE()and sysfs attribute specific visibility function resulted in kernel error (Invalid permissions 01). Instead, I made them channel shared by all attributes.
Yeah, we don't provide a way to do is_visible for those attribute groups because may of the ones the driver provides undergo non trivial transformations. Normal solution is to just provide two versions of the structure that has the attributes pointer and pick between them.
Quoted 14 lines
I tried a lib-like code arrangement like ADIS IMU but didn't manage to get it to work. Separated the code into
ltc2378.h,ltc2378.c(lib),ltc2378-core.c(base driver), andltc2378-offload.cwith lib and core built as module andoffload.cbuiltin. Though, I ran into different build and linkage errors depending on howltc2378-offload.cutilities were encapsulated. Having read through IMU ADIS code, I now think LTC2378 is different and doesn't require a code design that much elaborated. Differently from the separate code for supporting specific adis devices, all ltc2378-like devices can be supported by the same set of functions fromltc2378.c. We can maybe have an ops struct with pointers to buffer specific routines (e.g. offload vs triggered). That might be worth it if we would want to switch between buffer types at runtime. For v2, I'm keeping the module design similar to v1. I appreciate additional examples if a module redesign is still recommended.
For the module design just make sure every combination builds as expected. Sashiko has some views on this. I haven't checked if they are right or not.
A few comments from me + some comments on sashiko's feedback:
https://sashiko.dev/#/patchset/cover.1779976379.git.marcelo.schmitt%40analog.com
Quoted 28 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +22
-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 =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
drivers/iio/adc/ltc2378-offload-buffer.c line 74
Quoted 10 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +10
-0+* 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 (cnv_wf.duty_length_ns <LTC2378_TCNV_HIGH_NS || count++ <100 );
Sashiko is confused by the loop condition. If the intent is not && count++ < 100 add a comment on why.
Quoted 11 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +11
-0+ +/* +* Configure SPI offload PWM trigger. +* The trigger should fire after tBUSYLH + tCONV + tDSDOBUSYL. +* Minimum time needed: TBUSYLH (13ns) + TCONV (part-specific) + TDSDOBUSYL (5ns) +* +* Use the same period as CNV PWM to avoid timing issues. +* Convert back from period to frequency for the SPI offload API. +*/ + offload_period_ns = cnv_wf.period_length_ns; + config->periodic.frequency_hz =DIV_ROUND_UP (HZ_PER_GHZ , offload_period_ns);
drivers/iio/adc/ltc2378-offload-buffer.c line 95
Quoted 10 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +10
-0+ min_read_offset =LTC2378_TBUSYLH_NS + st->info->tconv_ns +LTC2378_TDSDOBUSYL_NS ; + offload_offset_ns = min_read_offset; + count =0 ; +do { + config->periodic.offset_ns = offload_offset_ns; + ret =spi_offload_trigger_validate (st->offload_trigger, config); +if (ret) +return ret; + offload_offset_ns +=10 ; + }while (config->periodic.offset_ns < min_read_offset || count++ <100 );
Same here.
Quoted 7 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +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-offload-buffer.c line 125
Quoted 10 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +9
-0+int ltc2378_read_avail (struct iio_dev *indio_dev,struct iio_chan_spec const *chan, +const int **vals,int *type,int *length,long mask) +{ +struct ltc2378_state *st =iio_priv (indio_dev); + +switch (mask) { +case IIO_CHAN_INFO_SAMP_FREQ : + *vals = st->sample_freq_range; + *type =IIO_VAL_INT ;
Sashiko comments on setting *length here. It's wrong.
Quoted 10 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +7
-0+return IIO_AVAIL_RANGE ; +default : +return -EINVAL ; + } +} +EXPORT_SYMBOL_NS_GPL (ltc2378_read_avail,"IIO_LTC2378" ); +
drivers/iio/adc/ltc2378-offload-buffer.c line 245
Quoted 10 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +10
-0+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" ); + +pwm_disable (st->cnv_trigger);
Why do we need to disable it? I guess something else might have left it running. Perhaps a comment?
Quoted 9 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +9
-0+ +return 0 ; +} + +static const struct spi_offload_config ltc2378_offload_config = { + .capability_flags =SPI_OFFLOAD_CAP_TRIGGER | +SPI_OFFLOAD_CAP_RX_STREAM_DMA , +}; +
drivers/iio/adc/ltc2378-offload-buffer.c line 264
Quoted 10 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +10
-0+int ltc2378_offload_buffer_setup (struct iio_dev *indio_dev,struct spi_device *spi) +{ +struct ltc2378_state *st =iio_priv (indio_dev); +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 dev_err_probe (dev, ret,"failed to get offload\n" );
Sashiko observes this may print which we probably don't want given we are falling back to no offload which is an entirely valid condition.
Quoted 48 lines
drivers/iio/adc/ltc2378-offload-buffer.c new file +32
-0+ + 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; + + 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 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 ; +} +EXPORT_SYMBOL_NS_GPL (ltc2378_offload_buffer_setup,"IIO_LTC2378" ); + +MODULE_IMPORT_NS ("IIO_LTC2378" );drivers/iio/adc/ltc2378.c +1
-1@@ -190,7 +230,7 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev,int ret;switch (info) { -case IIO_CHAN_INFO_RAW : +case IIO_CHAN_INFO_RAW : {IIO_DEV_ACQUIRE_DIRECT_MODE (indio_dev, claim);if (IIO_DEV_ACQUIRE_FAILED (claim))return -EBUSY ;
drivers/iio/adc/ltc2378.c static int ltc2378_read_raw(struct iio_dev *indio_dev, · line 243
Quoted 5 lines
drivers/iio/adc/ltc2378.c +1
-1@@ -200,7 +240,7 @@ static int ltc2378_read_raw(struct iio_dev *indio_dev,return ret;return IIO_VAL_INT ; - + }
Ah. There they are. Move back to previous patch.
Quoted 5 lines
drivers/iio/adc/ltc2378.c +0
-0case IIO_CHAN_INFO_SCALE :static int
ltc2378_probe(struct spi_device *spi)
drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 318
Quoted 9 lines
drivers/iio/adc/ltc2378.c +2
-4@@ -261,10 +314,35 @@ static int ltc2378_probe(struct spi_device *spi) st->chans[0 ].type =IIO_VOLTAGE ; st->chans[0 ].info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) |BIT (IIO_CHAN_INFO_SCALE ); - st->chans[0 ].scan_type.format = st->info->twos_comp ?IIO_SCAN_FORMAT_SIGNED_INT : -IIO_SCAN_FORMAT_UNSIGNED_INT ; - st->chans[0 ].scan_type.realbits = st->info->resolution; - st->chans[0 ].scan_type.storagebits = st->info->resolution >16 ?32 :16 ; + +struct iio_scan_type ltc2378_scan;
= { };
Sashiko noted this - you only set some parts of this below. Or use...
drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 327
Quoted 9 lines
drivers/iio/adc/ltc2378.c +9
-0+ + ret =ltc2378_offload_buffer_setup (indio_dev, spi); +if (ret == -ENODEV ) { +/* SPI offloading is unavailable. Fall back to triggered buffer. */ +dev_dbg (dev,"triggered data capture not supported\n" ); + ltc2378_scan.format = st->info->twos_comp ?IIO_SCAN_FORMAT_SIGNED_INT : +IIO_SCAN_FORMAT_UNSIGNED_INT ; + ltc2378_scan.realbits = st->info->resolution; + ltc2378_scan.storagebits = st->info->resolution >16 ?32 :16 ;
ltc2378_scan = (struct iio_scan_type) {
.format = ...
};here and same below. That will both perhaps be more readable and also ensure all elements are set to what we want (appropriate defaults for some).
drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 337
Quoted 10 lines
drivers/iio/adc/ltc2378.c +10
-0+ }else if (ret) { +return dev_err_probe (dev, ret,"error on SPI offload setup\n" ); + }else { +/* +* Currently, the available offload hardware + DMA configuration +* only supports pushing 32-bit data elements to IIO buffers in +* CPU endianness. +*/ + st->chans[0 ].info_mask_shared_by_all =BIT (IIO_CHAN_INFO_SAMP_FREQ ); + st->chans[0 ].info_mask_shared_by_all_available =BIT (IIO_CHAN_INFO_SAMP_FREQ );
So this is the thing you commented on above. Hmm. Normally when sampling frequency
is effectively decoupled from that possible via direct read (if we assume no software overhead) then we do put this as a buffer attribute.
I'm not sure it matters particularly here though as we don't get to control direct read sampling frequency (which usually has something to do with integration time or similar). So I think this solution is ok.
drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 348
Quoted 11 lines
drivers/iio/adc/ltc2378.c +8
-0+ + ltc2378_scan.format = st->info->twos_comp ?IIO_SCAN_FORMAT_SIGNED_INT : +IIO_SCAN_FORMAT_UNSIGNED_INT ; + ltc2378_scan.realbits = st->info->resolution; + ltc2378_scan.storagebits =32 ; + } + + st->chans[0 ].scan_type = ltc2378_scan; st->xfer.rx_buf = &st->scan.data; st->xfer.len =BITS_TO_BYTES (st->chans[0 ].scan_type.storagebits);
From sashiko:
(adding some more context)
Quoted 1 line
st->xfer.bits_per_word=st->info->resolution> 16 ? 32 : 16;
What happens if there are any direct reads when offload is in use. You set the bpw to 16 for low res parts yet I think the offload hardware always does 32 - so this probably wants an update just to keep code and what the hardware is doing inline.
drivers/iio/adc/ltc2378.h struct ltc2378_state { · line 85
Quoted 13 lines
drivers/iio/adc/ltc2378.c +1
-0@@ -340,3 +418,4 @@ MODULE_AUTHOR("Ioan-Daniel Pop <[email protected]>");MODULE_AUTHOR ("Marcelo Schmitt <[email protected]>" );MODULE_DESCRIPTION ("Analog Devices LTC2378 ADC series driver" );MODULE_LICENSE ("GPL" ); +MODULE_IMPORT_NS ("IIO_LTC2378" );drivers/iio/adc/ltc2378.h +3
-0+#else /* CONFIG_IIO_LTC2378_LIB_OFFLOAD_BUFFER */ + +#define LTC2378_WRITE_RAW_PTR (NULL)
Not sure why you'd ever need brackets around the NULL.
drivers/iio/adc/ltc2378.h struct ltc2378_state { · line 100
Quoted 10 lines
drivers/iio/adc/ltc2378.h +8
-0+ +#define LTC2378_READ_AVAIL_PTR (NULL) +int ltc2378_get_sampling_frequency (struct ltc2378_state *st,int *val) +{ +return -EOPNOTSUPP ; +} + +int ltc2378_set_sampling_frequency (struct ltc2378_state *st,int freq_Hz);
Sashiko noted this - which rather implies you didn't build with these stubs. Stray ;
Quoted 3 lines
drivers/iio/adc/ltc2378.h +3
-0+{ +return -EOPNOTSUPP ; +}
Marcelo Schmitt
maintainerPATCH v2 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.
---
Change log v1 -> v2:
- Initialize init scan_type according to buffer selection.
- Use IIO_CHAN_SOFT_TIMESTAMP directly on right hand of assignment.
drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/ltc2378.c | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-)
drivers/iio/adc/Kconfig +1 -0
@@ -945,6 +945,7 @@ config LTC2378 depends on GPIOLIB || PWM select IIO_BUFFER select LTC2378_OFFLOAD_BUFFER if SPI_OFFLOAD && PWM && IIO_BUFFER + select IIO_TRIGGERED_BUFFER help Say yes here to build support for Analog Devices LTC2378-20 and similar analog to digital converters.
drivers/iio/adc/ltc2378.c +34 -1
@@ -18,8 +18,12 @@#include <linux/regulator/consumer.h> #include <linux/spi/spi.h> #include <linux/types.h> +#include <linux/units.h> +#include <linux/iio/buffer.h> #include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #include <linux/iio/types.h> #include "ltc2378.h"
@@ -278,6 +282,25 @@ static const struct iio_info ltc2378_iio_info = { .read_avail = LTC2378_READ_AVAIL, }; +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_probe (struct spi_device *spi) {struct device *dev = &spi->dev;
@@ -314,17 +337,27 @@ static int ltc2378_probe(struct spi_device *spi) st->chans[0 ].type =IIO_VOLTAGE ; st->chans[0 ].info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) |BIT (IIO_CHAN_INFO_SCALE ); + st->chans[0 ].scan_index =0 ;struct iio_scan_type ltc2378_scan; ret =ltc2378_offload_buffer_setup (indio_dev, spi);if (ret == -ENODEV ) {/* SPI offloading is unavailable. Fall back to triggered buffer. */ -dev_dbg (dev,"triggered data capture not supported\n" ); ltc2378_scan.format = st->info->twos_comp ?IIO_SCAN_FORMAT_SIGNED_INT :IIO_SCAN_FORMAT_UNSIGNED_INT ; ltc2378_scan.realbits = st->info->resolution; ltc2378_scan.storagebits = st->info->resolution >16 ?32 :16 ; + + ret =devm_iio_triggered_buffer_setup (dev, indio_dev, + &iio_pollfunc_store_time, + <c2378_trigger_handler, +NULL ); +if (ret) +return ret; + +/* Add timestamp channel */ + st->chans[num_iio_chans++] =IIO_CHAN_SOFT_TIMESTAMP (1 ); }else if (ret) {return dev_err_probe (dev, ret,"error on SPI offload setup\n" ); }else {
Signature · 1 line
2.53.0
[email protected]
AI reviewRe: [PATCH v2 4/4] iio: adc: ltc2378: Enable triggered buffer data capture
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [High] The `ltc2378_scan` structure is allocated on the stack but not fully initialized, leaving fields…
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The ltc2378_scan structure is allocated on the stack but not fully initialized, leaving fields like endianness, shift, and repeat with garbage values. This leads to out-of-bounds array reads in the IIO core, data corruption, and potential information disclosure. --
commit 9e45014ec9c648942b0872f2dcaabf867779b73e
Author: Marcelo Schmitt <[email protected]>
iio: adc: ltc2378: Enable triggered buffer data capture
This commit adds support for triggered buffer data captures to the LTC2378 ADC driver. It implements a trigger handler and configures the scan type for both SPI offload and fallback triggered buffer cases.
Quoted 4 lines
drivers/iio/adc/ltc2378.c +0
-0
[ ... ]
Quoted 6 lines
drivers/iio/adc/ltc2378.c +1
-0@@ -314,17 +337,27 @@ static int ltc2378_probe(struct spi_device *spi) st->chans[0 ].type =IIO_VOLTAGE ; st->chans[0 ].info_mask_separate =BIT (IIO_CHAN_INFO_RAW ) |BIT (IIO_CHAN_INFO_SCALE ); + st->chans[0 ].scan_index =0 ;
drivers/iio/adc/ltc2378.c static int ltc2378_probe(struct spi_device *spi) · line 350
Quoted 10 lines
drivers/iio/adc/ltc2378.c +0
-1struct iio_scan_type ltc2378_scan; ret =ltc2378_offload_buffer_setup (indio_dev, spi);if (ret == -ENODEV ) {/* SPI offloading is unavailable. Fall back to triggered buffer. */ -dev_dbg (dev,"triggered data capture not supported\n" ); ltc2378_scan.format = st->info->twos_comp ?IIO_SCAN_FORMAT_SIGNED_INT :IIO_SCAN_FORMAT_UNSIGNED_INT ; ltc2378_scan.realbits = st->info->resolution; ltc2378_scan.storagebits = st->info->resolution >16 ?32 :16 ;
[Severity: High]
Does this leave fields in ltc2378_scan uninitialized?
It looks like ltc2378_scan is allocated on the stack but not fully initialized. Only format, realbits, and storagebits are assigned here, leaving fields like endianness, shift, and repeat with stack garbage.
When st->chans[0].scan_type is later assigned from this struct, the uninitialized endianness field could cause an out-of-bounds array read in iio_show_scan_type() when indexing into iio_endian_prefix.
The garbage repeat value could also result in miscalculated IIO buffer chunk sizes.
Could ltc2378_scan be zero-initialized to prevent these issues?
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=4
Jonathan Cameron
maintainerRe: [PATCH v2 4/4] iio: adc: ltc2378: Enable triggered buffer data capture
In reply to Marcelo SchmittQuoted 3 lines · On Thu, 28 May 2026 12:04:37 -0300 Marcelo Schmitt…
Enable users to run triggered data captures with LTC2378 and similar ADCs.
Signed-off-by Marcelo Schmitt <[email protected]>
LGTM. Only sashiko comment was actually on code in previous patch.
Quoted 12 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).
The initial support patch enables single-shot sample read with a GPIO connected to the CNV pin.
The second support 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.
Quoted 7 lines
The last support patch enables running buffered data captures without SPI offloading.
Some structures and variables are introduced earlier to reduce diff in latter patches.
The initial version of the LTC2378 driver was developed by Ioan-Daniel. Though, despite of the many changes I made to the code, I've kept him as module author to provide credit for his work.
No datasheet link, no explanation why the brand new driver is needed. No need to resend, just answer the above questions.
Quoted 1 line
Link to v1: https://lore.kernel.org/linux-iio/[email protected]/
Signature · 2 lines
With Best Regards, Andy Shevchenko
Quoted 15 lines · Andy Shevchenko
Quoted 14 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).
The initial support patch enables single-shot sample read with a GPIO connected to the CNV pin.
The second support 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.
Quoted 8 lines
Quoted 5 lines
Some structures and variables are introduced earlier to reduce diff in latter patches.
The initial version of the LTC2378 driver was developed by Ioan-Daniel. Though, despite of the many changes I made to the code, I've kept him as module author to provide credit for his work.
No datasheet link, no explanation why the brand new driver is needed. No need to resend, just answer the above questions.
Hello Andy, thank you for having a look at this set.
In short, this new driver is needed for supporting LTC2378-20 and similar ADCs. Even though these parts are somewhat similar to AD4000, the available HDL for high speed sample rate mode is different and the wiring configuration is also different. Because of that, I think cramming LTC2378 support into AD4000 driver might not be a good idea. I'll add a note about that in v3 together with datasheet links.
There are some good points that Jonathan and sashiko raised in v2 and I'm still working out a solution for some of them. Will share a v3 after tackling out the remaining issues.
Thanks,
Marcelo
Quoted 6 lines
Quoted 2 lines
Link to v1: https://lore.kernel.org/linux-iio/[email protected]/
Signature · 2 lines
With Best Regards, Andy Shevchenko