* [PATCH 0/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC
@ 2026-01-29 15:27 Antoniu Miclaus
2026-01-29 15:27 ` [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
2026-01-29 15:27 ` [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
0 siblings, 2 replies; 9+ messages in thread
From: Antoniu Miclaus @ 2026-01-29 15:27 UTC (permalink / raw)
To: Lars-Peter Clausen, Michael Hennerich, Antoniu Miclaus,
Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-iio,
devicetree, linux-kernel
Add support for the AD4880, a dual-channel 20-bit 40MSPS SAR ADC from
the same family as AD4080.
The AD4880 has two independent ADC channels, each with its own SPI
configuration interface and LVDS data output. The driver uses
spi_new_ancillary_device() for the second channel's SPI and requires
two io-backend instances for the data interfaces.
Driver changes:
- Extend state structure with arrays for regmaps, backends, and SPI devices
- Refactor setup into per-channel function
- Make filter_type, sampling_frequency, and oversampling_ratio per-channel
- Add update_scan_mode callback for multi-channel buffer handling
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad4880.pdf
Antoniu Miclaus (2):
dt-bindings: iio: adc: ad4080: add AD4880 support
iio: adc: ad4080: add support for AD4880 dual-channel ADC
.../bindings/iio/adc/adi,ad4080.yaml | 49 +++-
drivers/iio/adc/ad4080.c | 236 +++++++++++++-----
2 files changed, 220 insertions(+), 65 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-01-29 15:27 [PATCH 0/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
@ 2026-01-29 15:27 ` Antoniu Miclaus
2026-01-29 16:20 ` David Lechner
2026-01-29 15:27 ` [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
1 sibling, 1 reply; 9+ messages in thread
From: Antoniu Miclaus @ 2026-01-29 15:27 UTC (permalink / raw)
To: Lars-Peter Clausen, Michael Hennerich, Antoniu Miclaus,
Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-iio,
devicetree, linux-kernel
Add AD4880 dual-channel ADC to the AD4080 bindings. The AD4880 is a
dual-channel variant with two independent ADC channels, each with its
own SPI configuration interface.
For AD4880, the binding requires:
- adi,aux-spi-cs property for secondary channel chip select
- Two io-backends entries for each channel's data interface
Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
.../bindings/iio/adc/adi,ad4080.yaml | 49 ++++++++++++++++++-
1 file changed, 48 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml
index ccd6a0ac1539..3909e3095507 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml
@@ -18,6 +18,9 @@ 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
$ref: /schemas/spi/spi-peripheral-props.yaml#
@@ -31,10 +34,18 @@ properties:
- adi,ad4084
- adi,ad4086
- adi,ad4087
+ - adi,ad4880
reg:
maxItems: 1
+ adi,aux-spi-cs:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Chip select for the auxiliary SPI interface used by multi-channel
+ devices like AD4880. Each additional channel beyond the first requires
+ its own SPI configuration interface on a separate chip select.
+
spi-max-frequency:
description: Configuration of the SPI bus.
maximum: 50000000
@@ -57,7 +68,8 @@ properties:
vrefin-supply: true
io-backends:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
adi,lvds-cnv-enable:
description: Enable the LVDS signal type on the CNV pin. Default is CMOS.
@@ -78,6 +90,23 @@ required:
- vdd33-supply
- vrefin-supply
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: adi,ad4880
+ then:
+ properties:
+ io-backends:
+ minItems: 2
+ required:
+ - adi,aux-spi-cs
+ else:
+ properties:
+ io-backends:
+ maxItems: 1
+
additionalProperties: false
examples:
@@ -98,4 +127,22 @@ examples:
io-backends = <&iio_backend>;
};
};
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ adc@0 {
+ compatible = "adi,ad4880";
+ reg = <0>;
+ adi,aux-spi-cs = <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
* [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-01-29 15:27 [PATCH 0/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-01-29 15:27 ` [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
@ 2026-01-29 15:27 ` Antoniu Miclaus
2026-01-29 15:56 ` Andy Shevchenko
` (2 more replies)
1 sibling, 3 replies; 9+ messages in thread
From: Antoniu Miclaus @ 2026-01-29 15:27 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, 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.
Key changes:
- Add AD4880 chip info with 2 channels
- Extend state structure to support arrays of regmaps and backends
- Refactor setup into per-channel function
- Add adi,aux-spi-cs property for secondary channel chip select
- Add channel index parameter to channel macro for scan_index support
- Make all IIO attributes per-channel (filter_type, oversampling_ratio,
sampling_frequency) for independent channel configuration
Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
---
drivers/iio/adc/ad4080.c | 236 ++++++++++++++++++++++++++++-----------
1 file changed, 172 insertions(+), 64 deletions(-)
diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
index 7cf3b6ed7940..e588ff23a7a5 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;
};
@@ -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)
+ 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)));
@@ -257,6 +265,7 @@ static int ad4080_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long m)
{
struct ad4080_state *st = iio_priv(indio_dev);
+ unsigned int ch = chan->channel;
int dec_rate;
switch (m) {
@@ -266,15 +275,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[ch] == SINC_5_COMP)
dec_rate *= 2;
- if (st->filter_type)
+ if (st->filter_type[ch])
*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[ch] == FILTER_NONE) {
*val = 1;
} else {
*val = ad4080_get_dec_rate(indio_dev, chan);
@@ -292,10 +301,11 @@ static int ad4080_write_raw(struct iio_dev *indio_dev,
int val, int val2, long mask)
{
struct ad4080_state *st = iio_priv(indio_dev);
+ unsigned int ch = chan->channel;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- if (st->filter_type == FILTER_NONE && val > 1)
+ if (st->filter_type[ch] == FILTER_NONE && val > 1)
return -EINVAL;
return ad4080_set_dec_rate(indio_dev, chan, val);
@@ -304,23 +314,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 +339,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 +354,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 +367,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;
}
@@ -377,17 +389,18 @@ static int ad4080_read_avail(struct iio_dev *indio_dev,
long mask)
{
struct ad4080_state *st = iio_priv(indio_dev);
+ unsigned int ch = chan->channel;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- switch (st->filter_type) {
+ switch (st->filter_type[ch]) {
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[ch] >= SINC_5 ?
(ARRAY_SIZE(ad4080_dec_rate_avail) - 2) :
ARRAY_SIZE(ad4080_dec_rate_avail);
break;
@@ -399,11 +412,35 @@ static int ad4080_read_avail(struct iio_dev *indio_dev,
}
}
+static int ad4080_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct ad4080_state *st = iio_priv(indio_dev);
+ unsigned int ch;
+ int ret;
+
+ for (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,
.write_raw = ad4080_write_raw,
.read_avail = ad4080_read_avail,
+ .update_scan_mode = ad4080_update_scan_mode,
};
static const struct iio_enum ad4080_filter_type_enum = {
@@ -414,23 +451,23 @@ static const struct iio_enum ad4080_filter_type_enum = {
};
static struct iio_chan_spec_ext_info ad4080_ext_info[] = {
- IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad4080_filter_type_enum),
- IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL,
+ 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) { \
+#define AD4080_CHANNEL_DEFINE(bits, storage, idx) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
- .channel = 0, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ .channel = (idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
- .info_mask_shared_by_all_available = \
+ .info_mask_separate_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 +475,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, 0);
-static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32);
+static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16, 0);
-static const struct iio_chan_spec ad4083_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 ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16);
+static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16, 0);
-static const struct iio_chan_spec ad4086_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 ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16);
+static const struct iio_chan_spec ad4880_channels[] = {
+ AD4080_CHANNEL_DEFINE(20, 32, 0),
+ AD4080_CHANNEL_DEFINE(20, 32, 1),
+};
static const struct ad4080_chip_info ad4080_chip_info = {
.name = "ad4080",
@@ -510,25 +552,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 +588,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 +607,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 +616,39 @@ 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_setup(struct iio_dev *indio_dev)
+{
+ struct ad4080_state *st = iio_priv(indio_dev);
+ unsigned int ch;
+ int ret;
+
+ for (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 = regmap_get_device(st->regmap);
+ struct device *dev = regmap_get_device(st->regmap[0]);
st->lvds_cnv_en = device_property_read_bool(dev, "adi,lvds-cnv-enable");
@@ -596,12 +662,19 @@ static int ad4080_properties_parse(struct ad4080_state *st)
return 0;
}
+static void ad4080_unregister_ancillary(void *data)
+{
+ spi_unregister_device(data);
+}
+
static int ad4080_probe(struct spi_device *spi)
{
+ static const char * const backend_names[] = { "0", "1" };
struct iio_dev *indio_dev;
struct device *dev = &spi->dev;
struct ad4080_state *st;
struct clk *clk;
+ unsigned int ch;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
@@ -610,6 +683,10 @@ static int ad4080_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
+ st->info = spi_get_device_match_data(spi);
+ if (!st->info)
+ return -ENODEV;
+
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(ad4080_power_supplies),
ad4080_power_supplies);
@@ -617,13 +694,35 @@ 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 device for additional channel (AD4880) */
+ if (st->info->num_channels > 1) {
+ u32 aux_cs;
+
+ ret = device_property_read_u32(dev, "adi,aux-spi-cs", &aux_cs);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "missing adi,aux-spi-cs for multi-channel device\n");
+
+ st->spi[1] = spi_new_ancillary_device(spi, aux_cs);
+ if (IS_ERR(st->spi[1]))
+ return PTR_ERR(st->spi[1]);
+
+ ret = devm_add_action_or_reset(dev, ad4080_unregister_ancillary,
+ st->spi[1]);
+ if (ret)
+ return ret;
+
+ st->regmap[1] = devm_regmap_init_spi(st->spi[1],
+ &ad4080_regmap_config);
+ if (IS_ERR(st->regmap[1]))
+ return PTR_ERR(st->regmap[1]);
+ }
ret = devm_mutex_init(dev, &st->lock);
if (ret)
@@ -644,15 +743,22 @@ 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 (ch = 0; ch < st->info->num_channels; ch++) {
+ if (st->info->num_channels > 1)
+ st->back[ch] = devm_iio_backend_get(dev, backend_names[ch]);
+ else
+ st->back[ch] = devm_iio_backend_get(dev, NULL);
- ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
- if (ret)
- return ret;
+ if (IS_ERR(st->back[ch]))
+ return PTR_ERR(st->back[ch]);
+
+ ret = devm_iio_backend_enable(dev, st->back[ch]);
+ if (ret)
+ return ret;
+ }
- ret = devm_iio_backend_enable(dev, st->back);
+ ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
if (ret)
return ret;
@@ -670,6 +776,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 +788,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 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-01-29 15:27 ` [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
@ 2026-01-29 15:56 ` Andy Shevchenko
2026-01-29 16:39 ` David Lechner
2026-01-31 18:19 ` Jonathan Cameron
2 siblings, 0 replies; 9+ messages in thread
From: Andy Shevchenko @ 2026-01-29 15:56 UTC (permalink / raw)
To: Antoniu Miclaus
Cc: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-iio, devicetree,
linux-kernel
On Thu, Jan 29, 2026 at 05:27:30PM +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. 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.
> Key changes:
> - Add AD4880 chip info with 2 channels
> - Extend state structure to support arrays of regmaps and backends
> - Refactor setup into per-channel function
> - Add adi,aux-spi-cs property for secondary channel chip select
> - Add channel index parameter to channel macro for scan_index support
> - Make all IIO attributes per-channel (filter_type, oversampling_ratio,
> sampling_frequency) for independent channel configuration
Can something be split as prerequisite?
Overall, making
struct regmap *map = ...->...[ch];
will help to make many lines of code easier to read.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-01-29 15:27 ` [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
@ 2026-01-29 16:20 ` David Lechner
2026-02-03 15:42 ` Miclaus, Antoniu
0 siblings, 1 reply; 9+ messages in thread
From: David Lechner @ 2026-01-29 16:20 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-iio, devicetree,
linux-kernel
On 1/29/26 9:27 AM, Antoniu Miclaus wrote:
> Add AD4880 dual-channel ADC to the AD4080 bindings. The AD4880 is a
> dual-channel variant with two independent ADC channels, each with its
> own SPI configuration interface.
>
> For AD4880, the binding requires:
> - adi,aux-spi-cs property for secondary channel chip select
> - Two io-backends entries for each channel's data interface
>
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
> ---
> .../bindings/iio/adc/adi,ad4080.yaml | 49 ++++++++++++++++++-
> 1 file changed, 48 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml
> index ccd6a0ac1539..3909e3095507 100644
> --- a/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml
> +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml
> @@ -18,6 +18,9 @@ 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
Can we get a datasheet link?
>
> $ref: /schemas/spi/spi-peripheral-props.yaml#
> @@ -31,10 +34,18 @@ properties:
> - adi,ad4084
> - adi,ad4086
> - adi,ad4087
> + - adi,ad4880
>
> reg:
> maxItems: 1
>
> + adi,aux-spi-cs:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description:
> + Chip select for the auxiliary SPI interface used by multi-channel
> + devices like AD4880. Each additional channel beyond the first requires
> + its own SPI configuration interface on a separate chip select.
> +
> spi-max-frequency:
> description: Configuration of the SPI bus.
> maximum: 50000000
> @@ -57,7 +68,8 @@ properties:
> vrefin-supply: true
>
> io-backends:
> - maxItems: 1
> + minItems: 1
> + maxItems: 2
>
> adi,lvds-cnv-enable:
> description: Enable the LVDS signal type on the CNV pin. Default is CMOS.
> @@ -78,6 +90,23 @@ required:
> - vdd33-supply
> - vrefin-supply
>
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: adi,ad4880
> + then:
> + properties:
> + io-backends:
> + minItems: 2
> + required:
> + - adi,aux-spi-cs
> + else:
> + properties:
> + io-backends:
> + maxItems: 1
> +
> additionalProperties: false
>
> examples:
> @@ -98,4 +127,22 @@ examples:
> io-backends = <&iio_backend>;
> };
> };
> + - |
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + adc@0 {
> + compatible = "adi,ad4880";
> + reg = <0>;
> + adi,aux-spi-cs = <1>;
We can already have multiple reg for a multiple CS device.
reg = <0>, <1>;
So we shouldn't need a new adi,aux-spi-cs property for that.
> + 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] 9+ messages in thread
* Re: [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-01-29 15:27 ` [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-01-29 15:56 ` Andy Shevchenko
@ 2026-01-29 16:39 ` David Lechner
2026-01-31 18:19 ` Jonathan Cameron
2 siblings, 0 replies; 9+ messages in thread
From: David Lechner @ 2026-01-29 16:39 UTC (permalink / raw)
To: Antoniu Miclaus, Lars-Peter Clausen, Michael Hennerich,
Jonathan Cameron, Nuno Sá, Andy Shevchenko, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-iio, devicetree,
linux-kernel
On 1/29/26 9:27 AM, 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. 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.
>
...
> +static int ad4080_update_scan_mode(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct ad4080_state *st = iio_priv(indio_dev);
> + unsigned int ch;
> + int ret;
> +
> + for (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;
Previously, the single-channel chips didn't call backend channel
enable/disable. It this going to cause problems for that? I.e. the
function isn't implemented in the backend?
> + }
> +
> + return 0;
> +}
> +
> static const struct iio_info ad4080_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 = ad4080_update_scan_mode,
> };
>
> static const struct iio_enum ad4080_filter_type_enum = {
> @@ -414,23 +451,23 @@ static const struct iio_enum ad4080_filter_type_enum = {
> };
>
> static struct iio_chan_spec_ext_info ad4080_ext_info[] = {
> - IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad4080_filter_type_enum),
> - IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL,
> + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4080_filter_type_enum),
> + IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE,
> &ad4080_filter_type_enum),
This is a breaking ABI change, so could be problamatic. We probably need to
keep the old info[] for the single channle chips and make a new info[] for
the multi-channels chips to avoid breaking things.
> { }
> };
>
...
> @@ -617,13 +694,35 @@ 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 device for additional channel (AD4880) */
> + if (st->info->num_channels > 1) {
> + u32 aux_cs;
> +
> + ret = device_property_read_u32(dev, "adi,aux-spi-cs", &aux_cs);
As in the DT bindings comment, this sould be looking up the value at index 1
of the reg property rather than a custom property.
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "missing adi,aux-spi-cs for multi-channel device\n");
> +
> + st->spi[1] = spi_new_ancillary_device(spi, aux_cs);
> + if (IS_ERR(st->spi[1]))
> + return PTR_ERR(st->spi[1]);
> +
> + ret = devm_add_action_or_reset(dev, ad4080_unregister_ancillary,
> + st->spi[1]);
> + if (ret)
> + return ret;
> +
> + st->regmap[1] = devm_regmap_init_spi(st->spi[1],
> + &ad4080_regmap_config);
> + if (IS_ERR(st->regmap[1]))
> + return PTR_ERR(st->regmap[1]);
> + }
>
> ret = devm_mutex_init(dev, &st->lock);
> if (ret)
> @@ -644,15 +743,22 @@ 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 (ch = 0; ch < st->info->num_channels; ch++) {
> + if (st->info->num_channels > 1)
> + st->back[ch] = devm_iio_backend_get(dev, backend_names[ch]);
It looks like we missed io-backend-names from the DT bindings. In this case
though, going by index instead of name would make more sense. So we could add
a devm_iio_backend_get_by_index() function instead of modifying the DT bindings.
Then we wouldn't need the if statement here.
> + else
> + st->back[ch] = devm_iio_backend_get(dev, NULL);
>
> - ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
> - if (ret)
> - return ret;
> + if (IS_ERR(st->back[ch]))
> + return PTR_ERR(st->back[ch]);
> +
> + ret = devm_iio_backend_enable(dev, st->back[ch]);
> + if (ret)
> + return ret;
> + }
>
> - ret = devm_iio_backend_enable(dev, st->back);
> + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
If this is correct, it could use a comment explaining why only one
backend has the buffer even if there are two backends.
> if (ret)
> return ret;
>
> @@ -670,6 +776,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 +788,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);
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC
2026-01-29 15:27 ` [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-01-29 15:56 ` Andy Shevchenko
2026-01-29 16:39 ` David Lechner
@ 2026-01-31 18:19 ` Jonathan Cameron
2 siblings, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2026-01-31 18:19 UTC (permalink / raw)
To: Antoniu Miclaus
Cc: Lars-Peter Clausen, Michael Hennerich, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-iio, devicetree, linux-kernel
On Thu, 29 Jan 2026 17:27:30 +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.
>
> Key changes:
> - Add AD4880 chip info with 2 channels
> - Extend state structure to support arrays of regmaps and backends
> - Refactor setup into per-channel function
> - Add adi,aux-spi-cs property for secondary channel chip select
> - Add channel index parameter to channel macro for scan_index support
> - Make all IIO attributes per-channel (filter_type, oversampling_ratio,
> sampling_frequency) for independent channel configuration
>
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
Hi Antoniu
A few additional comments from me to add to those of the other reviewers.
Jonathan
> ---
> drivers/iio/adc/ad4080.c | 236 ++++++++++++++++++++++++++++-----------
> 1 file changed, 172 insertions(+), 64 deletions(-)
>
> diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c
> index 7cf3b6ed7940..e588ff23a7a5 100644
> --- a/drivers/iio/adc/ad4080.c
> +++ b/drivers/iio/adc/ad4080.c
> static int ad4080_probe(struct spi_device *spi)
> {
> + static const char * const backend_names[] = { "0", "1" };
> struct iio_dev *indio_dev;
> struct device *dev = &spi->dev;
> struct ad4080_state *st;
> struct clk *clk;
> + unsigned int ch;
> int ret;
>
> indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> @@ -610,6 +683,10 @@ static int ad4080_probe(struct spi_device *spi)
>
> st = iio_priv(indio_dev);
>
> + st->info = spi_get_device_match_data(spi);
> + if (!st->info)
> + return -ENODEV;
> +
> ret = devm_regulator_bulk_get_enable(dev,
> ARRAY_SIZE(ad4080_power_supplies),
> ad4080_power_supplies);
> @@ -617,13 +694,35 @@ 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 device for additional channel (AD4880) */
> + if (st->info->num_channels > 1) {
This hard codes assumption that it is 2. So perhaps better to check for
that explicitly rather than simply > 1.
Maybe turn this into an appropriate loop so you don't need to care that
it's 2 or bigger.
> + u32 aux_cs;
> +
> + ret = device_property_read_u32(dev, "adi,aux-spi-cs", &aux_cs);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "missing adi,aux-spi-cs for multi-channel device\n");
> +
> + st->spi[1] = spi_new_ancillary_device(spi, aux_cs);
> + if (IS_ERR(st->spi[1]))
> + return PTR_ERR(st->spi[1]);
> +
> + ret = devm_add_action_or_reset(dev, ad4080_unregister_ancillary,
> + st->spi[1]);
> + if (ret)
> + return ret;
> +
> + st->regmap[1] = devm_regmap_init_spi(st->spi[1],
> + &ad4080_regmap_config);
> + if (IS_ERR(st->regmap[1]))
> + return PTR_ERR(st->regmap[1]);
> + }
>
> ret = devm_mutex_init(dev, &st->lock);
> if (ret)
> @@ -644,15 +743,22 @@ 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 (ch = 0; ch < st->info->num_channels; ch++) {
> + if (st->info->num_channels > 1)
> + st->back[ch] = devm_iio_backend_get(dev, backend_names[ch]);
> + else
> + st->back[ch] = devm_iio_backend_get(dev, NULL);
To me, it makes sense to always use names for multi channel devices and only
fall back to this if there is only one supported channel.
Something like
char *name = NULL;
if (st->info->num_channels != 1)
name = backend_names[ch];
st->back[ch] = devm_iio_backend_get(dev, name);
>
> - ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
> - if (ret)
> - return ret;
> + if (IS_ERR(st->back[ch]))
> + return PTR_ERR(st->back[ch]);
> +
> + ret = devm_iio_backend_enable(dev, st->back[ch]);
This changes the ordering so we now enable it before requesting the buffer.
That may well be fine, but I'd kind of prefer that to be made clear. Perhaps
with a precursor patch reorganizing that order where you can talk about why
it is fine to do so.
> + if (ret)
> + return ret;
> + }
>
> - ret = devm_iio_backend_enable(dev, st->back);
> + ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
Add a comment on why requesting only the first buffer is enough.
> if (ret)
> return ret;
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-01-29 16:20 ` David Lechner
@ 2026-02-03 15:42 ` Miclaus, Antoniu
2026-02-03 17:11 ` David Lechner
0 siblings, 1 reply; 9+ messages in thread
From: Miclaus, Antoniu @ 2026-02-03 15:42 UTC (permalink / raw)
To: David Lechner
Cc: Lars-Peter Clausen, Hennerich, Michael, Jonathan Cameron,
Sa, Nuno, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
...
> > + then:
> > + properties:
> > + io-backends:
> > + minItems: 2
> > + required:
> > + - adi,aux-spi-cs
> > + else:
> > + properties:
> > + io-backends:
> > + maxItems: 1
> > +
> > additionalProperties: false
> >
> > examples:
> > @@ -98,4 +127,22 @@ examples:
> > io-backends = <&iio_backend>;
> > };
> > };
> > + - |
> > + spi {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > +
> > + adc@0 {
> > + compatible = "adi,ad4880";
> > + reg = <0>;
> > + adi,aux-spi-cs = <1>;
>
> We can already have multiple reg for a multiple CS device.
>
> reg = <0>, <1>;
>
> So we shouldn't need a new adi,aux-spi-cs property for that.
Sure, that was the initial plan but for some reason going for multiple regs gets me to this:
[ 11.396833] spi_new_ancillary_device from ad4080_probe+0x4b8/0x5a4
[ 11.396861] ad4080_probe from really_probe+0xc8/0x2c8
[ 11.397281] ad4080 spi1.0: chipselect 1 already in use
[ 11.397297] ad4080 spi1.0: failed to register ancillary device
[ 11.397317] ad4080 spi1.0: probe with driver ad4080 failed with error -16
While the aux-spi-cs works fine. Any tips?
>
> > + 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] 9+ messages in thread
* Re: [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support
2026-02-03 15:42 ` Miclaus, Antoniu
@ 2026-02-03 17:11 ` David Lechner
0 siblings, 0 replies; 9+ messages in thread
From: David Lechner @ 2026-02-03 17:11 UTC (permalink / raw)
To: Miclaus, Antoniu
Cc: Lars-Peter Clausen, Hennerich, Michael, Jonathan Cameron,
Sa, Nuno, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-iio@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
On 2/3/26 9:42 AM, Miclaus, Antoniu wrote:
> ...
...
>>> @@ -98,4 +127,22 @@ examples:
>>> io-backends = <&iio_backend>;
>>> };
>>> };
>>> + - |
>>> + spi {
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + adc@0 {
>>> + compatible = "adi,ad4880";
>>> + reg = <0>;
>>> + adi,aux-spi-cs = <1>;
>>
>> We can already have multiple reg for a multiple CS device.
>>
>> reg = <0>, <1>;
>>
>> So we shouldn't need a new adi,aux-spi-cs property for that.
>
> Sure, that was the initial plan but for some reason going for multiple regs gets me to this:
>
> [ 11.396833] spi_new_ancillary_device from ad4080_probe+0x4b8/0x5a4
> [ 11.396861] ad4080_probe from really_probe+0xc8/0x2c8
> [ 11.397281] ad4080 spi1.0: chipselect 1 already in use
> [ 11.397297] ad4080 spi1.0: failed to register ancillary device
> [ 11.397317] ad4080 spi1.0: probe with driver ad4080 failed with error -16
>
> While the aux-spi-cs works fine. Any tips?
The one existing user of spi_new_ancillary_device() used the multiple
regs like I suggested. So I wonder if that is broken too. The changes
for multiple CS for SPI parallel memories in the SPI core was made 2
years after that driver was added. So I could see how it could have
been broken without anyone noticing.
I didn't look at it too deeply yet. But my guess is that we need to
modify __spi_add_device() to change how it verifies the chip select.
If it is being called from spi_add_device(), don't change the behavior.
If it is being called from spi_new_ancillary_device(), then the
chip select should be verified as one assigned to the main device
rather than verifying that it is unused. I.e. don't call spi_dev_check_cs().
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-02-03 17:11 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-29 15:27 [PATCH 0/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-01-29 15:27 ` [PATCH 1/2] dt-bindings: iio: adc: ad4080: add AD4880 support Antoniu Miclaus
2026-01-29 16:20 ` David Lechner
2026-02-03 15:42 ` Miclaus, Antoniu
2026-02-03 17:11 ` David Lechner
2026-01-29 15:27 ` [PATCH 2/2] iio: adc: ad4080: add support for AD4880 dual-channel ADC Antoniu Miclaus
2026-01-29 15:56 ` Andy Shevchenko
2026-01-29 16:39 ` David Lechner
2026-01-31 18:19 ` Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox