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
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
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;
© 2016 - 2026 Red Hat, Inc.