mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
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:
parent
7511d97a8f
commit
97e5f68ca4
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user