From b75bb04c380061bf8fa1c246b7bb806ec9e123a9 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 16 Mar 2026 16:24:04 +0800 Subject: [PATCH 1/4] iio: ABI: add DAC high_z powerdown mode and current output entries Add high_z powerdown mode for DACs with high impedance output in current mode (IDAC) and 15kohm_to_gnd resistor to GND. Also add out_currentY_powerdown_mode, out_currentY_powerdown_mode_available, and out_currentY_powerdown entries to document current output powerdown support. Signed-off-by: Kim Seer Paller --- Documentation/ABI/testing/sysfs-bus-iio | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 8cc76e3a2be2ea..c5cacc8748d8bc 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -738,6 +738,8 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_powerdown_mode What: /sys/bus/iio/devices/iio:deviceX/out_voltage_powerdown_mode +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_powerdown_mode +What: /sys/bus/iio/devices/iio:deviceX/out_current_powerdown_mode What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown_mode What: /sys/bus/iio/devices/iio:deviceX/out_altvoltage_powerdown_mode KernelVersion: 2.6.38 @@ -749,6 +751,7 @@ Description: 2.5kohm_to_gnd: connected to ground via a 2.5kOhm resistor, 6kohm_to_gnd: connected to ground via a 6kOhm resistor, 7.7kohm_to_gnd: connected to ground via a 7.7kOhm resistor, + 15kohm_to_gnd: connected to ground via a 15kOhm resistor, 20kohm_to_gnd: connected to ground via a 20kOhm resistor, 32kohm_to_gnd: connected to ground via a 32kOhm resistor, 42kohm_to_gnd: connected to ground via a 42kOhm resistor, @@ -757,13 +760,16 @@ Description: 125kohm_to_gnd: connected to ground via an 125kOhm resistor, 500kohm_to_gnd: connected to ground via a 500kOhm resistor, 640kohm_to_gnd: connected to ground via a 640kOhm resistor, - three_state: left floating. + three_state: left floating, + high_z: left floating. For a list of available output power down options read outX_powerdown_mode_available. If Y is not present the mode is shared across all outputs. What: /sys/.../iio:deviceX/out_voltageY_powerdown_mode_available What: /sys/.../iio:deviceX/out_voltage_powerdown_mode_available +What: /sys/.../iio:deviceX/out_currentY_powerdown_mode_available +What: /sys/.../iio:deviceX/out_current_powerdown_mode_available What: /sys/.../iio:deviceX/out_altvoltageY_powerdown_mode_available What: /sys/.../iio:deviceX/out_altvoltage_powerdown_mode_available KernelVersion: 2.6.38 @@ -774,6 +780,8 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_voltage_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_current_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_altvoltage_powerdown KernelVersion: 2.6.38 From 090964910f19ce76fc0b786d2e4c43d6741d413b Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 16 Mar 2026 16:36:36 +0800 Subject: [PATCH 2/4] dt-bindings: iio: dac: add adi,ad5710r.yaml Add device tree bindings for the Analog Devices AD5710R/AD5711R 8-channel 12-/16-bit Configurable IDAC/VDAC. Signed-off-by: Kim Seer Paller --- .../bindings/iio/dac/adi,ad5710r.yaml | 143 ++++++++++++++++++ MAINTAINERS | 7 + 2 files changed, 150 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml new file mode 100644 index 00000000000000..17c1bf0092421f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml @@ -0,0 +1,143 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad5710r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD5710R/AD5711R 8-channel 12-/16-bit Configurable IDAC/VDAC + +maintainers: + - Kim Seer Paller + +description: | + The AD5710R (16-bit) and AD5711R (12-bit) are 8-channel, low-power, + configurable current/voltage output digital-to-analog converters (DACs) with + an on-chip 2.5V, 3ppm/°C reference. Each channel can be independently + configured as a voltage output (0V to VREF or 0V to 2xVREF) or a current + output (0mA to 50mA). These devices operate from a single 2.7V to 5.5V + supply and are guaranteed monotonic by design. + Datasheet can be found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad5711r-ad5710r.pdf + +properties: + compatible: + enum: + - adi,ad5710r + - adi,ad5711r + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 20000000 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + vdd-supply: + description: Power Supply Input. + + iovdd-supply: + description: Digital Power Supply Input. + + io-channels: + description: + ADC channel used to monitor internal die temperature, output voltages, and + current of a selected channel via the MUXOUT pin. + maxItems: 1 + + ref-supply: + description: + Reference Input/Output. The voltage at the REF pin sets the full-scale + range of all channels. If not provided the internal reference is used and + also provided on the VREF pin. + + reset-gpios: + description: + Active low signal that is falling edge sensitive. When it is deasserted, + the digital core initialization is performed and all DAC registers except + the Interface Configuration A register are reset to their default values. + maxItems: 1 + + ldac-gpios: + description: + LDAC pin to be used as a hardware trigger to update the DAC channels. If + not present, the DAC channels are updated by Software LDAC. + maxItems: 1 + + adi,range-double: + description: + Configure the output range for all channels. If the property is present, + the output will range from 0V to 2Vref. If the property is not present, + the output will range from 0V to Vref. + type: boolean + +patternProperties: + "^channel@[0-7]$": + $ref: /schemas/iio/dac/dac.yaml# + type: object + description: + Represents the external channels which are connected to the DAC. + + properties: + reg: + description: Channel number + items: + minimum: 0 + maximum: 7 + + adi,ch-func: + description: + Channel output type. Use CH_FUNC_VOLTAGE_OUTPUT for voltage + output or CH_FUNC_CURRENT_OUTPUT for current output. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2] + + required: + - reg + - adi,ch-func + + unevaluatedProperties: false + +required: + - compatible + - reg + - vdd-supply + - iovdd-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + dac@0 { + compatible = "adi,ad5710r"; + reg = <0>; + spi-max-frequency = <1000000>; + vdd-supply = <&vdd>; + iovdd-supply = <&iovdd>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,ch-func = ; + }; + + channel@1 { + reg = <1>; + adi,ch-func = ; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index c5ef8e12e5f85a..db29201794e458 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1328,6 +1328,13 @@ F: Documentation/iio/ad4695.rst F: drivers/iio/adc/ad4695.c F: include/dt-bindings/iio/adc/adc/adi,ad4695.h +ANALOG DEVICES INC AD5710R DRIVER +M: Kim Seer Paller +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml + ANALOG DEVICES INC AD7091R DRIVER M: Marcelo Schmitt L: linux-iio@vger.kernel.org From 78f39bad7d29ebfee9b5b9172c5f5c056bb28bd6 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 16 Mar 2026 16:39:57 +0800 Subject: [PATCH 3/4] iio: dac: ad5710r: Add driver for AD5710R and AD5711R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AD5710R (16-bit) and AD5711R (12-bit) are 8-channel, low-power, configurable current/voltage output DACs with an on-chip 2.5V, 3ppm/°C reference. Each channel can be independently configured as a voltage output (0V to VREF or 0V to 2xVREF) or a current output (0mA to 50mA). These devices operate from a single 2.7V to 5.5V supply and are guaranteed monotonic by design. Signed-off-by: Kim Seer Paller --- MAINTAINERS | 1 + drivers/iio/dac/Kconfig | 11 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5710r.c | 494 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 507 insertions(+) create mode 100644 drivers/iio/dac/ad5710r.c diff --git a/MAINTAINERS b/MAINTAINERS index db29201794e458..5a750da32fc247 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1334,6 +1334,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,ad5710r.yaml +F: drivers/iio/dac/ad5710r.c ANALOG DEVICES INC AD7091R DRIVER M: Marcelo Schmitt diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 67627a97ead148..01ce8568633445 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -250,6 +250,17 @@ config AD5696_I2C To compile this driver as a module, choose M here: the module will be called ad5696. +config AD5710R + tristate "Analog Devices AD5710R/AD5711R DAC driver" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for Analog Devices AD5710R, AD5711R + Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad5710r. + config AD5755 tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" depends on SPI_MASTER diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 6f4db74fff4b17..e941c1f8f92249 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o +obj-$(CONFIG_AD5710R) += ad5710r.o obj-$(CONFIG_AD7293) += ad7293.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8460) += ad8460.o diff --git a/drivers/iio/dac/ad5710r.c b/drivers/iio/dac/ad5710r.c new file mode 100644 index 00000000000000..fecbeaa931a4ab --- /dev/null +++ b/drivers/iio/dac/ad5710r.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD5710R/AD5711R 8-channel 12-/16-bit Configurable IDAC/VDAC + * + * Copyright 2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD5710R_INTERFACE_CONFIG_A 0x00 +#define AD5710R_OUTPUT_OPERATING_MODE_0 0x20 +#define AD5710R_OUTPUT_OPERATING_MODE_1 0x21 +#define AD5710R_OUTPUT_CONTROL_0 0x2A +#define AD5710R_REFERENCE_CONTROL_0 0x3C +#define AD5710R_SW_LDAC_TRIG_A 0xE5 +#define AD5710R_INPUT_CH(chan) (2 * chan + 0xEB) +#define AD5710R_CHN_VMODE_EN 0xFF + +#define AD5710R_SLD_TRIG_A BIT(7) +#define AD5710R_OUTPUT_CONTROL_RANGE BIT(2) +#define AD5710R_REFERENCE_CONTROL_SEL BIT(0) +#define AD5710R_REG_VAL_MASK GENMASK(15, 0) +#define AD5710R_OP_MODE_CHAN_MSK(chan) (GENMASK(1, 0) << 2 * (chan)) +#define AD5710R_CHN_VMODE_EN_BIT(chan) BIT(chan) + +#define AD5710R_SW_RESET (BIT(7) | BIT(0)) +#define AD5710R_NORMAL_OP 0 +#define AD5710R_INTERNAL_VREF_mV 2500 +#define AD5710R_INTERNAL_IREF_mA 50 +#define AD5710R_LDAC_PULSE_US 100 +#define AD5710R_NUM_CHANNELS 8 + +/* Non-constant mask variant of FIELD_PREP() */ +#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) + +struct ad5710r_chip_info { + const char *name; + unsigned int resolution; +}; + +struct ad5710r_state { + struct regmap *regmap; + const struct ad5710r_chip_info *chip_info; + struct iio_chan_spec *iio_channels; + struct gpio_desc *ldac_gpio; + int vref_mV; + /* + * DMA (thus cache coherency maintenance) may require the transfer + * buffers to live in their own cache lines. + */ + __be16 buf __aligned(IIO_DMA_MINALIGN); +}; + +static ssize_t ad5710r_get_powerdown_mode(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5710r_state *st = iio_priv(indio_dev); + unsigned int mode; + int ret; + + ret = regmap_read(st->regmap, AD5710R_CHN_VMODE_EN, &mode); + if (ret) + return ret; + + if (mode & BIT(chan->channel)) + return sysfs_emit(buf, "15kohm_to_gnd\n"); + + return sysfs_emit(buf, "high_z\n"); +} + +static ssize_t ad5710r_get_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5710r_state *st = iio_priv(indio_dev); + unsigned int mode; + int ret; + + ret = regmap_read(st->regmap, chan->channel < 4 ? + AD5710R_OUTPUT_OPERATING_MODE_0 : + AD5710R_OUTPUT_OPERATING_MODE_1, &mode); + if (ret) + return ret; + + if (mode & AD5710R_OP_MODE_CHAN_MSK(chan->channel < 4 ? chan->channel : + chan->channel - 4)) + return sysfs_emit(buf, "1\n"); + + return sysfs_emit(buf, "0\n"); +} + +static ssize_t ad5710r_set_dac_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5710r_state *st = iio_priv(indio_dev); + unsigned int reg, mask, val; + bool powerdown; + int ret; + + ret = kstrtobool(buf, &powerdown); + if (ret) + return ret; + + reg = chan->channel < 4 ? AD5710R_OUTPUT_OPERATING_MODE_0 : + AD5710R_OUTPUT_OPERATING_MODE_1; + + mask = chan->channel < 4 ? AD5710R_OP_MODE_CHAN_MSK(chan->channel) : + AD5710R_OP_MODE_CHAN_MSK(chan->channel - 4); + + val = field_prep(mask, powerdown); + + ret = regmap_update_bits(st->regmap, reg, mask, val); + if (ret) + return ret; + + return len; +} + +static int ad5710r_trigger_hw_ldac(struct gpio_desc *ldac_gpio) +{ + gpiod_set_value_cansleep(ldac_gpio, 1); + fsleep(AD5710R_LDAC_PULSE_US); + gpiod_set_value_cansleep(ldac_gpio, 0); + + return 0; +} + +static int ad5710r_dac_write(struct ad5710r_state *st, unsigned int chan, + unsigned int val) +{ + int ret; + + st->buf = cpu_to_be16(val << (16 - st->chip_info->resolution)); + + ret = regmap_bulk_write(st->regmap, AD5710R_INPUT_CH(chan), + &st->buf, sizeof(st->buf)); + if (ret) + return ret; + + if (st->ldac_gpio) + return ad5710r_trigger_hw_ldac(st->ldac_gpio); + + return regmap_set_bits(st->regmap, AD5710R_SW_LDAC_TRIG_A, + AD5710R_SLD_TRIG_A); +} + +static int ad5710r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad5710r_state *st = iio_priv(indio_dev); + unsigned int mode; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = regmap_bulk_read(st->regmap, + AD5710R_INPUT_CH(chan->channel), + &st->buf, sizeof(st->buf)); + if (ret) + return ret; + + *val = be16_to_cpu(st->buf) >> (16 - st->chip_info->resolution); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regmap_read(st->regmap, AD5710R_CHN_VMODE_EN, &mode); + if (ret) + return ret; + + if (mode & BIT(chan->channel)) + *val = st->vref_mV; + else + *val = AD5710R_INTERNAL_IREF_mA; + + *val2 = st->chip_info->resolution; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ad5710r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad5710r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (val < 0 || val > ((1 << st->chip_info->resolution) - 1)) + return -EINVAL; + + return ad5710r_dac_write(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad5710r_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad5710r_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static const struct iio_chan_spec_ext_info ad5710r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad5710r_get_dac_powerdown, + .write = ad5710r_set_dac_powerdown, + }, + { + .name = "powerdown_mode", + .shared = IIO_SEPARATE, + .read = ad5710r_get_powerdown_mode, + }, + { } +}; + +static const struct ad5710r_chip_info ad5710r_chip = { + .name = "ad5710r", + .resolution = 16, +}; + +static const struct ad5710r_chip_info ad5711r_chip = { + .name = "ad5711r", + .resolution = 12, +}; + +static int ad5710r_parse_channel_cfg(struct ad5710r_state *st, u8 *num_channels) +{ + struct device *dev = regmap_get_device(st->regmap); + int ret, num_chan; + u32 reg; + + num_chan = device_get_child_node_count(dev); + if (!num_chan) + return dev_err_probe(dev, -ENODEV, "No channels configured\n"); + + st->iio_channels = devm_kcalloc(dev, num_chan, sizeof(*st->iio_channels), GFP_KERNEL); + if (!st->iio_channels) + return -ENOMEM; + + device_for_each_child_node_scoped(dev, child) { + u32 ch_func; + enum iio_chan_type chan_type; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "Failed to read reg property of %pfwP\n", child); + + if (reg >= AD5710R_NUM_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "reg out of range in %pfwP\n", child); + + ret = fwnode_property_read_u32(child, "adi,ch-func", &ch_func); + if (ret) + return dev_err_probe(dev, ret, + "Missing adi,ch-func property for %pfwP\n", child); + + switch (ch_func) { + case CH_FUNC_VOLTAGE_OUTPUT: + ret = regmap_set_bits(st->regmap, AD5710R_CHN_VMODE_EN, + AD5710R_CHN_VMODE_EN_BIT(reg)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set voltage output mode for %pfwP\n", child); + + chan_type = IIO_VOLTAGE; + break; + case CH_FUNC_CURRENT_OUTPUT: + chan_type = IIO_CURRENT; + break; + default: + return dev_err_probe(dev, -EINVAL, + "Invalid adi,ch-func %u for %pfwP\n", + ch_func, child); + } + + st->iio_channels[reg] = (struct iio_chan_spec) { + .type = chan_type, + .channel = reg, + .indexed = 1, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .ext_info = ad5710r_ext_info, + }; + + /* Enable the channel in normal operation mode */ + if (reg < 4) { + ret = regmap_update_bits(st->regmap, AD5710R_OUTPUT_OPERATING_MODE_0, + AD5710R_OP_MODE_CHAN_MSK(reg), + field_prep(AD5710R_OP_MODE_CHAN_MSK(reg), + AD5710R_NORMAL_OP)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set normal operating mode for %pfwP\n", child); + } else { + ret = regmap_update_bits(st->regmap, AD5710R_OUTPUT_OPERATING_MODE_1, + AD5710R_OP_MODE_CHAN_MSK(reg - 4), + field_prep(AD5710R_OP_MODE_CHAN_MSK(reg - 4), + AD5710R_NORMAL_OP)); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set normal operating mode for %pfwP\n", child); + } + } + + *num_channels = num_chan; + + return 0; +} + +static int ad5710r_setup(struct ad5710r_state *st, int external_vref_uV, u8 *num_channels) +{ + struct device *dev = regmap_get_device(st->regmap); + struct gpio_desc *reset_gpio; + u8 range_multiplier; + int ret; + + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpio)) + return dev_err_probe(dev, PTR_ERR(reset_gpio), + "Failed to get reset GPIO\n"); + + if (reset_gpio) { + /* Perform hardware reset */ + fsleep(1 * USEC_PER_MSEC); + gpiod_set_value_cansleep(reset_gpio, 0); + } else { + /* Perform software reset */ + ret = regmap_update_bits(st->regmap, AD5710R_INTERFACE_CONFIG_A, + AD5710R_SW_RESET, AD5710R_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, AD5710R_OUTPUT_CONTROL_0, + AD5710R_OUTPUT_CONTROL_RANGE); + if (ret) + return ret; + + range_multiplier = 2; + } + + if (external_vref_uV) { + st->vref_mV = range_multiplier * external_vref_uV / MILLI; + } else { + ret = regmap_set_bits(st->regmap, AD5710R_REFERENCE_CONTROL_0, + AD5710R_REFERENCE_CONTROL_SEL); + if (ret) + return ret; + + st->vref_mV = range_multiplier * AD5710R_INTERNAL_VREF_mV; + } + + st->ldac_gpio = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_LOW); + if (IS_ERR(st->ldac_gpio)) + return dev_err_probe(dev, PTR_ERR(st->ldac_gpio), + "Failed to get ldac GPIO\n"); + + ret = ad5710r_parse_channel_cfg(st, num_channels); + if (ret) + return ret; + + return 0; +} + +static const struct regmap_config ad5710r_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = AD5710R_CHN_VMODE_EN, +}; + +static const struct iio_info ad5710r_info = { + .read_raw = ad5710r_read_raw, + .write_raw = ad5710r_write_raw, + .debugfs_reg_access = ad5710r_reg_access, +}; + +static int ad5710r_probe(struct spi_device *spi) +{ + static const char * const regulators[] = { "vdd", "iovdd" }; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad5710r_state *st; + int ret, external_vref_uV; + u8 num_channels; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->regmap = devm_regmap_init_spi(spi, &ad5710r_regmap_config); + if (IS_ERR(st->regmap)) + return dev_err_probe(dev, PTR_ERR(st->regmap), + "Failed to init regmap"); + + st->chip_info = spi_get_device_match_data(spi); + if (!st->chip_info) + return -ENODEV; + + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulators), + regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + external_vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref"); + if (external_vref_uV < 0 && external_vref_uV != -ENODEV) + return external_vref_uV; + + if (external_vref_uV == -ENODEV) + external_vref_uV = 0; + + ret = ad5710r_setup(st, external_vref_uV, &num_channels); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->info = &ad5710r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->iio_channels; + indio_dev->num_channels = num_channels; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad5710r_id[] = { + { "ad5710r", (kernel_ulong_t)&ad5710r_chip }, + { "ad5711r", (kernel_ulong_t)&ad5711r_chip }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5710r_id); + +static const struct of_device_id ad5710r_of_match[] = { + { .compatible = "adi,ad5710r", .data = &ad5710r_chip }, + { .compatible = "adi,ad5711r", .data = &ad5711r_chip }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5710r_of_match); + +static struct spi_driver ad5710r_driver = { + .driver = { + .name = "ad5710r", + .of_match_table = ad5710r_of_match, + }, + .probe = ad5710r_probe, + .id_table = ad5710r_id, +}; +module_spi_driver(ad5710r_driver); + +MODULE_AUTHOR("Kim Seer Paller "); +MODULE_DESCRIPTION("Analog Devices AD5710R/AD5711R DAC Driver"); +MODULE_LICENSE("GPL"); From 1f98b404318bbfd0d4163b68163e4429c90aacf8 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 16 Mar 2026 16:42:17 +0800 Subject: [PATCH 4/4] iio: Kconfig.adi: imply AD5710R Add entry for the AD5710R driver. Signed-off-by: Kim Seer Paller --- drivers/iio/Kconfig.adi | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/Kconfig.adi b/drivers/iio/Kconfig.adi index d34388a1c2c580..312b8794e5ed83 100644 --- a/drivers/iio/Kconfig.adi +++ b/drivers/iio/Kconfig.adi @@ -217,3 +217,4 @@ config IIO_ALL_ADI_DRIVERS imply AD7091R8 imply AD9739A imply MAX22007 + imply AD5710R