iio: adc: ad4080: add support for AD4880 dual-channel ADC

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>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
This commit is contained in:
Antoniu Miclaus 2026-04-20 13:12:25 +03:00 committed by Jonathan Cameron
parent 80cc6d13d1
commit 370461ac8d

View File

@ -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>
@ -134,6 +135,9 @@
#define AD4086_CHIP_ID 0x0056
#define AD4087_CHIP_ID 0x0057
#define AD4088_CHIP_ID 0x0058
#define AD4880_CHIP_ID 0x0750
#define AD4080_MAX_CHANNELS 2
#define AD4080_LVDS_CNV_CLK_CNT_MAX 7
@ -179,8 +183,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
@ -189,7 +194,7 @@ struct ad4080_state {
struct mutex lock;
unsigned int num_lanes;
unsigned long clk_rate;
enum ad4080_filter_type filter_type;
enum ad4080_filter_type filter_type[AD4080_MAX_CHANNELS];
bool lvds_cnv_en;
};
@ -206,9 +211,9 @@ static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg,
struct ad4080_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
return regmap_read(st->regmap[0], reg, readval);
return regmap_write(st->regmap, reg, writeval);
return regmap_write(st->regmap[0], reg, writeval);
}
static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2)
@ -229,8 +234,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;
@ -242,13 +248,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)));
@ -268,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[chan->channel] == SINC_5_COMP)
dec_rate *= 2;
if (st->filter_type)
if (st->filter_type[chan->channel])
*val = DIV_ROUND_CLOSEST(st->clk_rate, dec_rate);
else
*val = st->clk_rate;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (st->filter_type == FILTER_NONE) {
if (st->filter_type[chan->channel] == FILTER_NONE) {
*val = 1;
} else {
*val = ad4080_get_dec_rate(indio_dev, chan);
@ -297,7 +304,7 @@ static int ad4080_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (st->filter_type == FILTER_NONE && val > 1)
if (st->filter_type[chan->channel] == FILTER_NONE && val > 1)
return -EINVAL;
return ad4080_set_dec_rate(indio_dev, chan, val);
@ -306,23 +313,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);
}
@ -331,9 +338,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;
@ -345,6 +353,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;
@ -357,18 +366,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;
}
@ -382,14 +391,14 @@ static int ad4080_read_avail(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (st->filter_type) {
switch (st->filter_type[chan->channel]) {
case FILTER_NONE:
*vals = ad4080_dec_rate_none;
*length = ARRAY_SIZE(ad4080_dec_rate_none);
break;
default:
*vals = ad4080_dec_rate_avail;
*length = st->filter_type >= SINC_5 ?
*length = st->filter_type[chan->channel] >= SINC_5 ?
(ARRAY_SIZE(ad4080_dec_rate_avail) - 2) :
ARRAY_SIZE(ad4080_dec_rate_avail);
break;
@ -401,6 +410,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,
@ -408,6 +439,19 @@ static const struct iio_info ad4080_iio_info = {
.read_avail = ad4080_read_avail,
};
/*
* AD4880 needs update_scan_mode to enable/disable individual backend channels.
* Single-channel devices don't need this as their backends may not implement
* chan_enable/chan_disable operations.
*/
static const struct iio_info ad4880_iio_info = {
.debugfs_reg_access = ad4080_reg_access,
.read_raw = ad4080_read_raw,
.write_raw = ad4080_write_raw,
.read_avail = ad4080_read_avail,
.update_scan_mode = ad4880_update_scan_mode,
};
static const struct iio_enum ad4080_filter_type_enum = {
.items = ad4080_filter_type_iio_enum,
.num_items = ARRAY_SIZE(ad4080_filter_type_iio_enum),
@ -422,17 +466,28 @@ static struct iio_chan_spec_ext_info ad4080_ext_info[] = {
{ }
};
#define AD4080_CHANNEL_DEFINE(bits, storage) { \
/*
* AD4880 needs per-channel filter configuration since each channel has
* its own independent ADC with separate SPI interface.
*/
static struct iio_chan_spec_ext_info ad4880_ext_info[] = {
IIO_ENUM("filter_type", IIO_SEPARATE, &ad4080_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SEPARATE,
&ad4080_filter_type_enum),
{ }
};
#define AD4080_CHANNEL_DEFINE(bits, storage, idx) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = 0, \
.channel = (idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.ext_info = ad4080_ext_info, \
.scan_index = 0, \
.scan_index = (idx), \
.scan_type = { \
.sign = 's', \
.realbits = (bits), \
@ -440,23 +495,51 @@ static struct iio_chan_spec_ext_info ad4080_ext_info[] = {
}, \
}
static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32);
/*
* AD4880 has per-channel attributes (filter_type, oversampling_ratio,
* sampling_frequency) since each channel has its own independent ADC
* with separate SPI configuration interface.
*/
#define AD4880_CHANNEL_DEFINE(bits, storage, idx) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_separate_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.ext_info = ad4880_ext_info, \
.scan_index = (idx), \
.scan_type = { \
.sign = 's', \
.realbits = (bits), \
.storagebits = (storage), \
}, \
}
static const struct iio_chan_spec ad4081_channel = AD4080_CHANNEL_DEFINE(20, 32);
static const struct iio_chan_spec ad4080_channel = AD4080_CHANNEL_DEFINE(20, 32, 0);
static const struct iio_chan_spec ad4082_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 ad4082_channel = AD4080_CHANNEL_DEFINE(20, 32, 0);
static const struct iio_chan_spec ad4084_channel = AD4080_CHANNEL_DEFINE(16, 16);
static const struct iio_chan_spec ad4083_channel = AD4080_CHANNEL_DEFINE(16, 16, 0);
static const struct iio_chan_spec ad4085_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 ad4085_channel = AD4080_CHANNEL_DEFINE(16, 16, 0);
static const struct iio_chan_spec ad4087_channel = AD4080_CHANNEL_DEFINE(14, 16);
static const struct iio_chan_spec ad4086_channel = AD4080_CHANNEL_DEFINE(14, 16, 0);
static const struct iio_chan_spec ad4088_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 ad4088_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",
@ -548,25 +631,34 @@ static const struct ad4080_chip_info ad4088_chip_info = {
.lvds_cnv_clk_cnt_max = 8,
};
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;
@ -575,18 +667,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;
@ -594,7 +686,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,
@ -603,24 +695,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");
@ -655,14 +761,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;
@ -670,9 +790,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;
@ -682,15 +803,25 @@ static int ad4080_probe(struct spi_device *spi)
st->clk_rate = clk_get_rate(clk);
st->back = devm_iio_backend_get(dev, NULL);
if (IS_ERR(st->back))
return PTR_ERR(st->back);
/* Get backends for all channels */
for (unsigned int ch = 0; ch < st->info->num_channels; ch++) {
st->back[ch] = devm_iio_backend_get_by_index(dev, ch);
if (IS_ERR(st->back[ch]))
return PTR_ERR(st->back[ch]);
ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
if (ret)
return ret;
ret = devm_iio_backend_enable(dev, st->back[ch]);
if (ret)
return ret;
}
ret = devm_iio_backend_enable(dev, st->back);
/*
* Request buffer from the first backend only. For multi-channel
* devices (e.g., AD4880), the FPGA uses two axi_ad408x IP instances
* (one per ADC channel) whose outputs are combined by a packer block
* that interleaves all channel data into a single DMA stream routed
* through the first backend's clock domain.
*/
ret = devm_iio_backend_request_buffer(dev, st->back[0], indio_dev);
if (ret)
return ret;
@ -711,6 +842,7 @@ static const struct spi_device_id ad4080_id[] = {
{ "ad4086", (kernel_ulong_t)&ad4086_chip_info },
{ "ad4087", (kernel_ulong_t)&ad4087_chip_info },
{ "ad4088", (kernel_ulong_t)&ad4088_chip_info },
{ "ad4880", (kernel_ulong_t)&ad4880_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4080_id);
@ -725,6 +857,7 @@ static const struct of_device_id ad4080_of_match[] = {
{ .compatible = "adi,ad4086", &ad4086_chip_info },
{ .compatible = "adi,ad4087", &ad4087_chip_info },
{ .compatible = "adi,ad4088", &ad4088_chip_info },
{ .compatible = "adi,ad4880", &ad4880_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad4080_of_match);