* [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
@ 2026-03-21 10:01 Antoniu Miclaus
2026-03-21 10:01 ` [PATCH v7 1/4] iio: backend: use __free(fwnode_handle) for automatic cleanup Antoniu Miclaus
` (5 more replies)
0 siblings, 6 replies; 17+ messages in thread
From: Antoniu Miclaus @ 2026-03-21 10:01 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
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 v7:
- Drop debugfs_reg_access for dual-channel AD4880 variant
- Pass struct device * to ad4080_properties_parse() instead of
using regmap_get_device(st->regmap[0])
- Use 100-column limit consistently for function signatures
- Add architecture summary to cover letter (per Andy's request)
Antoniu Miclaus (4):
iio: backend: use __free(fwnode_handle) for automatic cleanup
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 | 231 ++++++++++++++----
drivers/iio/industrialio-backend.c | 59 +++--
include/linux/iio/backend.h | 1 +
4 files changed, 273 insertions(+), 71 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v7 1/4] iio: backend: use __free(fwnode_handle) for automatic cleanup
2026-03-21 10:01 [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
@ 2026-03-21 10:01 ` Antoniu Miclaus
2026-03-21 12:03 ` Jonathan Cameron
2026-03-21 10:01 ` [PATCH v7 2/4] iio: backend: add devm_iio_backend_get_by_index() Antoniu Miclaus
` (4 subsequent siblings)
5 siblings, 1 reply; 17+ messages in thread
From: Antoniu Miclaus @ 2026-03-21 10:01 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, David Lechner, Nuno Sá, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel
Cc: Andy Shevchenko
Convert __devm_iio_backend_fwnode_get() to use the __free(fwnode_handle)
cleanup attribute for the fwnode_back variable, removing the need for
manual fwnode_handle_put() calls. Move the declaration closer to its
first use, narrowing its scope.
No functional change.
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Reviewed-by: Nuno Sá <nuno.sa@analog.com>
Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
Changes in v7:
- No changes
drivers/iio/industrialio-backend.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
index 447b694d6d5f..58f7e1426095 100644
--- a/drivers/iio/industrialio-backend.c
+++ b/drivers/iio/industrialio-backend.c
@@ -952,7 +952,6 @@ 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)
{
- struct fwnode_handle *fwnode_back;
struct iio_backend *back;
unsigned int index;
int ret;
@@ -967,7 +966,8 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con
index = 0;
}
- fwnode_back = fwnode_find_reference(fwnode, "io-backends", index);
+ struct fwnode_handle *fwnode_back __free(fwnode_handle) =
+ fwnode_find_reference(fwnode, "io-backends", index);
if (IS_ERR(fwnode_back))
return dev_err_cast_probe(dev, fwnode_back,
"Cannot get Firmware reference\n");
@@ -977,7 +977,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con
if (!device_match_fwnode(back->dev, fwnode_back))
continue;
- fwnode_handle_put(fwnode_back);
ret = __devm_iio_backend_get(dev, back);
if (ret)
return ERR_PTR(ret);
@@ -988,7 +987,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con
return back;
}
- fwnode_handle_put(fwnode_back);
return ERR_PTR(-EPROBE_DEFER);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v7 2/4] iio: backend: add devm_iio_backend_get_by_index()
2026-03-21 10:01 [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-03-21 10:01 ` [PATCH v7 1/4] iio: backend: use __free(fwnode_handle) for automatic cleanup Antoniu Miclaus
@ 2026-03-21 10:01 ` Antoniu Miclaus
2026-03-21 10:01 ` [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
` (3 subsequent siblings)
5 siblings, 0 replies; 17+ messages in thread
From: Antoniu Miclaus @ 2026-03-21 10:01 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
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 v7:
- Use 100-column limit consistently for function signatures
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] 17+ messages in thread
* [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-03-21 10:01 [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-03-21 10:01 ` [PATCH v7 1/4] iio: backend: use __free(fwnode_handle) for automatic cleanup Antoniu Miclaus
2026-03-21 10:01 ` [PATCH v7 2/4] iio: backend: add devm_iio_backend_get_by_index() Antoniu Miclaus
@ 2026-03-21 10:01 ` Antoniu Miclaus
2026-03-21 12:08 ` Jonathan Cameron
2026-03-23 19:44 ` Conor Dooley
2026-03-21 10:01 ` [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
` (2 subsequent siblings)
5 siblings, 2 replies; 17+ messages in thread
From: Antoniu Miclaus @ 2026-03-21 10:01 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
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 v7:
- 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] 17+ messages in thread
* [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-21 10:01 [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
` (2 preceding siblings ...)
2026-03-21 10:01 ` [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
@ 2026-03-21 10:01 ` Antoniu Miclaus
2026-03-21 12:18 ` Jonathan Cameron
2026-03-23 7:59 ` kernel test robot
2026-03-21 12:25 ` [PATCH v7 0/4] " Jonathan Cameron
2026-03-23 10:58 ` Jonathan Cameron
5 siblings, 2 replies; 17+ messages in thread
From: Antoniu Miclaus @ 2026-03-21 10:01 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
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 v7:
- Drop debugfs_reg_access for dual-channel AD4880 variant
- Pass struct device * to ad4080_properties_parse() instead of
using regmap_get_device(st->regmap[0])
drivers/iio/adc/ad4080.c | 231 ++++++++++++++++++++++++++++++---------
1 file changed, 181 insertions(+), 50 deletions(-)
diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
index 7cf3b6ed7940..8767eef418e9 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
@@ -203,10 +208,11 @@ static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg,
{
struct ad4080_state *st = iio_priv(indio_dev);
+ /* Use channel 0 regmap for debugfs access */
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 +233,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 +247,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)
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)));
@@ -304,23 +312,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 +337,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 +352,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,11 +365,11 @@ 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));
@@ -399,6 +409,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 +438,18 @@ 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 = {
+ .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,51 @@ 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), \
+ .storagebits = (storage), \
+ }, \
+}
+
+/*
+ * 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), \
@@ -438,17 +516,22 @@ static struct iio_chan_spec_ext_info ad4080_ext_info[] = {
}, \
}
-static const struct iio_chan_spec ad4080_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 ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32);
+static const struct iio_chan_spec ad4081_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 ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16, 0);
-static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16);
+static const struct iio_chan_spec ad4084_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 ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16, 0);
-static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16);
+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,23 @@ 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), all backends share a single IIO buffer
+ * as data from all ADC channels is interleaved into one stream.
+ */
+ ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
if (ret)
return ret;
@@ -670,6 +799,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 +811,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] 17+ messages in thread
* Re: [PATCH v7 1/4] iio: backend: use __free(fwnode_handle) for automatic cleanup
2026-03-21 10:01 ` [PATCH v7 1/4] iio: backend: use __free(fwnode_handle) for automatic cleanup Antoniu Miclaus
@ 2026-03-21 12:03 ` Jonathan Cameron
0 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2026-03-21 12:03 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,
Andy Shevchenko
On Sat, 21 Mar 2026 12:01:51 +0200
Antoniu Miclaus <antoniu.miclaus@analog.com> wrote:
> Convert __devm_iio_backend_fwnode_get() to use the __free(fwnode_handle)
> cleanup attribute for the fwnode_back variable, removing the need for
> manual fwnode_handle_put() calls. Move the declaration closer to its
> first use, narrowing its scope.
>
> No functional change.
>
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
> Reviewed-by: Nuno Sá <nuno.sa@analog.com>
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
This patch stands on it's own as a good improvement so applied to the testing
branch of iio.git. If we end up going to a v8 just don't include this
patch. (I'm easily confused ;)
Thanks,
Jonathan
> ---
> Changes in v7:
> - No changes
>
> drivers/iio/industrialio-backend.c | 6 ++----
> 1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c
> index 447b694d6d5f..58f7e1426095 100644
> --- a/drivers/iio/industrialio-backend.c
> +++ b/drivers/iio/industrialio-backend.c
> @@ -952,7 +952,6 @@ 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)
> {
> - struct fwnode_handle *fwnode_back;
> struct iio_backend *back;
> unsigned int index;
> int ret;
> @@ -967,7 +966,8 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con
> index = 0;
> }
>
> - fwnode_back = fwnode_find_reference(fwnode, "io-backends", index);
> + struct fwnode_handle *fwnode_back __free(fwnode_handle) =
> + fwnode_find_reference(fwnode, "io-backends", index);
> if (IS_ERR(fwnode_back))
> return dev_err_cast_probe(dev, fwnode_back,
> "Cannot get Firmware reference\n");
> @@ -977,7 +977,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con
> if (!device_match_fwnode(back->dev, fwnode_back))
> continue;
>
> - fwnode_handle_put(fwnode_back);
> ret = __devm_iio_backend_get(dev, back);
> if (ret)
> return ERR_PTR(ret);
> @@ -988,7 +987,6 @@ static struct iio_backend *__devm_iio_backend_fwnode_get(struct device *dev, con
> return back;
> }
>
> - fwnode_handle_put(fwnode_back);
> return ERR_PTR(-EPROBE_DEFER);
> }
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-03-21 10:01 ` [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
@ 2026-03-21 12:08 ` Jonathan Cameron
2026-03-22 15:33 ` Miclaus, Antoniu
2026-03-23 19:44 ` Conor Dooley
1 sibling, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2026-03-21 12:08 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, 21 Mar 2026 12:01:53 +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).
>
> 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
From the v6 discussion. I'd just like to know a little more on this.
Are they really separate backends?
Given discussion about interleaved data, I was kind of assuming they
were different front end interfaces to a single backend IP.
The freedom this binding is giving is for those two backends to be
completely unrelated. I'm not sure if we want that.
Jonathan
>
> Reviewed-by: David Lechner <dlechner@baylibre.com>
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
> Changes in v7:
> - 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>;
> + };
> + };
> ...
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-21 10:01 ` [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
@ 2026-03-21 12:18 ` Jonathan Cameron
2026-03-22 16:26 ` Miclaus, Antoniu
2026-03-23 7:59 ` kernel test robot
1 sibling, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2026-03-21 12:18 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, 21 Mar 2026 12:01:54 +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).
>
> 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.
Silly question - can we be sure that they both are on the same SPI bus?
I think it's reasonable to assume no one would burn pins to wire the
control interfaces up to separate busses. I'm not even sure how we'd
do a binding if they were on separate busses.
Otherwise, a follow on from the 'is it one backend or two' question
on the binding.
That long discussion between you and Andy has me looking at this a little
more closely.
Jonathan
>
> 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 v7:
> - Drop debugfs_reg_access for dual-channel AD4880 variant
> - Pass struct device * to ad4080_properties_parse() instead of
> using regmap_get_device(st->regmap[0])
>
> drivers/iio/adc/ad4080.c | 231 ++++++++++++++++++++++++++++++---------
> 1 file changed, 181 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
> index 7cf3b6ed7940..8767eef418e9 100644
> --- a/drivers/iio/adc/ad4080.c
> +++ b/drivers/iio/adc/ad4080.c
> @@ -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,23 @@ 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), all backends share a single IIO buffer
> + * as data from all ADC channels is interleaved into one stream.
> + */
> + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
So this is the interleaving bit. Follows on from my question on the binding
and whether it is appropriate to represent it as two separate backends
vs a single one. With a single one we'd need to make the control interfaces
take a parameter to say which 'front end' we were configuring - though it
kind of maps to channels in the particular case and we already have
a parameter for that.
The other option might be to make the dt-binding take a phandle + index to
say this backend, with this front end interface.
> if (ret)
> return ret;
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-21 10:01 [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
` (3 preceding siblings ...)
2026-03-21 10:01 ` [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
@ 2026-03-21 12:25 ` Jonathan Cameron
2026-03-23 10:58 ` Jonathan Cameron
5 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2026-03-21 12:25 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, 21 Mar 2026 12:01:50 +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,
This might be worth confirming as the datasheet I found implies they
are separate - if there is a section that says they must be wired to the
same clock, please add a reference. They aren't wired together internally
and in LVDS mode I'd imagine they are kept separate to make signal timing
simpler as each complete channels worth of LVDS are in blocks in the pin out.
> and a single interleaved data output stream.
Aas per my (very late!) reply to v6, is this true, or is this down to
the backend? My reading of the datasheet suggests they are separate
interfaces from the ADC itself.
> Splitting them into separate IIO devices would make synchronized
> dual-channel capture impossible from userspace.
Would be more complex and require some extra glue logic in the subsystem
I think, but not impossible. Just hard.
>
> 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.
As above. Not seeing that from the datasheet. I'd change these to say
that's the backend constraint.
>
> 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).
Again, reference for that interleaved bitstream.
> 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.
Leaving that for now is fine. Can revisit if we care later. Given
it's debugfs and mostly useless after driver development is done
I doubt we really care.
>
> 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 v7:
> - Drop debugfs_reg_access for dual-channel AD4880 variant
> - Pass struct device * to ad4080_properties_parse() instead of
> using regmap_get_device(st->regmap[0])
> - Use 100-column limit consistently for function signatures
> - Add architecture summary to cover letter (per Andy's request)
>
> Antoniu Miclaus (4):
> iio: backend: use __free(fwnode_handle) for automatic cleanup
> 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 | 231 ++++++++++++++----
> drivers/iio/industrialio-backend.c | 59 +++--
> include/linux/iio/backend.h | 1 +
> 4 files changed, 273 insertions(+), 71 deletions(-)
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-03-21 12:08 ` Jonathan Cameron
@ 2026-03-22 15:33 ` Miclaus, Antoniu
2026-03-23 10:57 ` Nuno Sá
0 siblings, 1 reply; 17+ messages in thread
From: Miclaus, Antoniu @ 2026-03-22 15:33 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
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Saturday, March 21, 2026 2:08 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 v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880
> support
>
> [External]
>
> On Sat, 21 Mar 2026 12:01:53 +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).
> >
> > 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
> From the v6 discussion. I'd just like to know a little more on this.
> Are they really separate backends?
>
Yes, they are separate backends. The FPGA reference design
instantiates two independent axi_ad408x IP cores
The AD4880 is essentially two AD4080 dies in one package.
The two data streams are merged by a util_cpack2 channel
packer before reaching a single DMA:
axi_ad408x (ch A) --
--> util_cpack2 --> single DMA
axi_ad408x (ch B) --/
Each backend needs independent LVDS alignment and lane
configuration, so a single-backend-with-channel-parameter
model wouldn't fit here.
https://github.com/analogdevicesinc/hdl/blob/main/projects/ad4880_fmc_evb/common/ad4880_fmc_evb_bd.tcl
> Given discussion about interleaved data, I was kind of assuming they
> were different front end interfaces to a single backend IP.
>
> The freedom this binding is giving is for those two backends to be
> completely unrelated. I'm not sure if we want that.
>
> Jonathan
>
>
> >
> > Reviewed-by: David Lechner <dlechner@baylibre.com>
> > Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> > ---
> > Changes in v7:
> > - 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>;
> > + };
> > + };
> > ...
^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-21 12:18 ` Jonathan Cameron
@ 2026-03-22 16:26 ` Miclaus, Antoniu
2026-03-22 17:39 ` Jonathan Cameron
0 siblings, 1 reply; 17+ messages in thread
From: Miclaus, Antoniu @ 2026-03-22 16:26 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
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Saturday, March 21, 2026 2:18 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 v7 4/4] iio: adc: ad4080: add support for AD4880 dual-
> channel ADC
>
> [External]
>
> On Sat, 21 Mar 2026 12:01:54 +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).
> >
> > 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.
>
> Silly question - can we be sure that they both are on the same SPI bus?
> I think it's reasonable to assume no one would burn pins to wire the
> control interfaces up to separate busses. I'm not even sure how we'd
> do a binding if they were on separate busses.
>
> Otherwise, a follow on from the 'is it one backend or two' question
> on the binding.
>
> That long discussion between you and Andy has me looking at this a little
> more closely.
Yes, both channels are on the same SPI bus with two chip
selects — one per internal ADC die.
For the backend question — as explained in my reply to the
binding patch, the FPGA uses two separate axi_ad408x IP
instances. The buffer is requested from back[0] because
the packer output feeds the DMA through backend A's clock
domain.
> Jonathan
>
> >
> > 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 v7:
> > - Drop debugfs_reg_access for dual-channel AD4880 variant
> > - Pass struct device * to ad4080_properties_parse() instead of
> > using regmap_get_device(st->regmap[0])
> >
> > drivers/iio/adc/ad4080.c | 231 ++++++++++++++++++++++++++++++-----
> ----
> > 1 file changed, 181 insertions(+), 50 deletions(-)
> >
> > diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
> > index 7cf3b6ed7940..8767eef418e9 100644
> > --- a/drivers/iio/adc/ad4080.c
> > +++ b/drivers/iio/adc/ad4080.c
>
> > @@ -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,23 @@ 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), all backends share a single IIO buffer
> > + * as data from all ADC channels is interleaved into one stream.
> > + */
> > + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
>
> So this is the interleaving bit. Follows on from my question on the binding
> and whether it is appropriate to represent it as two separate backends
> vs a single one. With a single one we'd need to make the control interfaces
> take a parameter to say which 'front end' we were configuring - though it
> kind of maps to channels in the particular case and we already have
> a parameter for that.
>
> The other option might be to make the dt-binding take a phandle + index to
> say this backend, with this front end interface.
>
> > if (ret)
> > return ret;
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-22 16:26 ` Miclaus, Antoniu
@ 2026-03-22 17:39 ` Jonathan Cameron
2026-03-22 19:15 ` Miclaus, Antoniu
0 siblings, 1 reply; 17+ messages in thread
From: Jonathan Cameron @ 2026-03-22 17:39 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 Sun, 22 Mar 2026 16:26:58 +0000
"Miclaus, Antoniu" <Antoniu.Miclaus@analog.com> wrote:
> > -----Original Message-----
> > From: Jonathan Cameron <jic23@kernel.org>
> > Sent: Saturday, March 21, 2026 2:18 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 v7 4/4] iio: adc: ad4080: add support for AD4880 dual-
> > channel ADC
> >
> > [External]
> >
> > On Sat, 21 Mar 2026 12:01:54 +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).
> > >
> > > 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.
> >
> > Silly question - can we be sure that they both are on the same SPI bus?
> > I think it's reasonable to assume no one would burn pins to wire the
> > control interfaces up to separate busses. I'm not even sure how we'd
> > do a binding if they were on separate busses.
> >
> > Otherwise, a follow on from the 'is it one backend or two' question
> > on the binding.
> >
> > That long discussion between you and Andy has me looking at this a little
> > more closely.
>
> Yes, both channels are on the same SPI bus with two chip
> selects — one per internal ADC die.
Can you give a reference for this? The datasheet I'm looking at
has separate cs, sclk, sdi and sdo per channel. So it would be
a board thing only that puts them on the same SPI bus.
>
> For the backend question — as explained in my reply to the
> binding patch, the FPGA uses two separate axi_ad408x IP
> instances. The buffer is requested from back[0] because
> the packer output feeds the DMA through backend A's clock
> domain.
Ok. So given the packer part makes this really one FPGA IP
with two 'backend interfaces'.
Lets be clear about that in the comments etc.
Jonathan
>
> > Jonathan
> >
> > >
> > > 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 v7:
> > > - Drop debugfs_reg_access for dual-channel AD4880 variant
> > > - Pass struct device * to ad4080_properties_parse() instead of
> > > using regmap_get_device(st->regmap[0])
> > >
> > > drivers/iio/adc/ad4080.c | 231 ++++++++++++++++++++++++++++++-----
> > ----
> > > 1 file changed, 181 insertions(+), 50 deletions(-)
> > >
> > > diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
> > > index 7cf3b6ed7940..8767eef418e9 100644
> > > --- a/drivers/iio/adc/ad4080.c
> > > +++ b/drivers/iio/adc/ad4080.c
> >
> > > @@ -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,23 @@ 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), all backends share a single IIO buffer
> > > + * as data from all ADC channels is interleaved into one stream.
> > > + */
> > > + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
> >
> > So this is the interleaving bit. Follows on from my question on the binding
> > and whether it is appropriate to represent it as two separate backends
> > vs a single one. With a single one we'd need to make the control interfaces
> > take a parameter to say which 'front end' we were configuring - though it
> > kind of maps to channels in the particular case and we already have
> > a parameter for that.
> >
> > The other option might be to make the dt-binding take a phandle + index to
> > say this backend, with this front end interface.
> >
> > > if (ret)
> > > return ret;
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-22 17:39 ` Jonathan Cameron
@ 2026-03-22 19:15 ` Miclaus, Antoniu
0 siblings, 0 replies; 17+ messages in thread
From: Miclaus, Antoniu @ 2026-03-22 19:15 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
> -----Original Message-----
> From: Jonathan Cameron <jic23@kernel.org>
> Sent: Sunday, March 22, 2026 7:39 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 v7 4/4] iio: adc: ad4080: add support for AD4880 dual-
> channel ADC
>
> [External]
>
> On Sun, 22 Mar 2026 16:26:58 +0000
> "Miclaus, Antoniu" <Antoniu.Miclaus@analog.com> wrote:
>
> > > -----Original Message-----
> > > From: Jonathan Cameron <jic23@kernel.org>
> > > Sent: Saturday, March 21, 2026 2:18 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 v7 4/4] iio: adc: ad4080: add support for AD4880
> dual-
> > > channel ADC
> > >
> > > [External]
> > >
> > > On Sat, 21 Mar 2026 12:01:54 +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).
> > > >
> > > > 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.
> > >
> > > Silly question - can we be sure that they both are on the same SPI bus?
> > > I think it's reasonable to assume no one would burn pins to wire the
> > > control interfaces up to separate busses. I'm not even sure how we'd
> > > do a binding if they were on separate busses.
> > >
> > > Otherwise, a follow on from the 'is it one backend or two' question
> > > on the binding.
> > >
> > > That long discussion between you and Andy has me looking at this a little
> > > more closely.
> >
> > Yes, both channels are on the same SPI bus with two chip
> > selects — one per internal ADC die.
>
> Can you give a reference for this? The datasheet I'm looking at
> has separate cs, sclk, sdi and sdo per channel. So it would be
> a board thing only that puts them on the same SPI bus.
The AD4880 datasheet shows both topologies — Figure 70 with fully independent SPI interfaces
and Figure 71 with shared SCLK/SDI and separate chip selects.
The config SPI is only used for register access — the actual data capture happens over LVDS, where
both channels are interleaved into one stream no matter how the config SPI is wired. Since the
driver treats this as a single IIO device with one buffer (matching the interleaved stream), the
shared-bus model with spi_new_ancillary_device() fits naturally IMO — one probe, one device, one IIO
instance.
>
> >
> > For the backend question — as explained in my reply to the
> > binding patch, the FPGA uses two separate axi_ad408x IP
> > instances. The buffer is requested from back[0] because
> > the packer output feeds the DMA through backend A's clock
> > domain.
>
> Ok. So given the packer part makes this really one FPGA IP
> with two 'backend interfaces'.
>
> Lets be clear about that in the comments etc.
>
Sure, can do that in v8.
> Jonathan
>
> >
> > > Jonathan
> > >
> > > >
> > > > 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 v7:
> > > > - Drop debugfs_reg_access for dual-channel AD4880 variant
> > > > - Pass struct device * to ad4080_properties_parse() instead of
> > > > using regmap_get_device(st->regmap[0])
> > > >
> > > > drivers/iio/adc/ad4080.c | 231 ++++++++++++++++++++++++++++++-
> ----
> > > ----
> > > > 1 file changed, 181 insertions(+), 50 deletions(-)
> > > >
> > > > diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
> > > > index 7cf3b6ed7940..8767eef418e9 100644
> > > > --- a/drivers/iio/adc/ad4080.c
> > > > +++ b/drivers/iio/adc/ad4080.c
> > >
> > > > @@ -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,23 @@ 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), all backends share a single IIO buffer
> > > > + * as data from all ADC channels is interleaved into one stream.
> > > > + */
> > > > + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
> > >
> > > So this is the interleaving bit. Follows on from my question on the binding
> > > and whether it is appropriate to represent it as two separate backends
> > > vs a single one. With a single one we'd need to make the control interfaces
> > > take a parameter to say which 'front end' we were configuring - though it
> > > kind of maps to channels in the particular case and we already have
> > > a parameter for that.
> > >
> > > The other option might be to make the dt-binding take a phandle + index to
> > > say this backend, with this front end interface.
> > >
> > > > if (ret)
> > > > return ret;
> >
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-21 10:01 ` [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-03-21 12:18 ` Jonathan Cameron
@ 2026-03-23 7:59 ` kernel test robot
1 sibling, 0 replies; 17+ messages in thread
From: kernel test robot @ 2026-03-23 7:59 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, David Lechner, Nuno Sá, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Olivier Moysan, linux-iio,
devicetree, linux-kernel
Cc: oe-kbuild-all
Hi Antoniu,
kernel test robot noticed the following build warnings:
[auto build test WARNING on v7.0-rc4]
[also build test WARNING on linus/master]
[cannot apply to jic23-iio/togreg next-20260320]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Antoniu-Miclaus/iio-backend-use-__free-fwnode_handle-for-automatic-cleanup/20260322-200018
base: v7.0-rc4
patch link: https://lore.kernel.org/r/20260321100154.1258-5-antoniu.miclaus%40analog.com
patch subject: [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260323/202603231533.tcfdLyO9-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260323/202603231533.tcfdLyO9-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603231533.tcfdLyO9-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/iio/adc/ad4080.c: In function 'ad4080_probe':
drivers/iio/adc/ad4080.c:738:31: error: implicit declaration of function 'devm_spi_new_ancillary_device'; did you mean 'spi_new_ancillary_device'? [-Werror=implicit-function-declaration]
738 | st->spi[ch] = devm_spi_new_ancillary_device(spi, spi_get_chipselect(spi, ch));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| spi_new_ancillary_device
>> drivers/iio/adc/ad4080.c:738:29: warning: assignment to 'struct spi_device *' from 'int' makes pointer from integer without a cast [-Wint-conversion]
738 | st->spi[ch] = devm_spi_new_ancillary_device(spi, spi_get_chipselect(spi, ch));
| ^
cc1: some warnings being treated as errors
vim +738 drivers/iio/adc/ad4080.c
704
705 static int ad4080_probe(struct spi_device *spi)
706 {
707 struct iio_dev *indio_dev;
708 struct device *dev = &spi->dev;
709 struct ad4080_state *st;
710 struct clk *clk;
711 int ret;
712
713 indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
714 if (!indio_dev)
715 return -ENOMEM;
716
717 st = iio_priv(indio_dev);
718
719 ret = devm_regulator_bulk_get_enable(dev,
720 ARRAY_SIZE(ad4080_power_supplies),
721 ad4080_power_supplies);
722 if (ret)
723 return dev_err_probe(dev, ret,
724 "failed to get and enable supplies\n");
725
726 /* Setup primary SPI device (channel 0) */
727 st->spi[0] = spi;
728 st->regmap[0] = devm_regmap_init_spi(spi, &ad4080_regmap_config);
729 if (IS_ERR(st->regmap[0]))
730 return PTR_ERR(st->regmap[0]);
731
732 st->info = spi_get_device_match_data(spi);
733 if (!st->info)
734 return -ENODEV;
735
736 /* Setup ancillary SPI devices for additional channels */
737 for (unsigned int ch = 1; ch < st->info->num_channels; ch++) {
> 738 st->spi[ch] = devm_spi_new_ancillary_device(spi, spi_get_chipselect(spi, ch));
739 if (IS_ERR(st->spi[ch]))
740 return dev_err_probe(dev, PTR_ERR(st->spi[ch]),
741 "failed to register ancillary device\n");
742
743 st->regmap[ch] = devm_regmap_init_spi(st->spi[ch], &ad4080_regmap_config);
744 if (IS_ERR(st->regmap[ch]))
745 return PTR_ERR(st->regmap[ch]);
746 }
747
748 ret = devm_mutex_init(dev, &st->lock);
749 if (ret)
750 return ret;
751
752 indio_dev->name = st->info->name;
753 indio_dev->channels = st->info->channels;
754 indio_dev->num_channels = st->info->num_channels;
755 indio_dev->info = st->info->num_channels > 1 ?
756 &ad4880_iio_info : &ad4080_iio_info;
757
758 ret = ad4080_properties_parse(st, dev);
759 if (ret)
760 return ret;
761
762 clk = devm_clk_get_enabled(&spi->dev, "cnv");
763 if (IS_ERR(clk))
764 return PTR_ERR(clk);
765
766 st->clk_rate = clk_get_rate(clk);
767
768 /* Get backends for all channels */
769 for (unsigned int ch = 0; ch < st->info->num_channels; ch++) {
770 st->back[ch] = devm_iio_backend_get_by_index(dev, ch);
771 if (IS_ERR(st->back[ch]))
772 return PTR_ERR(st->back[ch]);
773
774 ret = devm_iio_backend_enable(dev, st->back[ch]);
775 if (ret)
776 return ret;
777 }
778
779 /*
780 * Request buffer from the first backend only. For multi-channel
781 * devices (e.g., AD4880), all backends share a single IIO buffer
782 * as data from all ADC channels is interleaved into one stream.
783 */
784 ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
785 if (ret)
786 return ret;
787
788 ret = ad4080_setup(indio_dev);
789 if (ret)
790 return ret;
791
792 return devm_iio_device_register(&spi->dev, indio_dev);
793 }
794
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-03-22 15:33 ` Miclaus, Antoniu
@ 2026-03-23 10:57 ` Nuno Sá
0 siblings, 0 replies; 17+ messages in thread
From: Nuno Sá @ 2026-03-23 10:57 UTC (permalink / raw)
To: Miclaus, Antoniu, 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
On Sun, 2026-03-22 at 15:33 +0000, Miclaus, Antoniu wrote:
> > -----Original Message-----
> > From: Jonathan Cameron <jic23@kernel.org>
> > Sent: Saturday, March 21, 2026 2:08 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 v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880
> > support
> >
> > [External]
> >
> > On Sat, 21 Mar 2026 12:01:53 +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).
> > >
> > > 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
> > From the v6 discussion. I'd just like to know a little more on this.
> > Are they really separate backends?
> >
> Yes, they are separate backends. The FPGA reference design
> instantiates two independent axi_ad408x IP cores
> The AD4880 is essentially two AD4080 dies in one package.
>
> The two data streams are merged by a util_cpack2 channel
> packer before reaching a single DMA:
>
> axi_ad408x (ch A) --
> --> util_cpack2 --> single DMA
> axi_ad408x (ch B) --/
>
> Each backend needs independent LVDS alignment and lane
> configuration, so a single-backend-with-channel-parameter
> model wouldn't fit here.
>
> https://github.com/analogdevicesinc/hdl/blob/main/projects/ad4880_fmc_evb/common/ad4880_fmc_evb_bd.tcl
I was not really involved in this part but this whole discussion got me curious about why are
we going with the interleaved approach? Typically when each path has it's own serial interface, we
also have a separate DMA channel.
- Nuno Sá
>
> > Given discussion about interleaved data, I was kind of assuming they
> > were different front end interfaces to a single backend IP.
> >
> > The freedom this binding is giving is for those two backends to be
> > completely unrelated. I'm not sure if we want that.
> >
> > Jonathan
> >
> >
> > >
> > > Reviewed-by: David Lechner <dlechner@baylibre.com>
> > > Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> > > ---
> > > Changes in v7:
> > > - 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>;
> > > + };
> > > + };
> > > ...
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-03-21 10:01 [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
` (4 preceding siblings ...)
2026-03-21 12:25 ` [PATCH v7 0/4] " Jonathan Cameron
@ 2026-03-23 10:58 ` Jonathan Cameron
5 siblings, 0 replies; 17+ messages in thread
From: Jonathan Cameron @ 2026-03-23 10:58 UTC (permalink / raw)
To: Antoniu Miclaus
Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Nuno Sá, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Olivier Moysan, linux-iio, devicetree, linux-kernel
On Sat, 21 Mar 2026 12:01:50 +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).
Given the lkml cc this got picked up by sashiko:
https://sashiko.dev/#/patchset/20260321100154.1258-1-antoniu.miclaus%40analog.com
Mixed bag on correct and (I think) incorrect stuff but take a look and fix
up anything you agree with for v8.
Thanks,
Jonathan
>
> 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 v7:
> - Drop debugfs_reg_access for dual-channel AD4880 variant
> - Pass struct device * to ad4080_properties_parse() instead of
> using regmap_get_device(st->regmap[0])
> - Use 100-column limit consistently for function signatures
> - Add architecture summary to cover letter (per Andy's request)
>
> Antoniu Miclaus (4):
> iio: backend: use __free(fwnode_handle) for automatic cleanup
> 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 | 231 ++++++++++++++----
> drivers/iio/industrialio-backend.c | 59 +++--
> include/linux/iio/backend.h | 1 +
> 4 files changed, 273 insertions(+), 71 deletions(-)
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-03-21 10:01 ` [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
2026-03-21 12:08 ` Jonathan Cameron
@ 2026-03-23 19:44 ` Conor Dooley
1 sibling, 0 replies; 17+ messages in thread
From: Conor Dooley @ 2026-03-23 19:44 UTC (permalink / raw)
To: Antoniu Miclaus
Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Nuno Sá, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Olivier Moysan, linux-iio, devicetree, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 647 bytes --]
On Sat, Mar 21, 2026 at 12:01:53PM +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>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-03-23 19:44 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-21 10:01 [PATCH v7 0/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-03-21 10:01 ` [PATCH v7 1/4] iio: backend: use __free(fwnode_handle) for automatic cleanup Antoniu Miclaus
2026-03-21 12:03 ` Jonathan Cameron
2026-03-21 10:01 ` [PATCH v7 2/4] iio: backend: add devm_iio_backend_get_by_index() Antoniu Miclaus
2026-03-21 10:01 ` [PATCH v7 3/4] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
2026-03-21 12:08 ` Jonathan Cameron
2026-03-22 15:33 ` Miclaus, Antoniu
2026-03-23 10:57 ` Nuno Sá
2026-03-23 19:44 ` Conor Dooley
2026-03-21 10:01 ` [PATCH v7 4/4] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-03-21 12:18 ` Jonathan Cameron
2026-03-22 16:26 ` Miclaus, Antoniu
2026-03-22 17:39 ` Jonathan Cameron
2026-03-22 19:15 ` Miclaus, Antoniu
2026-03-23 7:59 ` kernel test robot
2026-03-21 12:25 ` [PATCH v7 0/4] " Jonathan Cameron
2026-03-23 10:58 ` Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox