* [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC
@ 2026-03-28 11:40 Antoniu Miclaus
2026-03-28 11:40 ` [PATCH v8 1/3] iio: backend: add devm_iio_backend_get_by_index() Antoniu Miclaus
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Antoniu Miclaus @ 2026-03-28 11:40 UTC (permalink / raw)
To: Lars-Peter Clausen, Michael Hennerich, Antoniu Miclaus,
Jonathan Cameron, David Lechner, Nuno Sá, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel
Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with
integrated fully differential amplifiers (FDA).
Architecture notes:
The AD4880 is modeled as a single IIO device rather than two independent
devices because the channels share power supplies, a voltage reference,
the CNV conversion clock, and a single interleaved data output stream.
Splitting them into separate IIO devices would make synchronized
dual-channel capture impossible from userspace.
An MFD approach does not apply here either - the channels are not
functionally distinct sub-devices but identical ADC paths sharing a
common data interface.
Each channel has fully independent configuration registers accessible
through separate SPI chip selects, so per-channel regmaps are used with
no locking between them. The data path has no software involvement at
runtime: the CNV clock triggers simultaneous conversions and the device
outputs an interleaved bitstream captured directly by the IIO backend
(FPGA). spi_new_ancillary_device() handles the configuration path;
the IIO backend handles the data path.
The debugfs_reg_access callback is not exposed for the dual-channel
variant since the IIO framework provides a single (reg, val) interface
with no channel parameter, and exposing only one channel would be
misleading.
The AD4880 is a fairly unique part - having separate SPI config
interfaces per channel with a shared interleaved data output is not
a common pattern.
Changes in v8:
- Drop fwnode_handle cleanup patch (now in jic23/testing)
- Clarify backend buffer comment to describe FPGA architecture
(two axi_ad408x IP instances with a packer block)
- Make filter_type a per-channel array instead of a single variable
- Restore debugfs_reg_access for AD4880 (uses channel 0 regmap),
based on sashiko's review
Antoniu Miclaus (3):
iio: backend: add devm_iio_backend_get_by_index()
dt-bindings: iio: adc: ad4080: add AD4880 support
iio: adc: ad4080: add support for AD4880 dual-channel ADC
.../bindings/iio/adc/adi,ad4080.yaml | 53 +++-
drivers/iio/adc/ad4080.c | 251 ++++++++++++++----
drivers/iio/industrialio-backend.c | 53 ++--
include/linux/iio/backend.h | 1 +
4 files changed, 282 insertions(+), 76 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v8 1/3] iio: backend: add devm_iio_backend_get_by_index() 2026-03-28 11:40 [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus @ 2026-03-28 11:40 ` Antoniu Miclaus 2026-03-28 11:40 ` [PATCH v8 2/3] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus ` (2 subsequent siblings) 3 siblings, 0 replies; 9+ messages in thread From: Antoniu Miclaus @ 2026-03-28 11:40 UTC (permalink / raw) To: Lars-Peter Clausen, Michael Hennerich, Antoniu Miclaus, Jonathan Cameron, David Lechner, Nuno Sá, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio, devicetree, linux-kernel Add a new function to get an IIO backend by its index in the io-backends device tree property. This is useful for multi-channel devices that have multiple backends, where looking up by index is more straightforward than using named backends. Extract __devm_iio_backend_fwnode_get_by_index() from the existing __devm_iio_backend_fwnode_get(), taking the index directly as a parameter. The new public API devm_iio_backend_get_by_index() uses the index to find the backend reference in the io-backends property, avoiding the need for io-backend-names. Reviewed-by: David Lechner <dlechner@baylibre.com> Reviewed-by: Nuno Sá <nuno.sa@analog.com> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com> --- Changes in v8: - No changes drivers/iio/industrialio-backend.c | 53 +++++++++++++++++++++--------- include/linux/iio/backend.h | 1 + 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 58f7e1426095..d6e922c65347 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -949,23 +949,13 @@ int iio_backend_data_transfer_addr(struct iio_backend *back, u32 address) } EXPORT_SYMBOL_NS_GPL(iio_backend_data_transfer_addr, "IIO_BACKEND"); -static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, - struct fwnode_handle *fwnode) +static struct iio_backend *__devm_iio_backend_fwnode_get_by_index(struct device *dev, + struct fwnode_handle *fwnode, + unsigned int index) { struct iio_backend *back; - unsigned int index; int ret; - if (name) { - ret = device_property_match_string(dev, "io-backend-names", - name); - if (ret < 0) - return ERR_PTR(ret); - index = ret; - } else { - index = 0; - } - struct fwnode_handle *fwnode_back __free(fwnode_handle) = fwnode_find_reference(fwnode, "io-backends", index); if (IS_ERR(fwnode_back)) @@ -981,8 +971,7 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con if (ret) return ERR_PTR(ret); - if (name) - back->idx = index; + back->idx = index; return back; } @@ -990,6 +979,24 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con return ERR_PTR(-EPROBE_DEFER); } +static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, const char *name, + struct fwnode_handle *fwnode) +{ + unsigned int index; + int ret; + + if (name) { + ret = device_property_match_string(dev, "io-backend-names", name); + if (ret < 0) + return ERR_PTR(ret); + index = ret; + } else { + index = 0; + } + + return __devm_iio_backend_fwnode_get_by_index(dev, fwnode, index); +} + /** * devm_iio_backend_get - Device managed backend device get * @dev: Consumer device for the backend @@ -1006,6 +1013,22 @@ struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name) } EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, "IIO_BACKEND"); +/** + * devm_iio_backend_get_by_index - Device managed backend device get by index + * @dev: Consumer device for the backend + * @index: Index of the backend in the io-backends property + * + * Gets the backend at @index associated with @dev. + * + * RETURNS: + * A backend pointer, negative error pointer otherwise. + */ +struct iio_backend *devm_iio_backend_get_by_index(struct device *dev, unsigned int index) +{ + return __devm_iio_backend_fwnode_get_by_index(dev, dev_fwnode(dev), index); +} +EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get_by_index, "IIO_BACKEND"); + /** * devm_iio_backend_fwnode_get - Device managed backend firmware node get * @dev: Consumer device for the backend diff --git a/include/linux/iio/backend.h b/include/linux/iio/backend.h index 7f815f3fed6a..ce2263d4c2de 100644 --- a/include/linux/iio/backend.h +++ b/include/linux/iio/backend.h @@ -237,6 +237,7 @@ int iio_backend_extend_chan_spec(struct iio_backend *back, struct iio_chan_spec *chan); void *iio_backend_get_priv(const struct iio_backend *conv); struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name); +struct iio_backend *devm_iio_backend_get_by_index(struct device *dev, unsigned int index); struct iio_backend *devm_iio_backend_fwnode_get(struct device *dev, const char *name, struct fwnode_handle *fwnode); -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v8 2/3] dt-bindings: iio: adc: ad4080: add AD4880 support 2026-03-28 11:40 [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus 2026-03-28 11:40 ` [PATCH v8 1/3] iio: backend: add devm_iio_backend_get_by_index() Antoniu Miclaus @ 2026-03-28 11:40 ` Antoniu Miclaus 2026-03-31 15:44 ` Rob Herring 2026-03-28 11:40 ` [PATCH v8 3/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus 2026-04-12 18:33 ` [PATCH v8 0/3] " Jonathan Cameron 3 siblings, 1 reply; 9+ messages in thread From: Antoniu Miclaus @ 2026-03-28 11:40 UTC (permalink / raw) To: Lars-Peter Clausen, Michael Hennerich, Antoniu Miclaus, Jonathan Cameron, David Lechner, Nuno Sá, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio, devicetree, linux-kernel Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with integrated fully differential amplifiers (FDA). The AD4880 has two independent ADC channels, each with its own SPI configuration interface. This requires: - Two entries in reg property for primary and secondary channel chip selects - Two io-backends entries for the two data channels Reviewed-by: David Lechner <dlechner@baylibre.com> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com> --- Changes in v8: - No changes .../bindings/iio/adc/adi,ad4080.yaml | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml index ccd6a0ac1539..0cf86c6f9925 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml @@ -18,7 +18,11 @@ description: | service a wide variety of precision, wide bandwidth data acquisition applications. + The AD4880 is a dual-channel variant with two independent ADC channels, + each with its own SPI configuration interface. + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4080.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad4880.pdf $ref: /schemas/spi/spi-peripheral-props.yaml# @@ -31,9 +35,15 @@ properties: - adi,ad4084 - adi,ad4086 - adi,ad4087 + - adi,ad4880 reg: - maxItems: 1 + minItems: 1 + maxItems: 2 + description: + SPI chip select(s). For single-channel devices, one chip select. + For multi-channel devices like AD4880, two chip selects are required + as each channel has its own SPI configuration interface. spi-max-frequency: description: Configuration of the SPI bus. @@ -57,7 +67,10 @@ properties: vrefin-supply: true io-backends: - maxItems: 1 + minItems: 1 + items: + - description: Backend for channel A (primary) + - description: Backend for channel B (secondary) adi,lvds-cnv-enable: description: Enable the LVDS signal type on the CNV pin. Default is CMOS. @@ -78,6 +91,25 @@ required: - vdd33-supply - vrefin-supply +allOf: + - if: + properties: + compatible: + contains: + const: adi,ad4880 + then: + properties: + reg: + minItems: 2 + io-backends: + minItems: 2 + else: + properties: + reg: + maxItems: 1 + io-backends: + maxItems: 1 + additionalProperties: false examples: @@ -98,4 +130,21 @@ examples: io-backends = <&iio_backend>; }; }; + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad4880"; + reg = <0>, <1>; + spi-max-frequency = <10000000>; + vdd33-supply = <&vdd33>; + vddldo-supply = <&vddldo>; + vrefin-supply = <&vrefin>; + clocks = <&cnv>; + clock-names = "cnv"; + io-backends = <&iio_backend_cha>, <&iio_backend_chb>; + }; + }; ... -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v8 2/3] dt-bindings: iio: adc: ad4080: add AD4880 support 2026-03-28 11:40 ` [PATCH v8 2/3] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus @ 2026-03-31 15:44 ` Rob Herring 0 siblings, 0 replies; 9+ messages in thread From: Rob Herring @ 2026-03-31 15:44 UTC (permalink / raw) To: Antoniu Miclaus Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron, David Lechner, Nuno Sá, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio, devicetree, linux-kernel On Sat, Mar 28, 2026 at 01:40:49PM +0200, Antoniu Miclaus wrote: > Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC > with integrated fully differential amplifiers (FDA). > > The AD4880 has two independent ADC channels, each with its own SPI > configuration interface. This requires: > - Two entries in reg property for primary and secondary channel > chip selects > - Two io-backends entries for the two data channels > > Reviewed-by: David Lechner <dlechner@baylibre.com> > Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com> > --- > Changes in v8: > - No changes Missing Conor's ack. ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v8 3/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC 2026-03-28 11:40 [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus 2026-03-28 11:40 ` [PATCH v8 1/3] iio: backend: add devm_iio_backend_get_by_index() Antoniu Miclaus 2026-03-28 11:40 ` [PATCH v8 2/3] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus @ 2026-03-28 11:40 ` Antoniu Miclaus 2026-04-12 18:33 ` [PATCH v8 0/3] " Jonathan Cameron 3 siblings, 0 replies; 9+ messages in thread From: Antoniu Miclaus @ 2026-03-28 11:40 UTC (permalink / raw) To: Lars-Peter Clausen, Michael Hennerich, Antoniu Miclaus, Jonathan Cameron, David Lechner, Nuno Sá, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio, devicetree, linux-kernel Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with integrated fully differential amplifiers (FDA). The AD4880 has two independent ADC channels, each with its own SPI configuration interface. The driver uses spi_new_ancillary_device() to create an additional SPI device for the second channel, allowing both channels to share the same SPI bus with different chip selects. Reviewed-by: David Lechner <dlechner@baylibre.com> Reviewed-by: Nuno Sá <nuno.sa@analog.com> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com> --- Changes in v8: - Clarify backend buffer comment to describe FPGA architecture (two axi_ad408x IP instances with a packer block) - Make filter_type a per-channel array instead of a single variable - Restore debugfs_reg_access for AD4880 (uses channel 0 regmap), based on sashiko's review drivers/iio/adc/ad4080.c | 251 ++++++++++++++++++++++++++++++--------- 1 file changed, 192 insertions(+), 59 deletions(-) diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c index 7cf3b6ed7940..fcd29e94107f 100644 --- a/drivers/iio/adc/ad4080.c +++ b/drivers/iio/adc/ad4080.c @@ -16,6 +16,7 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> @@ -131,6 +132,9 @@ #define AD4084_CHIP_ID 0x0054 #define AD4086_CHIP_ID 0x0056 #define AD4087_CHIP_ID 0x0057 +#define AD4880_CHIP_ID 0x0750 + +#define AD4080_MAX_CHANNELS 2 #define AD4080_LVDS_CNV_CLK_CNT_MAX 7 @@ -176,8 +180,9 @@ struct ad4080_chip_info { }; struct ad4080_state { - struct regmap *regmap; - struct iio_backend *back; + struct spi_device *spi[AD4080_MAX_CHANNELS]; + struct regmap *regmap[AD4080_MAX_CHANNELS]; + struct iio_backend *back[AD4080_MAX_CHANNELS]; const struct ad4080_chip_info *info; /* * Synchronize access to members the of driver state, and ensure @@ -187,7 +192,7 @@ struct ad4080_state { unsigned int num_lanes; unsigned int dec_rate; unsigned long clk_rate; - enum ad4080_filter_type filter_type; + enum ad4080_filter_type filter_type[AD4080_MAX_CHANNELS]; bool lvds_cnv_en; }; @@ -204,9 +209,9 @@ static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg, struct ad4080_state *st = iio_priv(indio_dev); if (readval) - return regmap_read(st->regmap, reg, readval); + return regmap_read(st->regmap[0], reg, readval); - return regmap_write(st->regmap, reg, writeval); + return regmap_write(st->regmap[0], reg, writeval); } static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2) @@ -227,8 +232,9 @@ static unsigned int ad4080_get_dec_rate(struct iio_dev *dev, struct ad4080_state *st = iio_priv(dev); int ret; unsigned int data; + unsigned int ch = chan->channel; - ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + ret = regmap_read(st->regmap[ch], AD4080_REG_FILTER_CONFIG, &data); if (ret) return ret; @@ -240,13 +246,14 @@ static int ad4080_set_dec_rate(struct iio_dev *dev, unsigned int mode) { struct ad4080_state *st = iio_priv(dev); + unsigned int ch = chan->channel; guard(mutex)(&st->lock); - if ((st->filter_type >= SINC_5 && mode >= 512) || mode < 2) + if ((st->filter_type[ch] >= SINC_5 && mode >= 512) || mode < 2) return -EINVAL; - return regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + return regmap_update_bits(st->regmap[ch], AD4080_REG_FILTER_CONFIG, AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, FIELD_PREP(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, (ilog2(mode) - 1))); @@ -266,15 +273,15 @@ static int ad4080_read_raw(struct iio_dev *indio_dev, dec_rate = ad4080_get_dec_rate(indio_dev, chan); if (dec_rate < 0) return dec_rate; - if (st->filter_type == SINC_5_COMP) + if (st->filter_type[chan->channel] == SINC_5_COMP) dec_rate *= 2; - if (st->filter_type) + if (st->filter_type[chan->channel]) *val = DIV_ROUND_CLOSEST(st->clk_rate, dec_rate); else *val = st->clk_rate; return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - if (st->filter_type == FILTER_NONE) { + if (st->filter_type[chan->channel] == FILTER_NONE) { *val = 1; } else { *val = ad4080_get_dec_rate(indio_dev, chan); @@ -295,7 +302,7 @@ static int ad4080_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - if (st->filter_type == FILTER_NONE && val > 1) + if (st->filter_type[chan->channel] == FILTER_NONE && val > 1) return -EINVAL; return ad4080_set_dec_rate(indio_dev, chan, val); @@ -304,23 +311,23 @@ static int ad4080_write_raw(struct iio_dev *indio_dev, } } -static int ad4080_lvds_sync_write(struct ad4080_state *st) +static int ad4080_lvds_sync_write(struct ad4080_state *st, unsigned int ch) { - struct device *dev = regmap_get_device(st->regmap); + struct device *dev = regmap_get_device(st->regmap[ch]); int ret; - ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); if (ret) return ret; - ret = iio_backend_interface_data_align(st->back, 10000); + ret = iio_backend_interface_data_align(st->back[ch], 10000); if (ret) return dev_err_probe(dev, ret, "Data alignment process failed\n"); dev_dbg(dev, "Success: Pattern correct and Locked!\n"); - return regmap_clear_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + return regmap_clear_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN); } @@ -329,9 +336,10 @@ static int ad4080_get_filter_type(struct iio_dev *dev, { struct ad4080_state *st = iio_priv(dev); unsigned int data; + unsigned int ch = chan->channel; int ret; - ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + ret = regmap_read(st->regmap[ch], AD4080_REG_FILTER_CONFIG, &data); if (ret) return ret; @@ -343,6 +351,7 @@ static int ad4080_set_filter_type(struct iio_dev *dev, unsigned int mode) { struct ad4080_state *st = iio_priv(dev); + unsigned int ch = chan->channel; int dec_rate; int ret; @@ -355,18 +364,18 @@ static int ad4080_set_filter_type(struct iio_dev *dev, if (mode >= SINC_5 && dec_rate >= 512) return -EINVAL; - ret = iio_backend_filter_type_set(st->back, mode); + ret = iio_backend_filter_type_set(st->back[ch], mode); if (ret) return ret; - ret = regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + ret = regmap_update_bits(st->regmap[ch], AD4080_REG_FILTER_CONFIG, AD4080_FILTER_CONFIG_FILTER_SEL_MSK, FIELD_PREP(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, mode)); if (ret) return ret; - st->filter_type = mode; + st->filter_type[ch] = mode; return 0; } @@ -380,14 +389,14 @@ static int ad4080_read_avail(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - switch (st->filter_type) { + switch (st->filter_type[chan->channel]) { case FILTER_NONE: *vals = ad4080_dec_rate_none; *length = ARRAY_SIZE(ad4080_dec_rate_none); break; default: *vals = ad4080_dec_rate_avail; - *length = st->filter_type >= SINC_5 ? + *length = st->filter_type[chan->channel] >= SINC_5 ? (ARRAY_SIZE(ad4080_dec_rate_avail) - 2) : ARRAY_SIZE(ad4080_dec_rate_avail); break; @@ -399,6 +408,28 @@ static int ad4080_read_avail(struct iio_dev *indio_dev, } } +static int ad4880_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int ret; + + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + /* + * Each backend has a single channel (channel 0 from the + * backend's perspective), so always use channel index 0. + */ + if (test_bit(ch, scan_mask)) + ret = iio_backend_chan_enable(st->back[ch], 0); + else + ret = iio_backend_chan_disable(st->back[ch], 0); + if (ret) + return ret; + } + + return 0; +} + static const struct iio_info ad4080_iio_info = { .debugfs_reg_access = ad4080_reg_access, .read_raw = ad4080_read_raw, @@ -406,6 +437,19 @@ static const struct iio_info ad4080_iio_info = { .read_avail = ad4080_read_avail, }; +/* + * AD4880 needs update_scan_mode to enable/disable individual backend channels. + * Single-channel devices don't need this as their backends may not implement + * chan_enable/chan_disable operations. + */ +static const struct iio_info ad4880_iio_info = { + .debugfs_reg_access = ad4080_reg_access, + .read_raw = ad4080_read_raw, + .write_raw = ad4080_write_raw, + .read_avail = ad4080_read_avail, + .update_scan_mode = ad4880_update_scan_mode, +}; + static const struct iio_enum ad4080_filter_type_enum = { .items = ad4080_filter_type_iio_enum, .num_items = ARRAY_SIZE(ad4080_filter_type_iio_enum), @@ -420,17 +464,28 @@ static struct iio_chan_spec_ext_info ad4080_ext_info[] = { { } }; -#define AD4080_CHANNEL_DEFINE(bits, storage) { \ +/* + * AD4880 needs per-channel filter configuration since each channel has + * its own independent ADC with separate SPI interface. + */ +static struct iio_chan_spec_ext_info ad4880_ext_info[] = { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4080_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE, + &ad4080_filter_type_enum), + { } +}; + +#define AD4080_CHANNEL_DEFINE(bits, storage, idx) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ - .channel = 0, \ + .channel = (idx), \ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_all_available = \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .ext_info = ad4080_ext_info, \ - .scan_index = 0, \ + .scan_index = (idx), \ .scan_type = { \ .sign = 's', \ .realbits = (bits), \ @@ -438,17 +493,45 @@ static struct iio_chan_spec_ext_info ad4080_ext_info[] = { }, \ } -static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32); +/* + * AD4880 has per-channel attributes (filter_type, oversampling_ratio, + * sampling_frequency) since each channel has its own independent ADC + * with separate SPI configuration interface. + */ +#define AD4880_CHANNEL_DEFINE(bits, storage, idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .ext_info = ad4880_ext_info, \ + .scan_index = (idx), \ + .scan_type = { \ + .sign = 's', \ + .realbits = (bits), \ + .storagebits = (storage), \ + }, \ +} -static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32); +static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); -static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16); +static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32, 0); -static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16); +static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); -static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16); +static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16, 0); -static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16); +static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); + +static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16, 0); + +static const struct iio_chan_spec ad4880_channels[] = { + AD4880_CHANNEL_DEFINE(20, 32, 0), + AD4880_CHANNEL_DEFINE(20, 32, 1), +}; static const struct ad4080_chip_info ad4080_chip_info = { .name = "ad4080", @@ -510,25 +593,34 @@ static const struct ad4080_chip_info ad4087_chip_info = { .lvds_cnv_clk_cnt_max = 1, }; -static int ad4080_setup(struct iio_dev *indio_dev) +static const struct ad4080_chip_info ad4880_chip_info = { + .name = "ad4880", + .product_id = AD4880_CHIP_ID, + .scale_table = ad4080_scale_table, + .num_scales = ARRAY_SIZE(ad4080_scale_table), + .num_channels = 2, + .channels = ad4880_channels, + .lvds_cnv_clk_cnt_max = AD4080_LVDS_CNV_CLK_CNT_MAX, +}; + +static int ad4080_setup_channel(struct ad4080_state *st, unsigned int ch) { - struct ad4080_state *st = iio_priv(indio_dev); - struct device *dev = regmap_get_device(st->regmap); + struct device *dev = regmap_get_device(st->regmap[ch]); __le16 id_le; u16 id; int ret; - ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + ret = regmap_write(st->regmap[ch], AD4080_REG_INTERFACE_CONFIG_A, AD4080_INTERFACE_CONFIG_A_SW_RESET); if (ret) return ret; - ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + ret = regmap_write(st->regmap[ch], AD4080_REG_INTERFACE_CONFIG_A, AD4080_INTERFACE_CONFIG_A_SDO_ENABLE); if (ret) return ret; - ret = regmap_bulk_read(st->regmap, AD4080_REG_PRODUCT_ID_L, &id_le, + ret = regmap_bulk_read(st->regmap[ch], AD4080_REG_PRODUCT_ID_L, &id_le, sizeof(id_le)); if (ret) return ret; @@ -537,18 +629,18 @@ static int ad4080_setup(struct iio_dev *indio_dev) if (id != st->info->product_id) dev_info(dev, "Unrecognized CHIP_ID 0x%X\n", id); - ret = regmap_set_bits(st->regmap, AD4080_REG_GPIO_CONFIG_A, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_GPIO_CONFIG_A, AD4080_GPIO_CONFIG_A_GPO_1_EN); if (ret) return ret; - ret = regmap_write(st->regmap, AD4080_REG_GPIO_CONFIG_B, + ret = regmap_write(st->regmap[ch], AD4080_REG_GPIO_CONFIG_B, FIELD_PREP(AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK, AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY)); if (ret) return ret; - ret = iio_backend_num_lanes_set(st->back, st->num_lanes); + ret = iio_backend_num_lanes_set(st->back[ch], st->num_lanes); if (ret) return ret; @@ -556,7 +648,7 @@ static int ad4080_setup(struct iio_dev *indio_dev) return 0; /* Set maximum LVDS Data Transfer Latency */ - ret = regmap_update_bits(st->regmap, + ret = regmap_update_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_B, AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, FIELD_PREP(AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, @@ -565,24 +657,38 @@ static int ad4080_setup(struct iio_dev *indio_dev) return ret; if (st->num_lanes > 1) { - ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_A, AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES); if (ret) return ret; } - ret = regmap_set_bits(st->regmap, + ret = regmap_set_bits(st->regmap[ch], AD4080_REG_ADC_DATA_INTF_CONFIG_B, AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN); if (ret) return ret; - return ad4080_lvds_sync_write(st); + return ad4080_lvds_sync_write(st, ch); } -static int ad4080_properties_parse(struct ad4080_state *st) +static int ad4080_setup(struct iio_dev *indio_dev) +{ + struct ad4080_state *st = iio_priv(indio_dev); + int ret; + + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + ret = ad4080_setup_channel(st, ch); + if (ret) + return ret; + } + + return 0; +} + +static int ad4080_properties_parse(struct ad4080_state *st, + struct device *dev) { - struct device *dev = regmap_get_device(st->regmap); st->lvds_cnv_en = device_property_read_bool(dev, "adi,lvds-cnv-enable"); @@ -617,14 +723,28 @@ static int ad4080_probe(struct spi_device *spi) return dev_err_probe(dev, ret, "failed to get and enable supplies\n"); - st->regmap = devm_regmap_init_spi(spi, &ad4080_regmap_config); - if (IS_ERR(st->regmap)) - return PTR_ERR(st->regmap); + /* Setup primary SPI device (channel 0) */ + st->spi[0] = spi; + st->regmap[0] = devm_regmap_init_spi(spi, &ad4080_regmap_config); + if (IS_ERR(st->regmap[0])) + return PTR_ERR(st->regmap[0]); st->info = spi_get_device_match_data(spi); if (!st->info) return -ENODEV; + /* Setup ancillary SPI devices for additional channels */ + for (unsigned int ch = 1; ch < st->info->num_channels; ch++) { + st->spi[ch] = devm_spi_new_ancillary_device(spi, spi_get_chipselect(spi, ch)); + if (IS_ERR(st->spi[ch])) + return dev_err_probe(dev, PTR_ERR(st->spi[ch]), + "failed to register ancillary device\n"); + + st->regmap[ch] = devm_regmap_init_spi(st->spi[ch], &ad4080_regmap_config); + if (IS_ERR(st->regmap[ch])) + return PTR_ERR(st->regmap[ch]); + } + ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; @@ -632,9 +752,10 @@ static int ad4080_probe(struct spi_device *spi) indio_dev->name = st->info->name; indio_dev->channels = st->info->channels; indio_dev->num_channels = st->info->num_channels; - indio_dev->info = &ad4080_iio_info; + indio_dev->info = st->info->num_channels > 1 ? + &ad4880_iio_info : &ad4080_iio_info; - ret = ad4080_properties_parse(st); + ret = ad4080_properties_parse(st, dev); if (ret) return ret; @@ -644,15 +765,25 @@ static int ad4080_probe(struct spi_device *spi) st->clk_rate = clk_get_rate(clk); - st->back = devm_iio_backend_get(dev, NULL); - if (IS_ERR(st->back)) - return PTR_ERR(st->back); + /* Get backends for all channels */ + for (unsigned int ch = 0; ch < st->info->num_channels; ch++) { + st->back[ch] = devm_iio_backend_get_by_index(dev, ch); + if (IS_ERR(st->back[ch])) + return PTR_ERR(st->back[ch]); - ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); - if (ret) - return ret; + ret = devm_iio_backend_enable(dev, st->back[ch]); + if (ret) + return ret; + } - ret = devm_iio_backend_enable(dev, st->back); + /* + * Request buffer from the first backend only. For multi-channel + * devices (e.g., AD4880), the FPGA uses two axi_ad408x IP instances + * (one per ADC channel) whose outputs are combined by a packer block + * that interleaves all channel data into a single DMA stream routed + * through the first backend's clock domain. + */ + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev); if (ret) return ret; @@ -670,6 +801,7 @@ static const struct spi_device_id ad4080_id[] = { { "ad4084", (kernel_ulong_t)&ad4084_chip_info }, { "ad4086", (kernel_ulong_t)&ad4086_chip_info }, { "ad4087", (kernel_ulong_t)&ad4087_chip_info }, + { "ad4880", (kernel_ulong_t)&ad4880_chip_info }, { } }; MODULE_DEVICE_TABLE(spi, ad4080_id); @@ -681,6 +813,7 @@ static const struct of_device_id ad4080_of_match[] = { { .compatible = "adi,ad4084", &ad4084_chip_info }, { .compatible = "adi,ad4086", &ad4086_chip_info }, { .compatible = "adi,ad4087", &ad4087_chip_info }, + { .compatible = "adi,ad4880", &ad4880_chip_info }, { } }; MODULE_DEVICE_TABLE(of, ad4080_of_match); -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC 2026-03-28 11:40 [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus ` (2 preceding siblings ...) 2026-03-28 11:40 ` [PATCH v8 3/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus @ 2026-04-12 18:33 ` Jonathan Cameron 2026-04-16 8:51 ` Miclaus, Antoniu 3 siblings, 1 reply; 9+ messages in thread From: Jonathan Cameron @ 2026-04-12 18:33 UTC (permalink / raw) To: Antoniu Miclaus Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner, Nuno Sá, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio, devicetree, linux-kernel On Sat, 28 Mar 2026 13:40:47 +0200 Antoniu Miclaus <antoniu.miclaus@analog.com> wrote: > Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with > integrated fully differential amplifiers (FDA). > > Architecture notes: > > The AD4880 is modeled as a single IIO device rather than two independent > devices because the channels share power supplies, a voltage reference, > the CNV conversion clock, and a single interleaved data output stream. > Splitting them into separate IIO devices would make synchronized > dual-channel capture impossible from userspace. > > An MFD approach does not apply here either - the channels are not > functionally distinct sub-devices but identical ADC paths sharing a > common data interface. > > Each channel has fully independent configuration registers accessible > through separate SPI chip selects, so per-channel regmaps are used with > no locking between them. The data path has no software involvement at > runtime: the CNV clock triggers simultaneous conversions and the device > outputs an interleaved bitstream captured directly by the IIO backend > (FPGA). spi_new_ancillary_device() handles the configuration path; > the IIO backend handles the data path. > > The debugfs_reg_access callback is not exposed for the dual-channel > variant since the IIO framework provides a single (reg, val) interface > with no channel parameter, and exposing only one channel would be > misleading. > > The AD4880 is a fairly unique part - having separate SPI config > interfaces per channel with a shared interleaved data output is not > a common pattern. I tried applying this and it's not going in cleanly (I didn't check exactly why). Please could you send a rebased version. The togreg branch should be fine I think, but maybe sanity check it against my current testing branch as well. Whilst this driver is making a few more assumptions about the backend than I'd ideally like, I think it is reasonable to postpone any handling for truely separate backends until (maybe) someone needs it. Thanks, Jonathan > > Changes in v8: > - Drop fwnode_handle cleanup patch (now in jic23/testing) > - Clarify backend buffer comment to describe FPGA architecture > (two axi_ad408x IP instances with a packer block) > - Make filter_type a per-channel array instead of a single variable > - Restore debugfs_reg_access for AD4880 (uses channel 0 regmap), > based on sashiko's review > > Antoniu Miclaus (3): > iio: backend: add devm_iio_backend_get_by_index() > dt-bindings: iio: adc: ad4080: add AD4880 support > iio: adc: ad4080: add support for AD4880 dual-channel ADC > > .../bindings/iio/adc/adi,ad4080.yaml | 53 +++- > drivers/iio/adc/ad4080.c | 251 ++++++++++++++---- > drivers/iio/industrialio-backend.c | 53 ++-- > include/linux/iio/backend.h | 1 + > 4 files changed, 282 insertions(+), 76 deletions(-) > ^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC 2026-04-12 18:33 ` [PATCH v8 0/3] " Jonathan Cameron @ 2026-04-16 8:51 ` Miclaus, Antoniu 2026-04-20 14:29 ` Jonathan Cameron 0 siblings, 1 reply; 9+ messages in thread From: Miclaus, Antoniu @ 2026-04-16 8:51 UTC (permalink / raw) To: Jonathan Cameron Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org -- Antoniu Miclăuş > -----Original Message----- > From: Jonathan Cameron <jic23@kernel.org> > Sent: Sunday, April 12, 2026 9:34 PM > To: Miclaus, Antoniu <Antoniu.Miclaus@analog.com> > Cc: Lars-Peter Clausen <lars@metafoo.de>; Hennerich, Michael > <Michael.Hennerich@analog.com>; David Lechner <dlechner@baylibre.com>; > Sa, Nuno <Nuno.Sa@analog.com>; Rob Herring <robh@kernel.org>; Krzysztof > Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; > Olivier Moysan <olivier.moysan@foss.st.com>; linux-iio@vger.kernel.org; > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org > Subject: Re: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual- > channel ADC > > [External] > > On Sat, 28 Mar 2026 13:40:47 +0200 > Antoniu Miclaus <antoniu.miclaus@analog.com> wrote: > > > Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with > > integrated fully differential amplifiers (FDA). > > > > Architecture notes: > > > > The AD4880 is modeled as a single IIO device rather than two independent > > devices because the channels share power supplies, a voltage reference, > > the CNV conversion clock, and a single interleaved data output stream. > > Splitting them into separate IIO devices would make synchronized > > dual-channel capture impossible from userspace. > > > > An MFD approach does not apply here either - the channels are not > > functionally distinct sub-devices but identical ADC paths sharing a > > common data interface. > > > > Each channel has fully independent configuration registers accessible > > through separate SPI chip selects, so per-channel regmaps are used with > > no locking between them. The data path has no software involvement at > > runtime: the CNV clock triggers simultaneous conversions and the device > > outputs an interleaved bitstream captured directly by the IIO backend > > (FPGA). spi_new_ancillary_device() handles the configuration path; > > the IIO backend handles the data path. > > > > The debugfs_reg_access callback is not exposed for the dual-channel > > variant since the IIO framework provides a single (reg, val) interface > > with no channel parameter, and exposing only one channel would be > > misleading. > > > > The AD4880 is a fairly unique part - having separate SPI config > > interfaces per channel with a shared interleaved data output is not > > a common pattern. > I tried applying this and it's not going in cleanly (I didn't check > exactly why). Please could you send a rebased version. The togreg > branch should be fine I think, but maybe sanity check it against > my current testing branch as well. The AD4880 driver has a cross-tree dependency on two SPI patches that are queued in spi/for-7.1: - ffef4123043c ("spi: allow ancillary devices to share parent's chip selects") - 463279e58811 ("spi: add devm_spi_new_ancillary_device()") The driver uses devm_spi_new_ancillary_device() with multi-CS to create an ancillary SPI device for the second channel's configuration interface, so it won't build against togreg alone. What approach do you suggest in this situation? > > Whilst this driver is making a few more assumptions about the backend > than I'd ideally like, I think it is reasonable to postpone any handling > for truely separate backends until (maybe) someone needs it. > > Thanks, > > Jonathan > > > > > Changes in v8: > > - Drop fwnode_handle cleanup patch (now in jic23/testing) > > - Clarify backend buffer comment to describe FPGA architecture > > (two axi_ad408x IP instances with a packer block) > > - Make filter_type a per-channel array instead of a single variable > > - Restore debugfs_reg_access for AD4880 (uses channel 0 regmap), > > based on sashiko's review > > > > Antoniu Miclaus (3): > > iio: backend: add devm_iio_backend_get_by_index() > > dt-bindings: iio: adc: ad4080: add AD4880 support > > iio: adc: ad4080: add support for AD4880 dual-channel ADC > > > > .../bindings/iio/adc/adi,ad4080.yaml | 53 +++- > > drivers/iio/adc/ad4080.c | 251 ++++++++++++++---- > > drivers/iio/industrialio-backend.c | 53 ++-- > > include/linux/iio/backend.h | 1 + > > 4 files changed, 282 insertions(+), 76 deletions(-) > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC 2026-04-16 8:51 ` Miclaus, Antoniu @ 2026-04-20 14:29 ` Jonathan Cameron 2026-04-20 14:35 ` Miclaus, Antoniu 0 siblings, 1 reply; 9+ messages in thread From: Jonathan Cameron @ 2026-04-20 14:29 UTC (permalink / raw) To: Miclaus, Antoniu Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org On Thu, 16 Apr 2026 08:51:46 +0000 "Miclaus, Antoniu" <Antoniu.Miclaus@analog.com> wrote: > -- > Antoniu Miclăuş > > > -----Original Message----- > > From: Jonathan Cameron <jic23@kernel.org> > > Sent: Sunday, April 12, 2026 9:34 PM > > To: Miclaus, Antoniu <Antoniu.Miclaus@analog.com> > > Cc: Lars-Peter Clausen <lars@metafoo.de>; Hennerich, Michael > > <Michael.Hennerich@analog.com>; David Lechner <dlechner@baylibre.com>; > > Sa, Nuno <Nuno.Sa@analog.com>; Rob Herring <robh@kernel.org>; Krzysztof > > Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; > > Olivier Moysan <olivier.moysan@foss.st.com>; linux-iio@vger.kernel.org; > > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org > > Subject: Re: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual- > > channel ADC > > > > [External] > > > > On Sat, 28 Mar 2026 13:40:47 +0200 > > Antoniu Miclaus <antoniu.miclaus@analog.com> wrote: > > > > > Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC with > > > integrated fully differential amplifiers (FDA). > > > > > > Architecture notes: > > > > > > The AD4880 is modeled as a single IIO device rather than two independent > > > devices because the channels share power supplies, a voltage reference, > > > the CNV conversion clock, and a single interleaved data output stream. > > > Splitting them into separate IIO devices would make synchronized > > > dual-channel capture impossible from userspace. > > > > > > An MFD approach does not apply here either - the channels are not > > > functionally distinct sub-devices but identical ADC paths sharing a > > > common data interface. > > > > > > Each channel has fully independent configuration registers accessible > > > through separate SPI chip selects, so per-channel regmaps are used with > > > no locking between them. The data path has no software involvement at > > > runtime: the CNV clock triggers simultaneous conversions and the device > > > outputs an interleaved bitstream captured directly by the IIO backend > > > (FPGA). spi_new_ancillary_device() handles the configuration path; > > > the IIO backend handles the data path. > > > > > > The debugfs_reg_access callback is not exposed for the dual-channel > > > variant since the IIO framework provides a single (reg, val) interface > > > with no channel parameter, and exposing only one channel would be > > > misleading. > > > > > > The AD4880 is a fairly unique part - having separate SPI config > > > interfaces per channel with a shared interleaved data output is not > > > a common pattern. > > I tried applying this and it's not going in cleanly (I didn't check > > exactly why). Please could you send a rebased version. The togreg > > branch should be fine I think, but maybe sanity check it against > > my current testing branch as well. > > The AD4880 driver has a cross-tree dependency on two SPI patches that are queued in spi/for-7.1: > > - ffef4123043c ("spi: allow ancillary devices to share parent's chip selects") > - 463279e58811 ("spi: add devm_spi_new_ancillary_device()") > > The driver uses devm_spi_new_ancillary_device() with multi-CS to create an ancillary SPI device for the second channel's configuration interface, so it won't build against togreg alone. > > What approach do you suggest in this situation? Ah. Makes sense. Given timing, wait and if I look to have forgotten this ping me around rc2 when I should have that 7.1 material in my base tree anyway. thanks, Jonathan > > > > > Whilst this driver is making a few more assumptions about the backend > > than I'd ideally like, I think it is reasonable to postpone any handling > > for truely separate backends until (maybe) someone needs it. > > > > Thanks, > > > > Jonathan > > > > > > > > Changes in v8: > > > - Drop fwnode_handle cleanup patch (now in jic23/testing) > > > - Clarify backend buffer comment to describe FPGA architecture > > > (two axi_ad408x IP instances with a packer block) > > > - Make filter_type a per-channel array instead of a single variable > > > - Restore debugfs_reg_access for AD4880 (uses channel 0 regmap), > > > based on sashiko's review > > > > > > Antoniu Miclaus (3): > > > iio: backend: add devm_iio_backend_get_by_index() > > > dt-bindings: iio: adc: ad4080: add AD4880 support > > > iio: adc: ad4080: add support for AD4880 dual-channel ADC > > > > > > .../bindings/iio/adc/adi,ad4080.yaml | 53 +++- > > > drivers/iio/adc/ad4080.c | 251 ++++++++++++++---- > > > drivers/iio/industrialio-backend.c | 53 ++-- > > > include/linux/iio/backend.h | 1 + > > > 4 files changed, 282 insertions(+), 76 deletions(-) > > > > ^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC 2026-04-20 14:29 ` Jonathan Cameron @ 2026-04-20 14:35 ` Miclaus, Antoniu 0 siblings, 0 replies; 9+ messages in thread From: Miclaus, Antoniu @ 2026-04-20 14:35 UTC (permalink / raw) To: Jonathan Cameron Cc: Lars-Peter Clausen, Hennerich, Michael, David Lechner, Sa, Nuno, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org -- Antoniu Miclăuş > -----Original Message----- > From: Jonathan Cameron <jic23@kernel.org> > Sent: Monday, April 20, 2026 5:29 PM > To: Miclaus, Antoniu <Antoniu.Miclaus@analog.com> > Cc: Lars-Peter Clausen <lars@metafoo.de>; Hennerich, Michael > <Michael.Hennerich@analog.com>; David Lechner <dlechner@baylibre.com>; > Sa, Nuno <Nuno.Sa@analog.com>; Rob Herring <robh@kernel.org>; Krzysztof > Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; > Olivier Moysan <olivier.moysan@foss.st.com>; linux-iio@vger.kernel.org; > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org > Subject: Re: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual- > channel ADC > > On Thu, 16 Apr 2026 08:51:46 +0000 > "Miclaus, Antoniu" <Antoniu.Miclaus@analog.com> wrote: > > > -- > > Antoniu Miclăuş > > > > > -----Original Message----- > > > From: Jonathan Cameron <jic23@kernel.org> > > > Sent: Sunday, April 12, 2026 9:34 PM > > > To: Miclaus, Antoniu <Antoniu.Miclaus@analog.com> > > > Cc: Lars-Peter Clausen <lars@metafoo.de>; Hennerich, Michael > > > <Michael.Hennerich@analog.com>; David Lechner > <dlechner@baylibre.com>; > > > Sa, Nuno <Nuno.Sa@analog.com>; Rob Herring <robh@kernel.org>; > Krzysztof > > > Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>; > > > Olivier Moysan <olivier.moysan@foss.st.com>; linux-iio@vger.kernel.org; > > > devicetree@vger.kernel.org; linux-kernel@vger.kernel.org > > > Subject: Re: [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 > dual- > > > channel ADC > > > > > > [External] > > > > > > On Sat, 28 Mar 2026 13:40:47 +0200 > > > Antoniu Miclaus <antoniu.miclaus@analog.com> wrote: > > > > > > > Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC > with > > > > integrated fully differential amplifiers (FDA). > > > > > > > > Architecture notes: > > > > > > > > The AD4880 is modeled as a single IIO device rather than two > independent > > > > devices because the channels share power supplies, a voltage reference, > > > > the CNV conversion clock, and a single interleaved data output stream. > > > > Splitting them into separate IIO devices would make synchronized > > > > dual-channel capture impossible from userspace. > > > > > > > > An MFD approach does not apply here either - the channels are not > > > > functionally distinct sub-devices but identical ADC paths sharing a > > > > common data interface. > > > > > > > > Each channel has fully independent configuration registers accessible > > > > through separate SPI chip selects, so per-channel regmaps are used with > > > > no locking between them. The data path has no software involvement at > > > > runtime: the CNV clock triggers simultaneous conversions and the device > > > > outputs an interleaved bitstream captured directly by the IIO backend > > > > (FPGA). spi_new_ancillary_device() handles the configuration path; > > > > the IIO backend handles the data path. > > > > > > > > The debugfs_reg_access callback is not exposed for the dual-channel > > > > variant since the IIO framework provides a single (reg, val) interface > > > > with no channel parameter, and exposing only one channel would be > > > > misleading. > > > > > > > > The AD4880 is a fairly unique part - having separate SPI config > > > > interfaces per channel with a shared interleaved data output is not > > > > a common pattern. > > > I tried applying this and it's not going in cleanly (I didn't check > > > exactly why). Please could you send a rebased version. The togreg > > > branch should be fine I think, but maybe sanity check it against > > > my current testing branch as well. > > > > The AD4880 driver has a cross-tree dependency on two SPI patches that are > queued in spi/for-7.1: > > > > - ffef4123043c ("spi: allow ancillary devices to share parent's chip selects") > > - 463279e58811 ("spi: add devm_spi_new_ancillary_device()") > > > > The driver uses devm_spi_new_ancillary_device() with multi-CS to create an > ancillary SPI device for the second channel's configuration interface, so it won't > build against togreg alone. > > > > What approach do you suggest in this situation? > Ah. Makes sense. > > Given timing, wait and if I look to have forgotten this ping me around > rc2 when I should have that 7.1 material in my base tree anyway. > Seems like the patches are already on the master branch: https://github.com/torvalds/linux/commit/405f6584d7d0fc46534fd370e374630283dffe60 There were some conflicts anyways with the ad4088 support that was not on my local branch. I added v9 in the meantime which solves those conflicts too. Regards, > thanks, > > Jonathan > > > > > > > > Whilst this driver is making a few more assumptions about the backend > > > than I'd ideally like, I think it is reasonable to postpone any handling > > > for truely separate backends until (maybe) someone needs it. > > > > > > Thanks, > > > > > > Jonathan > > > > > > > > > > > Changes in v8: > > > > - Drop fwnode_handle cleanup patch (now in jic23/testing) > > > > - Clarify backend buffer comment to describe FPGA architecture > > > > (two axi_ad408x IP instances with a packer block) > > > > - Make filter_type a per-channel array instead of a single variable > > > > - Restore debugfs_reg_access for AD4880 (uses channel 0 regmap), > > > > based on sashiko's review > > > > > > > > Antoniu Miclaus (3): > > > > iio: backend: add devm_iio_backend_get_by_index() > > > > dt-bindings: iio: adc: ad4080: add AD4880 support > > > > iio: adc: ad4080: add support for AD4880 dual-channel ADC > > > > > > > > .../bindings/iio/adc/adi,ad4080.yaml | 53 +++- > > > > drivers/iio/adc/ad4080.c | 251 ++++++++++++++---- > > > > drivers/iio/industrialio-backend.c | 53 ++-- > > > > include/linux/iio/backend.h | 1 + > > > > 4 files changed, 282 insertions(+), 76 deletions(-) > > > > > > ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-04-20 14:35 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-28 11:40 [PATCH v8 0/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus 2026-03-28 11:40 ` [PATCH v8 1/3] iio: backend: add devm_iio_backend_get_by_index() Antoniu Miclaus 2026-03-28 11:40 ` [PATCH v8 2/3] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus 2026-03-31 15:44 ` Rob Herring 2026-03-28 11:40 ` [PATCH v8 3/3] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus 2026-04-12 18:33 ` [PATCH v8 0/3] " Jonathan Cameron 2026-04-16 8:51 ` Miclaus, Antoniu 2026-04-20 14:29 ` Jonathan Cameron 2026-04-20 14:35 ` Miclaus, Antoniu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox