diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml index a355d52a9d641e..2bbb87eb045da3 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3530r.yaml @@ -27,6 +27,8 @@ properties: - adi,ad3530r - adi,ad3531 - adi,ad3531r + - adi,ad3532 + - adi,ad3532r reg: maxItems: 1 diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 67627a97ead148..c97705d16e135c 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -11,8 +11,8 @@ 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 Analog Devices AD3530R, AD3531R, + AD3532R Digital to Analog Converter. To compile this driver as a module, choose M here: the module will be called ad3530r. diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 6134613777b8e1..410222b7cd9b66 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -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. */ @@ -34,11 +35,26 @@ #define AD3530R_REFERENCE_CONTROL_0 0x3C #define AD3530R_SW_LDAC_TRIG_A 0xE5 #define AD3530R_INPUT_CH 0xEB -#define AD3530R_MAX_REG_ADDR 0xF9 #define AD3531R_SW_LDAC_TRIG_A 0xDD #define AD3531R_INPUT_CH 0xE3 +#define AD3532R_INTERFACE_CONFIG_A_0 0x1000 +#define AD3532R_INTERFACE_CONFIG_A_1 0x3000 +#define AD3532R_OUTPUT_OPERATING_MODE_0 0x1020 +#define AD3532R_OUTPUT_OPERATING_MODE_1 0x1021 +#define AD3532R_OUTPUT_OPERATING_MODE_2 0x3020 +#define AD3532R_OUTPUT_OPERATING_MODE_3 0x3021 +#define AD3532R_OUTPUT_CONTROL_0 0x102A +#define AD3532R_OUTPUT_CONTROL_1 0x302A +#define AD3532R_REFERENCE_CONTROL_0 0x103C +#define AD3532R_REFERENCE_CONTROL_1 0x303C +#define AD3532R_SW_LDAC_TRIG_0 0x10E5 +#define AD3532R_SW_LDAC_TRIG_1 0x30E5 +#define AD3532R_INPUT_CH_0 0x10EB +#define AD3532R_INPUT_CH_1 0x30EB +#define AD3532R_MAX_REG_ADDR 0x30F8 + #define AD3530R_SLD_TRIG_A BIT(7) #define AD3530R_OUTPUT_CONTROL_RANGE BIT(2) #define AD3530R_REFERENCE_CONTROL_SEL BIT(0) @@ -52,6 +68,7 @@ #define AD3530R_DAC_MAX_VAL GENMASK(15, 0) #define AD3530R_MAX_CHANNELS 8 #define AD3531R_MAX_CHANNELS 4 +#define AD3532R_MAX_CHANNELS 16 /* Non-constant mask variant of FIELD_PREP() */ #define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) @@ -70,10 +87,10 @@ struct ad3530r_chan { struct ad3530r_chip_info { const char *name; - const struct iio_chan_spec *channels; int (*input_ch_reg)(unsigned int channel); + int (*sw_ldac_trig_reg)(unsigned int channel); + const struct iio_chan_spec_ext_info *ext_info; unsigned int num_channels; - unsigned int sw_ldac_trig_reg; bool internal_ref_support; }; @@ -81,9 +98,10 @@ 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; + struct iio_chan_spec *iio_chans; int vref_mV; /* * DMA (thus cache coherency maintenance) may require the transfer @@ -102,12 +120,26 @@ static int ad3531r_input_ch_reg(unsigned int channel) return 2 * channel + AD3531R_INPUT_CH; } +static int ad3532r_input_ch_reg(unsigned int channel) +{ + if (channel < 8) + return 2 * channel + AD3532R_INPUT_CH_0; + + return 2 * (channel - 8) + AD3532R_INPUT_CH_1; +} + static const char * const ad3530r_powerdown_modes[] = { "1kohm_to_gnd", "7.7kohm_to_gnd", "32kohm_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) { @@ -136,6 +168,13 @@ static const struct iio_enum ad3530r_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, @@ -180,6 +219,106 @@ 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); + int ret; + unsigned int reg, pdmode, mask, val; + bool powerdown; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + guard(mutex)(&st->lock); + switch (chan->channel) { + case 0 ... 3: + reg = AD3532R_OUTPUT_OPERATING_MODE_0; + mask = AD3530R_OP_MODE_CHAN_MSK(chan->channel); + break; + case 4 ... 7: + reg = AD3532R_OUTPUT_OPERATING_MODE_1; + mask = AD3530R_OP_MODE_CHAN_MSK(chan->channel - 4); + break; + case 8 ... 11: + reg = AD3532R_OUTPUT_OPERATING_MODE_2; + mask = AD3530R_OP_MODE_CHAN_MSK(chan->channel - 8); + break; + case 12 ... 15: + reg = AD3532R_OUTPUT_OPERATING_MODE_3; + mask = AD3530R_OP_MODE_CHAN_MSK(chan->channel - 12); + break; + default: + return -EINVAL; + } + + pdmode = powerdown ? st->chan[chan->channel].powerdown_mode : 0; + val = field_prep(mask, pdmode); + + ret = regmap_update_bits(st->regmap, reg, mask, val); + if (ret) + return ret; + + st->chan[chan->channel].powerdown = powerdown; + + return len; +} + +static int ad3530r_normal_op_mode(const struct ad3530r_state *st) +{ + int ret; + u8 val; + + val = FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(0), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(1), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | + FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); + + if (st->chip_info->num_channels == AD3532R_MAX_CHANNELS) { + ret = regmap_write(st->regmap, AD3532R_OUTPUT_OPERATING_MODE_0, val); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD3532R_OUTPUT_OPERATING_MODE_1, val); + if (ret) + return ret; + + ret = regmap_write(st->regmap, AD3532R_OUTPUT_OPERATING_MODE_2, val); + if (ret) + return ret; + + return regmap_write(st->regmap, AD3532R_OUTPUT_OPERATING_MODE_3, val); + } + + if (st->chip_info->num_channels == AD3530R_MAX_CHANNELS) { + ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1, + val); + if (ret) + return ret; + } + + return regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val); +} + +static int ad3530r_trigger_sw_ldac_reg(unsigned int channel) +{ + if (channel < AD3531R_MAX_CHANNELS) + return AD3531R_SW_LDAC_TRIG_A; + + return AD3530R_SW_LDAC_TRIG_A; +} + +static int ad3532r_trigger_sw_ldac_reg(unsigned int channel) +{ + if (channel < 8) + return AD3532R_SW_LDAC_TRIG_0; + + return AD3532R_SW_LDAC_TRIG_1; +} + static int ad3530r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) { gpiod_set_value_cansleep(ldac_gpio, 1); @@ -205,7 +344,7 @@ static int ad3530r_dac_write(struct ad3530r_state *st, unsigned int chan, if (st->ldac_gpio) return ad3530r_trigger_hw_ldac(st->ldac_gpio); - return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg, + return regmap_set_bits(st->regmap, st->chip_info->sw_ldac_trig_reg(chan), AD3530R_SLD_TRIG_A); } @@ -279,77 +418,93 @@ static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { { } }; -#define AD3530R_CHAN(_chan) \ -{ \ - .type = IIO_VOLTAGE, \ - .indexed = 1, \ - .channel = _chan, \ - .output = 1, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .ext_info = ad3530r_ext_info, \ -} - -static const struct iio_chan_spec ad3530r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), - AD3530R_CHAN(4), - AD3530R_CHAN(5), - AD3530R_CHAN(6), - AD3530R_CHAN(7), +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), + { } }; -static const struct iio_chan_spec ad3531r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), +static const struct iio_chan_spec ad3530r_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), }; static const struct ad3530r_chip_info ad3530_chip = { .name = "ad3530", - .channels = ad3530r_channels, - .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .num_channels = 8, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .ext_info = ad3530r_ext_info, .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3530r_chip = { .name = "ad3530r", - .channels = ad3530r_channels, - .num_channels = ARRAY_SIZE(ad3530r_channels), - .sw_ldac_trig_reg = AD3530R_SW_LDAC_TRIG_A, + .num_channels = 8, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3530r_input_ch_reg, + .ext_info = ad3530r_ext_info, .internal_ref_support = true, }; static const struct ad3530r_chip_info ad3531_chip = { .name = "ad3531", - .channels = ad3531r_channels, - .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .num_channels = 4, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .ext_info = ad3530r_ext_info, .internal_ref_support = false, }; static const struct ad3530r_chip_info ad3531r_chip = { .name = "ad3531r", - .channels = ad3531r_channels, - .num_channels = ARRAY_SIZE(ad3531r_channels), - .sw_ldac_trig_reg = AD3531R_SW_LDAC_TRIG_A, + .num_channels = 4, + .sw_ldac_trig_reg = ad3530r_trigger_sw_ldac_reg, .input_ch_reg = ad3531r_input_ch_reg, + .ext_info = ad3530r_ext_info, + .internal_ref_support = true, +}; + +static const struct ad3530r_chip_info ad3532_chip = { + .name = "ad3532", + .num_channels = 16, + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .ext_info = ad3532r_ext_info, + .internal_ref_support = false, +}; + +static const struct ad3530r_chip_info ad3532r_chip = { + .name = "ad3532r", + .num_channels = 16, + .sw_ldac_trig_reg = ad3532r_trigger_sw_ldac_reg, + .input_ch_reg = ad3532r_input_ch_reg, + .ext_info = ad3532r_ext_info, .internal_ref_support = true, }; static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) { + const struct ad3530r_chip_info *chip_info = st->chip_info; struct device *dev = regmap_get_device(st->regmap); struct gpio_desc *reset_gpio; int i, ret; - u8 range_multiplier, val; + u8 range_multiplier; + + st->iio_chans = devm_kcalloc(dev, chip_info->num_channels, + sizeof(*st->iio_chans), GFP_KERNEL); + if (!st->iio_chans) + return -ENOMEM; reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) @@ -362,20 +517,46 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) gpiod_set_value_cansleep(reset_gpio, 0); } else { /* Perform software reset */ - ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A, - AD3530R_SW_RESET, AD3530R_SW_RESET); - if (ret) - return ret; + if (chip_info->num_channels == AD3532R_MAX_CHANNELS) { + ret = regmap_update_bits(st->regmap, + AD3532R_INTERFACE_CONFIG_A_0, + AD3530R_SW_RESET, AD3530R_SW_RESET); + if (ret) + return ret; + + ret = regmap_update_bits(st->regmap, + AD3532R_INTERFACE_CONFIG_A_1, + AD3530R_SW_RESET, AD3530R_SW_RESET); + if (ret) + return ret; + } else { + ret = regmap_update_bits(st->regmap, AD3530R_INTERFACE_CONFIG_A, + AD3530R_SW_RESET, AD3530R_SW_RESET); + if (ret) + return ret; + } } fsleep(10 * USEC_PER_MSEC); range_multiplier = 1; if (device_property_read_bool(dev, "adi,range-double")) { - ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0, - AD3530R_OUTPUT_CONTROL_RANGE); - if (ret) - return ret; + if (chip_info->num_channels == AD3532R_MAX_CHANNELS) { + ret = regmap_set_bits(st->regmap, AD3532R_OUTPUT_CONTROL_0, + AD3530R_OUTPUT_CONTROL_RANGE); + if (ret) + return ret; + + ret = regmap_set_bits(st->regmap, AD3532R_OUTPUT_CONTROL_1, + AD3530R_OUTPUT_CONTROL_RANGE); + if (ret) + return ret; + } else { + ret = regmap_set_bits(st->regmap, AD3530R_OUTPUT_CONTROL_0, + AD3530R_OUTPUT_CONTROL_RANGE); + if (ret) + return ret; + } range_multiplier = 2; } @@ -383,33 +564,38 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) if (external_vref_uV) { st->vref_mV = range_multiplier * external_vref_uV / MILLI; } else { - ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0, - AD3530R_REFERENCE_CONTROL_SEL); - if (ret) - return ret; + if (chip_info->num_channels == AD3532R_MAX_CHANNELS) { + ret = regmap_set_bits(st->regmap, AD3532R_REFERENCE_CONTROL_0, + AD3530R_REFERENCE_CONTROL_SEL); + if (ret) + return ret; + + ret = regmap_set_bits(st->regmap, AD3532R_REFERENCE_CONTROL_1, + AD3530R_REFERENCE_CONTROL_SEL); + if (ret) + return ret; + } else { + ret = regmap_set_bits(st->regmap, AD3530R_REFERENCE_CONTROL_0, + AD3530R_REFERENCE_CONTROL_SEL); + if (ret) + return ret; + } st->vref_mV = range_multiplier * AD3530R_INTERNAL_VREF_mV; } /* Set normal operating mode for all channels */ - val = FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(0), AD3530R_NORMAL_OP) | - FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(1), AD3530R_NORMAL_OP) | - FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(2), AD3530R_NORMAL_OP) | - FIELD_PREP(AD3530R_OP_MODE_CHAN_MSK(3), AD3530R_NORMAL_OP); - - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_0, val); + ret = ad3530r_normal_op_mode(st); if (ret) return ret; - if (st->chip_info->num_channels > 4) { - ret = regmap_write(st->regmap, AD3530R_OUTPUT_OPERATING_MODE_1, - val); - if (ret) - return ret; - } + for (i = 0; i < chip_info->num_channels; i++) { + st->iio_chans[i] = ad3530r_channel_template; + st->iio_chans[i].channel = i; + st->iio_chans[i].ext_info = chip_info->ext_info; - for (i = 0; i < st->chip_info->num_channels; i++) st->chan[i].powerdown_mode = AD3530R_POWERDOWN_32K; + } st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); if (IS_ERR(st->ldac_gpio)) @@ -422,7 +608,7 @@ static int ad3530r_setup(struct ad3530r_state *st, int external_vref_uV) static const struct regmap_config ad3530r_regmap_config = { .reg_bits = 16, .val_bits = 8, - .max_register = AD3530R_MAX_REG_ADDR, + .max_register = AD3532R_MAX_REG_ADDR, }; static const struct iio_info ad3530r_info = { @@ -480,7 +666,7 @@ static int ad3530r_probe(struct spi_device *spi) indio_dev->name = st->chip_info->name; indio_dev->info = &ad3530r_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channels; + indio_dev->channels = st->iio_chans; indio_dev->num_channels = st->chip_info->num_channels; return devm_iio_device_register(&spi->dev, indio_dev); @@ -491,6 +677,8 @@ static const struct spi_device_id ad3530r_id[] = { { "ad3530r", (kernel_ulong_t)&ad3530r_chip }, { "ad3531", (kernel_ulong_t)&ad3531_chip }, { "ad3531r", (kernel_ulong_t)&ad3531r_chip }, + { "ad3532", (kernel_ulong_t)&ad3532_chip }, + { "ad3532r", (kernel_ulong_t)&ad3532r_chip }, { } }; MODULE_DEVICE_TABLE(spi, ad3530r_id); @@ -500,6 +688,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);