iio: dac: ad3530r: Add support for AD3532R/AD3532

The AD3532R/AD3532 is a 16-channel, 16-bit voltage output DAC. It shares
similar functionality with AD3530R but splits its registers into two
banks: bank 0 at 0x10xx for channels 0-7 and bank 1 at 0x30xx for
channels 8-15. The input, LDAC trigger and operating-mode registers are
therefore selected per bank.

Add the AD3532R register map, channel specs, per-bank register arrays, a
dedicated powerdown handler and its own regmap_config, reusing the
table-driven helpers for the shared configuration steps.

Signed-off-by: Kim Seer Paller <kimseer.paller@analog.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
This commit is contained in:
Kim Seer Paller 2026-07-03 18:10:11 +08:00 committed by Jonathan Cameron
parent 7511d97a8f
commit 97e5f68ca4
2 changed files with 192 additions and 4 deletions

View File

@ -11,8 +11,11 @@ config AD3530R
depends on SPI
select REGMAP_SPI
help
Say yes here to build support for Analog Devices AD3530R, AD3531R
Digital to Analog Converter.
Say yes here to build support for the following Analog Devices
Digital to Analog Converters:
- AD3530/AD3530R (8-channel)
- AD3531/AD3531R (4-channel)
- AD3532/AD3532R (16-channel)
To compile this driver as a module, choose M here: the
module will be called ad3530r.

View File

@ -2,6 +2,7 @@
/*
* AD3530R/AD3530 8-channel, 16-bit Voltage Output DAC Driver
* AD3531R/AD3531 4-channel, 16-bit Voltage Output DAC Driver
* AD3532R/AD3532 16-channel, 16-bit Voltage Output DAC Driver
*
* Copyright 2025 Analog Devices Inc.
*/
@ -39,6 +40,25 @@
#define AD3531R_SW_LDAC_TRIG_A 0xDD
#define AD3531R_INPUT_CH 0xE3
/* AD3532R/AD3532 bank 0 registers (channels 0-7) */
#define AD3532R_INTERFACE_CONFIG_A_0 0x1000
#define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020
#define AD3532R_OUTPUT_OPERATING_MODE_1 0x1021
#define AD3532R_OUTPUT_CONTROL_0 0x102A
#define AD3532R_REFERENCE_CONTROL_0 0x103C
#define AD3532R_SW_LDAC_TRIG_0 0x10E5
#define AD3532R_INPUT_CH_0 0x10EB
/* AD3532R/AD3532 bank 1 registers (channels 8-15) */
#define AD3532R_INTERFACE_CONFIG_A_1 0x3000
#define AD3532R_OUTPUT_OPERATING_MODE_2 0x3020
#define AD3532R_OUTPUT_OPERATING_MODE_3 0x3021
#define AD3532R_OUTPUT_CONTROL_1 0x302A
#define AD3532R_REFERENCE_CONTROL_1 0x303C
#define AD3532R_SW_LDAC_TRIG_1 0x30E5
#define AD3532R_INPUT_CH_1 0x30EB
#define AD3532R_MAX_REG_ADDR 0x30F9
#define AD3530R_SLD_TRIG_A BIT(7)
#define AD3530R_OUTPUT_CONTROL_RANGE BIT(2)
#define AD3530R_REFERENCE_CONTROL_SEL BIT(0)
@ -50,8 +70,10 @@
#define AD3530R_LDAC_PULSE_US 100
#define AD3530R_DAC_MAX_VAL GENMASK(15, 0)
#define AD3530R_MAX_CHANNELS 8
#define AD3530R_CH_PER_REG 4
#define AD3530R_CH_PER_BANK 8
#define AD3531R_MAX_CHANNELS 4
#define AD3532R_MAX_CHANNELS 16
enum ad3530r_mode {
AD3530R_NORMAL_OP,
@ -85,7 +107,7 @@ struct ad3530r_state {
struct regmap *regmap;
/* lock to protect against multiple access to the device and shared data */
struct mutex lock;
struct ad3530r_chan chan[AD3530R_MAX_CHANNELS];
struct ad3530r_chan chan[AD3532R_MAX_CHANNELS];
const struct ad3530r_chip_info *chip_info;
struct gpio_desc *ldac_gpio;
int vref_mV;
@ -106,6 +128,14 @@ static int ad3531r_input_ch_reg(unsigned int channel)
return 2 * channel + AD3531R_INPUT_CH;
}
static int ad3532r_input_ch_reg(unsigned int channel)
{
unsigned int bank = channel / AD3530R_CH_PER_BANK;
unsigned int local_ch = channel % AD3530R_CH_PER_BANK;
return 2 * local_ch + (bank ? AD3532R_INPUT_CH_1 : AD3532R_INPUT_CH_0);
}
static const char * const ad3530r_powerdown_modes[] = {
"1kohm_to_gnd",
"7.7kohm_to_gnd",
@ -118,6 +148,12 @@ static const char * const ad3531r_powerdown_modes[] = {
"16kohm_to_gnd",
};
static const char * const ad3532r_powerdown_modes[] = {
"1kohm_to_gnd",
"10kohm_to_gnd",
"three_state",
};
static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@ -153,6 +189,13 @@ static const struct iio_enum ad3531r_powerdown_mode_enum = {
.set = ad3530r_set_powerdown_mode,
};
static const struct iio_enum ad3532r_powerdown_mode_enum = {
.items = ad3532r_powerdown_modes,
.num_items = ARRAY_SIZE(ad3532r_powerdown_modes),
.get = ad3530r_get_powerdown_mode,
.set = ad3530r_set_powerdown_mode,
};
static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
@ -197,6 +240,45 @@ static ssize_t ad3530r_set_dac_powerdown(struct iio_dev *indio_dev,
return len;
}
static ssize_t ad3532r_set_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct ad3530r_state *st = iio_priv(indio_dev);
unsigned int bank, local_ch, reg_in_bank, ch_in_reg;
unsigned int reg, mask, val;
bool powerdown;
int ret;
ret = kstrtobool(buf, &powerdown);
if (ret)
return ret;
bank = chan->channel / AD3530R_CH_PER_BANK;
local_ch = chan->channel % AD3530R_CH_PER_BANK;
reg_in_bank = local_ch / AD3530R_CH_PER_REG;
ch_in_reg = local_ch % AD3530R_CH_PER_REG;
reg = reg_in_bank + (bank ? AD3532R_OUTPUT_OPERATING_MODE_2 :
AD3532R_OUTPUT_OPERATING_MODE_0);
mask = AD3530R_OP_MODE_CHAN_MSK(ch_in_reg);
guard(mutex)(&st->lock);
if (powerdown) {
val = field_prep(mask, st->chan[chan->channel].powerdown_mode);
ret = regmap_update_bits(st->regmap, reg, mask, val);
} else {
ret = regmap_clear_bits(st->regmap, reg, mask);
}
if (ret)
return ret;
st->chan[chan->channel].powerdown = powerdown;
return len;
}
static int ad3530r_trigger_sw_ldac_reg(unsigned int channel)
{
return AD3530R_SW_LDAC_TRIG_A;
@ -207,6 +289,13 @@ static int ad3531r_trigger_sw_ldac_reg(unsigned int channel)
return AD3531R_SW_LDAC_TRIG_A;
}
static int ad3532r_trigger_sw_ldac_reg(unsigned int channel)
{
unsigned int bank = channel / AD3530R_CH_PER_BANK;
return bank ? AD3532R_SW_LDAC_TRIG_1 : AD3532R_SW_LDAC_TRIG_0;
}
static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio)
{
gpiod_set_value_cansleep(ldac_gpio, 1);
@ -319,6 +408,19 @@ static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = {
{ }
};
static const struct iio_chan_spec_ext_info ad3532r_ext_info[] = {
{
.name = "powerdown",
.shared = IIO_SEPARATE,
.read = ad3530r_get_dac_powerdown,
.write = ad3532r_set_dac_powerdown,
},
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3532r_powerdown_mode_enum),
IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE,
&ad3532r_powerdown_mode_enum),
{ }
};
#define AD3530R_CHAN(_chan, _ext_info) \
{ \
.type = IIO_VOLTAGE, \
@ -348,6 +450,25 @@ static const struct iio_chan_spec ad3531r_channels[] = {
AD3530R_CHAN(3, ad3531r_ext_info),
};
static const struct iio_chan_spec ad3532r_channels[] = {
AD3530R_CHAN(0, ad3532r_ext_info),
AD3530R_CHAN(1, ad3532r_ext_info),
AD3530R_CHAN(2, ad3532r_ext_info),
AD3530R_CHAN(3, ad3532r_ext_info),
AD3530R_CHAN(4, ad3532r_ext_info),
AD3530R_CHAN(5, ad3532r_ext_info),
AD3530R_CHAN(6, ad3532r_ext_info),
AD3530R_CHAN(7, ad3532r_ext_info),
AD3530R_CHAN(8, ad3532r_ext_info),
AD3530R_CHAN(9, ad3532r_ext_info),
AD3530R_CHAN(10, ad3532r_ext_info),
AD3530R_CHAN(11, ad3532r_ext_info),
AD3530R_CHAN(12, ad3532r_ext_info),
AD3530R_CHAN(13, ad3532r_ext_info),
AD3530R_CHAN(14, ad3532r_ext_info),
AD3530R_CHAN(15, ad3532r_ext_info),
};
static const unsigned int ad3530r_if_config[] = {
AD3530R_INTERFACE_CONFIG_A,
};
@ -369,12 +490,40 @@ static const unsigned int ad3531r_op_mode[] = {
AD3530R_OUTPUT_OPERATING_MODE_0,
};
static const unsigned int ad3532r_if_config[] = {
AD3532R_INTERFACE_CONFIG_A_0,
AD3532R_INTERFACE_CONFIG_A_1,
};
static const unsigned int ad3532r_out_ctrl[] = {
AD3532R_OUTPUT_CONTROL_0,
AD3532R_OUTPUT_CONTROL_1,
};
static const unsigned int ad3532r_ref_ctrl[] = {
AD3532R_REFERENCE_CONTROL_0,
AD3532R_REFERENCE_CONTROL_1,
};
static const unsigned int ad3532r_op_mode[] = {
AD3532R_OUTPUT_OPERATING_MODE_0,
AD3532R_OUTPUT_OPERATING_MODE_1,
AD3532R_OUTPUT_OPERATING_MODE_2,
AD3532R_OUTPUT_OPERATING_MODE_3,
};
static const struct regmap_config ad3530r_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.max_register = AD3530R_MAX_REG_ADDR,
};
static const struct regmap_config ad3532r_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.max_register = AD3532R_MAX_REG_ADDR,
};
static const struct ad3530r_chip_info ad3530_chip = {
.name = "ad3530",
.channels = ad3530r_channels,
@ -439,6 +588,38 @@ static const struct ad3530r_chip_info ad3531r_chip = {
.internal_ref_support = true,
};
static const struct ad3530r_chip_info ad3532_chip = {
.name = "ad3532",
.channels = ad3532r_channels,
.regmap_config = &ad3532r_regmap_config,
.num_channels = ARRAY_SIZE(ad3532r_channels),
.sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg,
.input_ch_reg = ad3532r_input_ch_reg,
.interface_config_a = ad3532r_if_config,
.output_control = ad3532r_out_ctrl,
.reference_control = ad3532r_ref_ctrl,
.op_mode = ad3532r_op_mode,
.num_banks = ARRAY_SIZE(ad3532r_if_config),
.num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode),
.internal_ref_support = false,
};
static const struct ad3530r_chip_info ad3532r_chip = {
.name = "ad3532r",
.channels = ad3532r_channels,
.regmap_config = &ad3532r_regmap_config,
.num_channels = ARRAY_SIZE(ad3532r_channels),
.sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg,
.input_ch_reg = ad3532r_input_ch_reg,
.interface_config_a = ad3532r_if_config,
.output_control = ad3532r_out_ctrl,
.reference_control = ad3532r_ref_ctrl,
.op_mode = ad3532r_op_mode,
.num_banks = ARRAY_SIZE(ad3532r_if_config),
.num_op_mode_regs = ARRAY_SIZE(ad3532r_op_mode),
.internal_ref_support = true,
};
static int ad3530r_set_reg_bank_bits(const struct ad3530r_state *st,
const unsigned int *regs,
unsigned int num_regs,
@ -610,6 +791,8 @@ static const struct spi_device_id ad3530r_id[] = {
{ .name = "ad3530r", .driver_data = (kernel_ulong_t)&ad3530r_chip },
{ .name = "ad3531", .driver_data = (kernel_ulong_t)&ad3531_chip },
{ .name = "ad3531r", .driver_data = (kernel_ulong_t)&ad3531r_chip },
{ .name = "ad3532", .driver_data = (kernel_ulong_t)&ad3532_chip },
{ .name = "ad3532r", .driver_data = (kernel_ulong_t)&ad3532r_chip },
{ }
};
MODULE_DEVICE_TABLE(spi, ad3530r_id);
@ -619,6 +802,8 @@ static const struct of_device_id ad3530r_of_match[] = {
{ .compatible = "adi,ad3530r", .data = &ad3530r_chip },
{ .compatible = "adi,ad3531", .data = &ad3531_chip },
{ .compatible = "adi,ad3531r", .data = &ad3531r_chip },
{ .compatible = "adi,ad3532", .data = &ad3532_chip },
{ .compatible = "adi,ad3532r", .data = &ad3532r_chip },
{ }
};
MODULE_DEVICE_TABLE(of, ad3530r_of_match);