* [PATCH 0/2] iio: dac: add support for AD5413
@ 2026-10-16 19:23 Bruce Tsao via B4 Relay
2026-10-16 19:23 ` [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC Bruce Tsao via B4 Relay
2026-10-16 19:23 ` [PATCH 2/2] iio: dac: add AD5413 driver Bruce Tsao via B4 Relay
0 siblings, 2 replies; 7+ messages in thread
From: Bruce Tsao via B4 Relay @ 2026-10-16 19:23 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-iio, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-kernel, Bruce Tsao
This series adds support for the Analog Devices AD5413: a single-channel,
14-bit, serial-input DAC with software-selectable voltage (+/-10.5 V) and
current (0-24 mA) outputs. The SPI interface runs in mode 1 and carries a
CRC-8 on every frame, which the device has enabled out of reset.
Patch 1 documents the device-tree binding; patch 2 adds the IIO driver.
Tested on an EVAL-AD5413SDZ with a Raspberry Pi 5 and an NXP i.MX93 FRDM
board (probe, voltage- and current-output, scale/offset, powerdown/resume).
Signed-off-by: Bruce Tsao <bruce.tsao@analog.com>
---
Bruce Tsao (2):
dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC
iio: dac: add AD5413 driver
.../devicetree/bindings/iio/dac/adi,ad5413.yaml | 101 ++++
drivers/iio/dac/Kconfig | 11 +
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/ad5413.c | 666 +++++++++++++++++++++
4 files changed, 779 insertions(+)
---
base-commit: a3b3580713f3ac5a32dc2874ee546828977a1d68
change-id: 20261017-ad5413-upstream-f8664fff4fdb
Best regards,
--
Bruce Tsao <bruce.tsao@analog.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC
2026-10-16 19:23 [PATCH 0/2] iio: dac: add support for AD5413 Bruce Tsao via B4 Relay
@ 2026-10-16 19:23 ` Bruce Tsao via B4 Relay
2026-10-16 19:36 ` sashiko-bot
2026-10-16 23:02 ` Jonathan Cameron
2026-10-16 19:23 ` [PATCH 2/2] iio: dac: add AD5413 driver Bruce Tsao via B4 Relay
1 sibling, 2 replies; 7+ messages in thread
From: Bruce Tsao via B4 Relay @ 2026-10-16 19:23 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-iio, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-kernel, Bruce Tsao
From: Bruce Tsao <bruce.tsao@analog.com>
Document the devicetree bindings for the Analog Devices AD5413, a
single-channel precision DAC controlled via SPI. The device supports
voltage or current output modes and programmable slew-rate control.
Signed-off-by: Bruce Tsao <bruce.tsao@analog.com>
---
.../devicetree/bindings/iio/dac/adi,ad5413.yaml | 101 +++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml
new file mode 100644
index 000000000000..74e869304aab
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/dac/adi,ad5413.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD5413 DAC
+
+description:
+ Analog Devices AD5413 is a single-channel precision DAC supporting voltage
+ or current output modes with programmable slew-rate control.
+ The output mode is selected via the adi,chan-current-mode property.
+ Optional reset GPIO may be provided.
+
+maintainers:
+ - bruce.tsao@analog.com
+
+properties:
+ compatible:
+ const: adi,ad5413
+
+ spi-max-frequency:
+ maximum: 50000000
+
+ spi-cpha: true
+
+ spi-cpol: false
+
+ reset-gpios:
+ maxItems: 1
+ description: Optional reset GPIO (active low).
+
+ avdd-supply:
+ description: Analog positive supply (AVDD)
+
+ dvdd-supply:
+ description: Digital positive supply (DVDD)
+
+ avss-supply:
+ description: Analog negative supply (AVSS)
+
+ adi,chan-current-mode:
+ type: boolean
+ description: Select current output mode. If absent, voltage mode is used.
+
+ adi,slew-time-us:
+ description: Output digital slew control time in microseconds
+ minimum: 0
+ maximum: 1000000
+ default: 0
+
+required:
+ - compatible
+ - spi-cpha
+ - avdd-supply
+ - dvdd-supply
+ - avss-supply
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "adi,ad5413";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ spi-cpha;
+ reset-gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
+ avdd-supply = <&avdd>;
+ dvdd-supply = <&dvdd>;
+ avss-supply = <&avss>;
+ adi,slew-time-us = <125>;
+ };
+ };
+
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ dac@0 {
+ compatible = "adi,ad5413";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ spi-cpha;
+ avdd-supply = <&avdd>;
+ dvdd-supply = <&dvdd>;
+ avss-supply = <&avss>;
+
+ adi,chan-current-mode;
+ };
+ };
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/2] iio: dac: add AD5413 driver
2026-10-16 19:23 [PATCH 0/2] iio: dac: add support for AD5413 Bruce Tsao via B4 Relay
2026-10-16 19:23 ` [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC Bruce Tsao via B4 Relay
@ 2026-10-16 19:23 ` Bruce Tsao via B4 Relay
2026-10-16 19:33 ` sashiko-bot
2026-10-16 23:40 ` Jonathan Cameron
1 sibling, 2 replies; 7+ messages in thread
From: Bruce Tsao via B4 Relay @ 2026-10-16 19:23 UTC (permalink / raw)
To: Jonathan Cameron
Cc: linux-iio, David Lechner, Nuno Sá, Andy Shevchenko,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, devicetree,
linux-kernel, Bruce Tsao
From: Bruce Tsao <bruce.tsao@analog.com>
The AD5413 is a single-channel, 14-bit, voltage and current output
digital-to-analog converter (DAC) with programmable output ranges,
slew rate control and on-chip diagnostics.
Add an IIO driver supporting the +-10.5 V voltage range and the
0-24 mA current range, output enable, slew rate control and device
reset over the SPI interface.
The device powers up with the SPI CRC enabled, so every frame carries
a CRC-8 byte and the interface is configured for SPI mode 1. This lets
the driver bring the device up directly from a cold boot without any
pre-conditioning.
Signed-off-by: Bruce Tsao <bruce.tsao@analog.com>
---
drivers/iio/dac/Kconfig | 11 +
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/ad5413.c | 666 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 678 insertions(+)
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index e373885e7c7a..402370c67e87 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -91,6 +91,17 @@ config AD5380
To compile this driver as module choose M here: the module will be called
ad5380.
+config AD5413
+ tristate "Analog Devices AD5413 DAC driver"
+ depends on SPI_MASTER
+ select CRC8
+ help
+ Say yes here to build support for Analog Devices AD5413 single channel
+ Digital to Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5413.
+
config AD5421
tristate "Analog Devices AD5421 DAC driver"
depends on SPI
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 2cd0fe23db9e..0157face1f67 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_AD3552R_LIB) += ad3552r-common.o
obj-$(CONFIG_AD3552R) += ad3552r.o
obj-$(CONFIG_AD5360) += ad5360.o
obj-$(CONFIG_AD5380) += ad5380.o
+obj-$(CONFIG_AD5413) += ad5413.o
obj-$(CONFIG_AD5421) += ad5421.o
obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
obj-$(CONFIG_AD5064) += ad5064.o
diff --git a/drivers/iio/dac/ad5413.c b/drivers/iio/dac/ad5413.c
new file mode 100644
index 000000000000..96cce32fc640
--- /dev/null
+++ b/drivers/iio/dac/ad5413.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AD5413 Digital to analog converters driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+#include <linux/util_macros.h>
+
+/* AD5413 registers definition */
+#define AD5413_REG_NOP 0x00
+#define AD5413_REG_DAC_INPUT 0x01
+#define AD5413_REG_DAC_OUTPUT 0x02
+#define AD5413_REG_CLEAR_CODE 0x03
+#define AD5413_REG_USER_GAIN 0x04
+#define AD5413_REG_USER_OFFSET 0x05
+#define AD5413_REG_DAC_CONFIG 0x06
+#define AD5413_REG_DAC_CONFIG_SR_STEP_MSK GENMASK(15, 13)
+#define AD5413_REG_DAC_CONFIG_SR_CLOCK_MSK GENMASK(12, 9)
+#define AD5413_REG_DAC_CONFIG_SR_EN_MSK BIT(8)
+#define AD5413_REG_DAC_CONFIG_RSET_EXT_EN_MSK BIT(7)
+#define AD5413_REG_DAC_CONFIG_OUT_EN_MSK BIT(6)
+#define AD5413_REG_DAC_CONFIG_INT_EN_MSK BIT(5)
+#define AD5413_REG_DAC_CONFIG_OVRNG_EN_MSK BIT(4)
+#define AD5413_REG_DAC_CONFIG_RANGE_MSK GENMASK(3, 0)
+#define AD5413_REG_SW_LDAC 0x07
+#define AD5413_REG_KEY 0x08
+#define AD5413_REG_KEY_REG_ADDR_MASK GENMASK(20, 16)
+#define AD5413_REG_KEY_CODE_MASK GENMASK(15, 0)
+#define AD5413_REG_GP_CONFIG1 0x09
+#define AD5413_REG_GP_CONFIG2 0x0A
+#define AD5413_REG_DIGITAL_DIAG_CONFIG 0x10
+#define AD5413_REG_FAULT_PIN_CONFIG 0x12
+#define AD5413_REG_TWO_STAGE_READBACK_SELECT 0x13
+#define AD5413_REG_DIGITAL_DIAG_RESULTS 0x14
+#define AD5413_REG_DIGITAL_DIAG_CAL_MEM_UNREFRESHED_MSK BIT(15)
+#define AD5413_REG_DIGITAL_DIAG_SLEW_BUSY_MSK BIT(14)
+#define AD5413_REG_ANALOG_DIAG_RESULTS 0x15
+#define AD5413_REG_STATUS 0x16
+#define AD5413_REG_CHIP_ID 0x17
+#define AD5413_REG_FREQ_MONITOR 0x18
+#define AD5413_REG_DEVICE_ID_3 0x1C
+
+/* Special Key Codes (bits[15:0]) */
+#define AD5413_REG_KEY_CODE_RESET_1 0x15FA
+#define AD5413_REG_KEY_CODE_RESET_2 0xAF51
+#define AD5413_REG_KEY_CODE_CALIB_MEM_REFRESH 0xFCBA
+
+/* DIGITAL_DIAG_RESULTS Register (Addr = 0x14) Busy Flags */
+
+#define AD5413_WR_FLAG_MSK(x) (0x80u | ((x) & 0x1F))
+
+/* CRC-8 polynomial x^8 + x^2 + x + 1, enabled by default on the device */
+#define AD5413_CRC8_POLY 0x07
+
+#define AD5413_FULL_SCALE_MICRO (16383ULL * MICRO)
+
+struct ad5413_range {
+ int reg;
+ int min;
+ int max;
+};
+
+struct ad5413_state {
+ struct spi_device *spi;
+ struct regulator_bulk_data supplies[3];
+ struct mutex lock; /* Protects all register access via SPI */
+ const struct ad5413_range *out_range;
+ unsigned int slew_time;
+ bool pwr_down;
+ u8 crc8_tbl[CRC8_TABLE_SIZE];
+ __be32 d32[3] __aligned(IIO_DMA_MINALIGN);
+};
+
+/*
+ * Output ranges corresponding to DAC_CONFIG[3:0] bits.
+ * Defined by the following symbolic identifiers:
+ * AD5413_RANGE_PLUSMINUS_10_5V - ±10.5 V voltage range
+ * AD5413_RANGE_0MA_24MA - 0 mA to 24 mA current range
+ */
+enum ad5413_output_range {
+ AD5413_RANGE_PLUSMINUS_10_5V = 0x3,
+ AD5413_RANGE_0MA_24MA = 0x9,
+};
+
+static const struct ad5413_range ad5413_voltage_range[] = { {
+ .reg = AD5413_RANGE_PLUSMINUS_10_5V,
+ .min = -10500000,
+ .max = 10500000,
+} };
+
+static const struct ad5413_range ad5413_current_range[] = { {
+ .reg = AD5413_RANGE_0MA_24MA,
+ .min = 0,
+ .max = 24000,
+} };
+
+static const int ad5413_sr_clk[16] = {
+ 240000, 200000, 150000, 128000, 64000, 32000, 16000, 8000,
+ 4000, 2000, 1000, 512, 256, 128, 64, 16,
+};
+
+static const int ad5413_sr_step[8] = {
+ 4, 12, 64, 120, 256, 500, 1820, 2048,
+};
+
+static int ad5413_spi_reg_read(struct ad5413_state *st, unsigned int addr,
+ u16 *val)
+{
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d32[0],
+ .len = 4,
+ .cs_change = 1,
+ },
+ {
+ .tx_buf = &st->d32[1],
+ .rx_buf = &st->d32[2],
+ .len = 4,
+ },
+ };
+ u8 sel[3], nop[3];
+ int ret;
+
+ /* Two-stage readback: select the register to read back. */
+ sel[0] = AD5413_WR_FLAG_MSK(AD5413_REG_TWO_STAGE_READBACK_SELECT);
+ sel[1] = 0;
+ sel[2] = addr;
+ st->d32[0] = cpu_to_be32((sel[0] << 24) | (sel[1] << 16) |
+ (sel[2] << 8) |
+ crc8(st->crc8_tbl, sel, sizeof(sel), 0));
+
+ /* NOP frame clocks the selected register contents out on SDO. */
+ nop[0] = AD5413_WR_FLAG_MSK(AD5413_REG_NOP);
+ nop[1] = 0;
+ nop[2] = 0;
+ st->d32[1] = cpu_to_be32((nop[0] << 24) | (nop[1] << 16) |
+ (nop[2] << 8) |
+ crc8(st->crc8_tbl, nop, sizeof(nop), 0));
+
+ ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The readback CRC covers the live FAULT pin status bit, which can
+ * toggle between capture and clock-out, so it is not validated here.
+ */
+ *val = be32_to_cpu(st->d32[2]) >> 8;
+
+ return 0;
+}
+
+static int ad5413_spi_reg_write(struct ad5413_state *st, unsigned int addr,
+ unsigned int val)
+{
+ u8 buf[3] = { };
+
+ buf[0] = AD5413_WR_FLAG_MSK(addr);
+ buf[1] = val >> 8;
+ buf[2] = val;
+
+ st->d32[0] = cpu_to_be32((buf[0] << 24) | (buf[1] << 16) |
+ (buf[2] << 8) |
+ crc8(st->crc8_tbl, buf, sizeof(buf), 0));
+
+ return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0]));
+}
+
+static int ad5413_spi_write_mask(struct ad5413_state *st, unsigned int addr,
+ unsigned long mask, unsigned int val)
+{
+ u16 regval;
+ int ret;
+
+ ret = ad5413_spi_reg_read(st, addr, ®val);
+ if (ret)
+ return ret;
+
+ return ad5413_spi_reg_write(st, addr, (regval & ~mask) | val);
+}
+
+static int ad5413_wait_for_task_complete(struct ad5413_state *st,
+ unsigned int reg, unsigned int mask)
+{
+ u16 val;
+ int ret, err;
+
+ err = read_poll_timeout(ad5413_spi_reg_read, ret,
+ ret || !(val & mask),
+ 100, 1000, false, st, reg, &val);
+ if (ret)
+ return ret;
+
+ if (err) {
+ dev_err(&st->spi->dev,
+ "Timeout waiting for bit 0x%x to clear in reg 0x%x\n",
+ mask, reg);
+ return err;
+ }
+
+ return 0;
+}
+
+static int ad5413_calib_mem_refresh(struct ad5413_state *st)
+{
+ int ret;
+
+ ret = ad5413_spi_reg_write(st, AD5413_REG_KEY,
+ AD5413_REG_KEY_CODE_CALIB_MEM_REFRESH);
+ if (ret < 0)
+ return dev_err_probe(&st->spi->dev, ret,
+ "Failed to initiate a calibration memory refresh\n");
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5413_wait_for_task_complete(st,
+ AD5413_REG_DIGITAL_DIAG_RESULTS,
+ AD5413_REG_DIGITAL_DIAG_CAL_MEM_UNREFRESHED_MSK);
+}
+
+static int ad5413_slew_rate_set(struct ad5413_state *st,
+ unsigned int sr_clk_idx,
+ unsigned int sr_step_idx)
+{
+ unsigned int mode;
+ unsigned long mask;
+ int ret;
+
+ mask = AD5413_REG_DAC_CONFIG_SR_EN_MSK |
+ AD5413_REG_DAC_CONFIG_SR_CLOCK_MSK |
+ AD5413_REG_DAC_CONFIG_SR_STEP_MSK;
+ mode = FIELD_PREP(AD5413_REG_DAC_CONFIG_SR_EN_MSK, 1) |
+ FIELD_PREP(AD5413_REG_DAC_CONFIG_SR_STEP_MSK, sr_step_idx) |
+ FIELD_PREP(AD5413_REG_DAC_CONFIG_SR_CLOCK_MSK, sr_clk_idx);
+
+ ret = ad5413_spi_write_mask(st, AD5413_REG_DAC_CONFIG, mask, mode);
+ if (ret < 0)
+ return ret;
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5413_wait_for_task_complete(st,
+ AD5413_REG_DIGITAL_DIAG_RESULTS,
+ AD5413_REG_DIGITAL_DIAG_SLEW_BUSY_MSK);
+}
+
+static int ad5413_slew_rate_config(struct ad5413_state *st)
+{
+ unsigned int sr_clk_idx = 0, sr_step_idx = 0;
+ int i, res;
+ s64 diff_new, diff_old = S64_MAX;
+ u64 sr_step, calc_slew_time;
+
+ /*
+ * The slew time can be determined by using the formula:
+ * Slew Time = (Full Scale Out / (Step Size x Update Clk Freq))
+ * where Slew time is expressed in microseconds
+ * Given the desired slew time, the following algorithm determines the
+ * best match for the step size and the update clock frequency.
+ */
+ for (i = 0; i < ARRAY_SIZE(ad5413_sr_clk); i++) {
+ /*
+ * Go through each valid update clock freq and determine a raw
+ * value for the step size by using the formula:
+ * Step Size = Full Scale Out / (Update Clk Freq * Slew Time)
+ */
+ sr_step = div64_u64(AD5413_FULL_SCALE_MICRO,
+ (u64)ad5413_sr_clk[i] * st->slew_time);
+ /*
+ * After a raw value for step size was determined, find the
+ * closest valid match
+ */
+ res = find_closest(sr_step, ad5413_sr_step, ARRAY_SIZE(ad5413_sr_step));
+
+ /* Calculate the slew time */
+ calc_slew_time = div_u64(AD5413_FULL_SCALE_MICRO,
+ ad5413_sr_step[res] * ad5413_sr_clk[i]);
+ /*
+ * Determine with how many microseconds the calculated slew time
+ * is different from the desired slew time and store the diff
+ * for the next iteration
+ */
+ diff_new = abs(st->slew_time - calc_slew_time);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ sr_clk_idx = i;
+ sr_step_idx = res;
+ }
+ }
+
+ return ad5413_slew_rate_set(st, sr_clk_idx, sr_step_idx);
+}
+
+static int ad5413_set_out_range(struct ad5413_state *st, int range)
+{
+ int ret;
+
+ ret = ad5413_spi_write_mask(st,
+ AD5413_REG_DAC_CONFIG,
+ AD5413_REG_DAC_CONFIG_RANGE_MSK,
+ FIELD_PREP(AD5413_REG_DAC_CONFIG_RANGE_MSK,
+ range));
+ if (ret < 0)
+ return ret;
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5413_wait_for_task_complete(st,
+ AD5413_REG_DIGITAL_DIAG_RESULTS,
+ AD5413_REG_DIGITAL_DIAG_CAL_MEM_UNREFRESHED_MSK);
+}
+
+static int ad5413_internal_buffers_en(struct ad5413_state *st, bool enable)
+{
+ int ret;
+
+ ret = ad5413_spi_write_mask(st,
+ AD5413_REG_DAC_CONFIG,
+ AD5413_REG_DAC_CONFIG_INT_EN_MSK,
+ FIELD_PREP(AD5413_REG_DAC_CONFIG_INT_EN_MSK,
+ enable));
+ if (ret < 0)
+ return ret;
+
+ /* Wait to allow time for the internal calibrations to complete */
+ return ad5413_wait_for_task_complete(st,
+ AD5413_REG_DIGITAL_DIAG_RESULTS,
+ AD5413_REG_DIGITAL_DIAG_CAL_MEM_UNREFRESHED_MSK);
+}
+
+static int ad5413_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct ad5413_state *st = iio_priv(indio_dev);
+ u16 regval;
+ int ret;
+
+ guard(mutex)(&st->lock);
+ if (readval) {
+ ret = ad5413_spi_reg_read(st, reg, ®val);
+ if (ret)
+ return ret;
+
+ *readval = regval;
+ return 0;
+ }
+
+ return ad5413_spi_reg_write(st, reg, writeval);
+}
+
+static int ad5413_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long info)
+{
+ struct ad5413_state *st = iio_priv(indio_dev);
+ int max, min, ret;
+ u16 regval;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW: {
+ guard(mutex)(&st->lock);
+ ret = ad5413_spi_reg_read(st, AD5413_REG_DAC_INPUT, ®val);
+ if (ret)
+ return ret;
+
+ /* 14-bit DAC data is in bits [15:2] of the register. */
+ *val = regval >> 2;
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ min = st->out_range->min;
+ max = st->out_range->max;
+ *val = (max - min) / 1000;
+ *val2 = 14;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ min = st->out_range->min;
+ max = st->out_range->max;
+ *val = div_s64((s64)min * BIT(14), max - min);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad5413_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2,
+ long info)
+{
+ struct ad5413_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW: {
+ guard(mutex)(&st->lock);
+ /* 14-bit DAC data is in bits [15:2] of the register. */
+ ret = ad5413_spi_reg_write(st, AD5413_REG_DAC_INPUT, val << 2);
+ return ret;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ad5413_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ad5413_state *st = iio_priv(indio_dev);
+
+ return sysfs_emit(buf, "%d\n", st->pwr_down);
+}
+
+static ssize_t ad5413_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ struct iio_chan_spec const *chan,
+ const char *buf, size_t len)
+{
+ struct ad5413_state *st = iio_priv(indio_dev);
+ bool pwr_down;
+ unsigned int dac_config_mode, val;
+ unsigned long dac_config_msk;
+ int ret;
+
+ ret = kstrtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ val = pwr_down ? 0 : 1;
+
+ dac_config_mode = FIELD_PREP(AD5413_REG_DAC_CONFIG_OUT_EN_MSK, (val)) |
+ FIELD_PREP(AD5413_REG_DAC_CONFIG_INT_EN_MSK, (val));
+ dac_config_msk = AD5413_REG_DAC_CONFIG_OUT_EN_MSK |
+ AD5413_REG_DAC_CONFIG_INT_EN_MSK;
+
+ guard(mutex)(&st->lock);
+ ret = ad5413_spi_write_mask(st, AD5413_REG_DAC_CONFIG, dac_config_msk,
+ dac_config_mode);
+ if (ret < 0)
+ return ret;
+
+ st->pwr_down = pwr_down;
+
+ return len;
+}
+
+static const struct iio_info ad5413_info = {
+ .read_raw = ad5413_read_raw,
+ .write_raw = ad5413_write_raw,
+ .debugfs_reg_access = &ad5413_reg_access,
+};
+
+static const struct iio_chan_spec_ext_info ad5413_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5413_read_powerdown,
+ .write = ad5413_write_powerdown,
+ .shared = IIO_SHARED_BY_TYPE,
+ },
+ { }
+};
+
+#define AD5413_DAC_CHAN(_chan_type) \
+ { \
+ .type = (_chan_type), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .indexed = 1, \
+ .output = 1, \
+ .ext_info = ad5413_ext_info, \
+ }
+
+static const struct iio_chan_spec ad5413_voltage_ch[] = {
+ AD5413_DAC_CHAN(IIO_VOLTAGE),
+};
+
+static const struct iio_chan_spec ad5413_current_ch[] = {
+ AD5413_DAC_CHAN(IIO_CURRENT),
+};
+
+static int ad5413_parse_fw(struct ad5413_state *st)
+{
+ struct device *dev = &st->spi->dev;
+ int ret;
+
+ if (device_property_read_bool(dev, "adi,chan-current-mode"))
+ st->out_range = &ad5413_current_range[0];
+ else
+ st->out_range = &ad5413_voltage_range[0];
+
+ st->slew_time = 0;
+ if (device_property_present(dev, "adi,slew-time-us")) {
+ ret = device_property_read_u32(dev,
+ "adi,slew-time-us",
+ &st->slew_time);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read adi,slew-time-us\n");
+
+ if (st->slew_time > 1 * MICRO)
+ return dev_err_probe(dev, -EINVAL, "adi,slew-time-us out of range\n");
+ }
+
+ return 0;
+}
+
+static int ad5413_init(struct ad5413_state *st)
+{
+ struct reset_control *reset;
+ u16 regval;
+ int ret;
+
+ reset = devm_reset_control_get_optional_exclusive(&st->spi->dev, NULL);
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ if (reset) {
+ /* Hard reset via the reset controller */
+ ret = reset_control_assert(reset);
+ if (ret)
+ return ret;
+
+ fsleep(100);
+
+ ret = reset_control_deassert(reset);
+ if (ret)
+ return ret;
+
+ fsleep(100);
+ } else {
+ /*
+ * CRC is enabled by default after power-on/reset. The SPI
+ * helpers always append the CRC byte, so the device accepts
+ * these soft-reset key frames without any bootstrap step.
+ */
+ ret = ad5413_spi_reg_write(st, AD5413_REG_KEY,
+ AD5413_REG_KEY_CODE_RESET_1);
+ if (ret)
+ return ret;
+
+ ret = ad5413_spi_reg_write(st, AD5413_REG_KEY,
+ AD5413_REG_KEY_CODE_RESET_2);
+ if (ret)
+ return ret;
+
+ fsleep(100);
+ }
+
+ ret = ad5413_calib_mem_refresh(st);
+ if (ret < 0)
+ return ret;
+
+ ret = ad5413_spi_reg_read(st, AD5413_REG_DIGITAL_DIAG_RESULTS, ®val);
+ if (ret)
+ return ret;
+
+ ret = ad5413_spi_reg_write(st, AD5413_REG_DIGITAL_DIAG_RESULTS, regval);
+ if (ret < 0)
+ return ret;
+
+ ret = ad5413_set_out_range(st, st->out_range->reg);
+ if (ret < 0)
+ return ret;
+
+ if (st->slew_time) {
+ ret = ad5413_slew_rate_config(st);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = ad5413_internal_buffers_en(st, 1);
+ if (ret < 0)
+ return ret;
+
+ return ad5413_spi_write_mask(st,
+ AD5413_REG_DAC_CONFIG,
+ AD5413_REG_DAC_CONFIG_OUT_EN_MSK,
+ FIELD_PREP(AD5413_REG_DAC_CONFIG_OUT_EN_MSK, 1));
+}
+
+static const char * const ad5413_supply_names[] = {
+ "avdd",
+ "dvdd",
+ "avss",
+};
+
+static int ad5413_probe(struct spi_device *spi)
+{
+ struct ad5413_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->spi = spi;
+
+ ret = devm_mutex_init(&spi->dev, &st->lock);
+ if (ret)
+ return ret;
+
+ crc8_populate_msb(st->crc8_tbl, AD5413_CRC8_POLY);
+
+ indio_dev->name = "ad5413";
+ indio_dev->info = &ad5413_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = 1;
+
+ ret = ad5413_parse_fw(st);
+ if (ret < 0)
+ return ret;
+
+ if (st->out_range->reg == AD5413_RANGE_PLUSMINUS_10_5V)
+ indio_dev->channels = ad5413_voltage_ch;
+ else
+ indio_dev->channels = ad5413_current_ch;
+
+ ret = devm_regulator_bulk_get_enable(&spi->dev,
+ ARRAY_SIZE(ad5413_supply_names),
+ ad5413_supply_names);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "failed to enable supplies\n");
+
+ ret = ad5413_init(st);
+ if (ret < 0)
+ return dev_err_probe(&spi->dev, ret, "AD5413 init failed\n");
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad5413_id[] = {
+ { .name = "ad5413" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad5413_id);
+
+static const struct of_device_id ad5413_of_match[] = {
+ { .compatible = "adi,ad5413" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad5413_of_match);
+
+static struct spi_driver ad5413_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ad5413_of_match,
+ },
+ .probe = ad5413_probe,
+ .id_table = ad5413_id,
+};
+module_spi_driver(ad5413_driver);
+
+MODULE_AUTHOR("Bruce Tsao <bruce.tsao@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD5413 DAC");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] iio: dac: add AD5413 driver
2026-10-16 19:23 ` [PATCH 2/2] iio: dac: add AD5413 driver Bruce Tsao via B4 Relay
@ 2026-10-16 19:33 ` sashiko-bot
2026-10-16 23:40 ` Jonathan Cameron
1 sibling, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-10-16 19:33 UTC (permalink / raw)
To: Bruce Tsao; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] iio: dac: ad5413: missing range check in ad5413_write_raw()
- [Low] iio: dac: ad5413: unused supplies array in struct ad5413_state
--
commit bcfddef4b6cda1b4c239a6094a38aa835c2860bd
Author: Bruce Tsao <bruce.tsao@analog.com>
iio: dac: add AD5413 driver
This commit adds an IIO driver for the Analog Devices AD5413 single-channel
14-bit voltage and current output DAC over SPI.
> diff --git a/drivers/iio/dac/ad5413.c b/drivers/iio/dac/ad5413.c
> new file mode 100644
> index 0000000000000..96cce32fc640e
> --- /dev/null
> +++ b/drivers/iio/dac/ad5413.c
> @@ -0,0 +1,666 @@
[ ... ]
> +struct ad5413_state {
> + struct spi_device *spi;
> + struct regulator_bulk_data supplies[3];
[Severity: Low]
Is supplies[3] in struct ad5413_state needed? ad5413_probe() enables the
regulators using devm_regulator_bulk_get_enable() with ad5413_supply_names, so
this array appears to be unused.
> + struct mutex lock; /* Protects all register access via SPI */
> + const struct ad5413_range *out_range;
> + unsigned int slew_time;
> + bool pwr_down;
> + u8 crc8_tbl[CRC8_TABLE_SIZE];
> + __be32 d32[3] __aligned(IIO_DMA_MINALIGN);
> +};
[ ... ]
> +static int ad5413_spi_reg_write(struct ad5413_state *st, unsigned int addr,
> + unsigned int val)
> +{
> + u8 buf[3] = { };
> +
> + buf[0] = AD5413_WR_FLAG_MSK(addr);
> + buf[1] = val >> 8;
> + buf[2] = val;
> +
> + st->d32[0] = cpu_to_be32((buf[0] << 24) | (buf[1] << 16) |
> + (buf[2] << 8) |
> + crc8(st->crc8_tbl, buf, sizeof(buf), 0));
> +
> + return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0]));
> +}
[ ... ]
> +static int ad5413_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val, int val2,
> + long info)
> +{
> + struct ad5413_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + switch (info) {
> + case IIO_CHAN_INFO_RAW: {
> + guard(mutex)(&st->lock);
> + /* 14-bit DAC data is in bits [15:2] of the register. */
> + ret = ad5413_spi_reg_write(st, AD5413_REG_DAC_INPUT, val << 2);
> + return ret;
> + }
[Severity: Medium]
Should ad5413_write_raw() validate that val is within the 14-bit range
(0 <= val < BIT(14)) before shifting and writing to AD5413_REG_DAC_INPUT?
When userspace writes a negative value or a value >= BIT(14) to the raw sysfs
attribute, evaluating val << 2 on a negative signed int or a large positive
int triggers signed left-shift undefined behavior, and ad5413_spi_reg_write()
silently truncates the shifted value into the lower 16 bits of buf[1] and
buf[2], programming an unintended DAC output instead of returning -EINVAL.
> + default:
> + return -EINVAL;
> + }
> +}
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261017-ad5413-upstream-v1-0-61f40c8fef25@analog.com?part=2
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC
2026-10-16 19:23 ` [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC Bruce Tsao via B4 Relay
@ 2026-10-16 19:36 ` sashiko-bot
2026-10-16 23:02 ` Jonathan Cameron
1 sibling, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-10-16 19:36 UTC (permalink / raw)
To: Bruce Tsao; +Cc: devicetree, robh, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] dt-bindings: iio: dac: adi,ad5413: missing reg property and requirement
- [Medium] dt-bindings: iio: dac: adi,ad5413: invalid maintainers and spi-cpol schema
- [Medium] dt-bindings: iio: dac: adi,ad5413: custom boolean mode property duplicates standard range bindings
--
commit b9fdb51ef057c689f916309a04134bf25c49ed72
Author: Bruce Tsao <bruce.tsao@analog.com>
dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC
This commit documents the devicetree bindings for the Analog Devices AD5413
single-channel SPI DAC, including power supplies, reset GPIO, output mode,
and slew-rate control.
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml
> new file mode 100644
> index 0000000000000..74e869304aab9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml
> @@ -0,0 +1,101 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,ad5413.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AD5413 DAC
> +
> +description:
> + Analog Devices AD5413 is a single-channel precision DAC supporting voltage
> + or current output modes with programmable slew-rate control.
> + The output mode is selected via the adi,chan-current-mode property.
> + Optional reset GPIO may be provided.
> +
> +maintainers:
> + - bruce.tsao@analog.com
> +
> +properties:
> + compatible:
> + const: adi,ad5413
> +
> + spi-max-frequency:
> + maximum: 50000000
> +
> + spi-cpha: true
> +
> + spi-cpol: false
[Severity: Medium]
Will this schema pass make dt_binding_check?
In the maintainers block, the entry uses a bare email address rather than
the required Full Name <email@domain> format:
maintainers:
- Bruce Tsao <bruce.tsao@analog.com>
Also, in the top-level properties block, spi-cpol is set to false. In DT
schema, boolean false property schemas are only valid inside conditional
if/then/else blocks to disallow a property defined at the top level. Since
spi-cpol is not defined in spi-peripheral-props.yaml and this binding sets
unevaluatedProperties: false, should spi-cpol: false simply be omitted?
> +
> + reset-gpios:
> + maxItems: 1
> + description: Optional reset GPIO (active low).
> +
> + avdd-supply:
> + description: Analog positive supply (AVDD)
> +
> + dvdd-supply:
> + description: Digital positive supply (DVDD)
> +
> + avss-supply:
> + description: Analog negative supply (AVSS)
> +
> + adi,chan-current-mode:
> + type: boolean
> + description: Select current output mode. If absent, voltage mode is used.
[Severity: Medium]
Should the output mode and range be described using standard IIO DAC range
properties rather than a custom adi,chan-current-mode boolean?
In drivers/iio/dac/ad5413.c, the hardware supports a 4-bit range configuration
field (AD5413_REG_DAC_CONFIG_RANGE_MSK = GENMASK(3, 0)) with multiple voltage
and current ranges, while the driver currently hardcodes only two of them
(AD5413_RANGE_PLUSMINUS_10_5V = 0x3 and AD5413_RANGE_0MA_24MA = 0x9) based on
this boolean.
Could this use output-range-microvolt and output-range-microamp from
Documentation/devicetree/bindings/iio/dac/dac.yaml (or adi,range-microvolt and
adi,range-microamp as in Documentation/devicetree/bindings/iio/dac/adi,ad5758.yaml)
so the binding describes the hardware capabilities rather than the current
driver limitation?
> +
> + adi,slew-time-us:
> + description: Output digital slew control time in microseconds
> + minimum: 0
> + maximum: 1000000
> + default: 0
> +
> +required:
> + - compatible
> + - spi-cpha
> + - avdd-supply
> + - dvdd-supply
> + - avss-supply
[Severity: Medium]
Should reg be defined with maxItems: 1 under properties and listed in
required?
The binding references /schemas/spi/spi-peripheral-props.yaml# under allOf,
which defines reg with minItems: 1 and maxItems: 256 and does not mark reg
as required. Because AD5413 is a single-chip-select SPI device and both
examples specify reg = <0>, omitting reg from properties and required leaves
the chip-select count unconstrained and optional during DT validation.
> +
> +allOf:
> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + dac@0 {
> + compatible = "adi,ad5413";
> + reg = <0>;
> + spi-max-frequency = <1000000>;
> + spi-cpha;
> + reset-gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
> + avdd-supply = <&avdd>;
> + dvdd-supply = <&dvdd>;
> + avss-supply = <&avss>;
> + adi,slew-time-us = <125>;
> + };
> + };
> +
> + - |
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + dac@0 {
> + compatible = "adi,ad5413";
> + reg = <0>;
> + spi-max-frequency = <1000000>;
> + spi-cpha;
> + avdd-supply = <&avdd>;
> + dvdd-supply = <&dvdd>;
> + avss-supply = <&avss>;
> +
> + adi,chan-current-mode;
> + };
> + };
In both examples above, could the standard properties (avdd-supply,
avss-supply, dvdd-supply, reset-gpios, spi-cpha, spi-max-frequency) be placed
in canonical natural-sort order after reg per
Documentation/devicetree/bindings/dts-coding-style.rst?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261017-ad5413-upstream-v1-0-61f40c8fef25@analog.com?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC
2026-10-16 19:23 ` [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC Bruce Tsao via B4 Relay
2026-10-16 19:36 ` sashiko-bot
@ 2026-10-16 23:02 ` Jonathan Cameron
1 sibling, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2026-10-16 23:02 UTC (permalink / raw)
To: Bruce Tsao via B4 Relay
Cc: bruce.tsao, linux-iio, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree, linux-kernel
On Sat, 17 Oct 2026 03:23:53 +0800
Bruce Tsao via B4 Relay <devnull+bruce.tsao.analog.com@kernel.org> wrote:
> From: Bruce Tsao <bruce.tsao@analog.com>
>
> Document the devicetree bindings for the Analog Devices AD5413, a
> single-channel precision DAC controlled via SPI. The device supports
> voltage or current output modes and programmable slew-rate control.
>
> Signed-off-by: Bruce Tsao <bruce.tsao@analog.com>
Hi Bruce,
Welcome to IIO.
> ---
> .../devicetree/bindings/iio/dac/adi,ad5413.yaml | 101 +++++++++++++++++++++
> 1 file changed, 101 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml
> new file mode 100644
> index 000000000000..74e869304aab
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5413.yaml
> @@ -0,0 +1,101 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/dac/adi,ad5413.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Analog Devices AD5413 DAC
> +
> +description:
> + Analog Devices AD5413 is a single-channel precision DAC supporting voltage
> + or current output modes with programmable slew-rate control.
> + The output mode is selected via the adi,chan-current-mode property.
> + Optional reset GPIO may be provided.
New line per sentence isn't really adding anything. I'd reflow this
a a paragraph.
> +
> +maintainers:
> + - bruce.tsao@analog.com
> +
> +properties:
> + compatible:
> + const: adi,ad5413
> +
> + spi-max-frequency:
> + maximum: 50000000
> +
> + spi-cpha: true
> +
> + spi-cpol: false
> +
> + reset-gpios:
> + maxItems: 1
> + description: Optional reset GPIO (active low).
From a quick glance looks like we also have LDAC SYNC and FAULT.
+ a reference out that probably wants to be modeled as a supply.
Upshot is this looks like an incomplete binding and generally
we try to make them as complete as possible from the start, even
if the driver doesn't yet use everything.
> +
> + avdd-supply:
> + description: Analog positive supply (AVDD)
> +
> + dvdd-supply:
> + description: Digital positive supply (DVDD)
> +
> + avss-supply:
> + description: Analog negative supply (AVSS)
> +
> + adi,chan-current-mode:
> + type: boolean
> + description: Select current output mode. If absent, voltage mode is used.
We have some existing dt bindings for multi mode channels on adi
parts. See adi,ch-func in various other bindings.
Whilst we've had various discussions about generalizing that to
a generic dac binding so far we haven't done so. Maybe now is the time...
> +
> + adi,slew-time-us:
> + description: Output digital slew control time in microseconds
> + minimum: 0
> + maximum: 1000000
> + default: 0
> +
> +required:
> + - compatible
> + - spi-cpha
> + - avdd-supply
> + - dvdd-supply
> + - avss-supply
> +
> +allOf:
> + - $ref: /schemas/spi/spi-peripheral-props.yaml#
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + dac@0 {
> + compatible = "adi,ad5413";
> + reg = <0>;
> + spi-max-frequency = <1000000>;
> + spi-cpha;
> + reset-gpios = <&gpio 0 GPIO_ACTIVE_LOW>;
> + avdd-supply = <&avdd>;
> + dvdd-supply = <&dvdd>;
> + avss-supply = <&avss>;
> + adi,slew-time-us = <125>;
> + };
> + };
> +
> + - |
> + spi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + dac@0 {
> + compatible = "adi,ad5413";
> + reg = <0>;
> + spi-max-frequency = <1000000>;
> + spi-cpha;
> + avdd-supply = <&avdd>;
> + dvdd-supply = <&dvdd>;
> + avss-supply = <&avss>;
> +
> + adi,chan-current-mode;
> + };
> + };
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] iio: dac: add AD5413 driver
2026-10-16 19:23 ` [PATCH 2/2] iio: dac: add AD5413 driver Bruce Tsao via B4 Relay
2026-10-16 19:33 ` sashiko-bot
@ 2026-10-16 23:40 ` Jonathan Cameron
1 sibling, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2026-10-16 23:40 UTC (permalink / raw)
To: Bruce Tsao via B4 Relay
Cc: bruce.tsao, linux-iio, David Lechner, Nuno Sá,
Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
devicetree, linux-kernel
On Sat, 17 Oct 2026 03:23:54 +0800
Bruce Tsao via B4 Relay <devnull+bruce.tsao.analog.com@kernel.org> wrote:
> From: Bruce Tsao <bruce.tsao@analog.com>
>
> The AD5413 is a single-channel, 14-bit, voltage and current output
> digital-to-analog converter (DAC) with programmable output ranges,
> slew rate control and on-chip diagnostics.
>
> Add an IIO driver supporting the +-10.5 V voltage range and the
> 0-24 mA current range, output enable, slew rate control and device
> reset over the SPI interface.
>
> The device powers up with the SPI CRC enabled, so every frame carries
> a CRC-8 byte and the interface is configured for SPI mode 1. This lets
> the driver bring the device up directly from a cold boot without any
> pre-conditioning.
>
> Signed-off-by: Bruce Tsao <bruce.tsao@analog.com>
Hi Bruce
Good state for a v1. Main thing in my feedback is take
another look at the endian conversions. It think those
have evolved into being way more complex than they need to be.
Jonathan
> diff --git a/drivers/iio/dac/ad5413.c b/drivers/iio/dac/ad5413.c
> new file mode 100644
> index 000000000000..96cce32fc640
> --- /dev/null
> +++ b/drivers/iio/dac/ad5413.c
> @@ -0,0 +1,666 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * AD5413 Digital to analog converters driver
> + *
> + * Copyright 2026 Analog Devices Inc.
> + */
> +#include <linux/bitfield.h>
> +#include <linux/crc8.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iopoll.h>
> +#include <linux/math64.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/spi/spi.h>
> +#include <linux/units.h>
> +#include <linux/util_macros.h>
> +
> +/* AD5413 registers definition */
> +#define AD5413_REG_NOP 0x00
> +#define AD5413_REG_DAC_INPUT 0x01
> +#define AD5413_REG_DAC_OUTPUT 0x02
> +#define AD5413_REG_CLEAR_CODE 0x03
> +#define AD5413_REG_USER_GAIN 0x04
> +#define AD5413_REG_USER_OFFSET 0x05
> +#define AD5413_REG_DAC_CONFIG 0x06
> +#define AD5413_REG_DAC_CONFIG_SR_STEP_MSK GENMASK(15, 13)
Mixing fields and register defines can make for hard reading. I sugest
using a bit of indenting to make them visually different.
#define AD5413_REG_DAC_CONFIG 0x06
#define AD5413_REG_DAC_CONFIG_SR_STEP_MSK GENMASK(15, 13)
#define AD5413_REG_DAC_CONFIG_SR_CLOCK_MSK GENMASK(12, 9)
#define AD5413_REG_DAC_CONFIG_SR_EN_MSK BIT(8)
#define AD5413_REG_DAC_CONFIG_RSET_EXT_EN_MSK BIT(7)
#define AD5413_REG_DAC_CONFIG_OUT_EN_MSK BIT(6)
#define AD5413_REG_DAC_CONFIG_INT_EN_MSK BIT(5)
#define AD5413_REG_DAC_CONFIG_OVRNG_EN_MSK BIT(4)
#define AD5413_REG_DAC_CONFIG_RANGE_MSK GENMASK(3, 0)
define AD5413_REG_SW_LDAC 0x07
etc. If you have values of fields defined, then indent them
even further.
> +#define AD5413_REG_DAC_CONFIG_SR_CLOCK_MSK GENMASK(12, 9)
> +#define AD5413_REG_DAC_CONFIG_SR_EN_MSK BIT(8)
> +#define AD5413_REG_DAC_CONFIG_RSET_EXT_EN_MSK BIT(7)
> +#define AD5413_REG_DAC_CONFIG_OUT_EN_MSK BIT(6)
> +#define AD5413_REG_DAC_CONFIG_INT_EN_MSK BIT(5)
> +#define AD5413_REG_DAC_CONFIG_OVRNG_EN_MSK BIT(4)
> +#define AD5413_REG_DAC_CONFIG_RANGE_MSK GENMASK(3, 0)
> +#define AD5413_REG_SW_LDAC 0x07
> +#define AD5413_REG_KEY 0x08
> +#define AD5413_REG_KEY_REG_ADDR_MASK GENMASK(20, 16)
> +#define AD5413_REG_KEY_CODE_MASK GENMASK(15, 0)
> +#define AD5413_REG_GP_CONFIG1 0x09
> +#define AD5413_REG_GP_CONFIG2 0x0A
> +#define AD5413_REG_DIGITAL_DIAG_CONFIG 0x10
> +#define AD5413_REG_FAULT_PIN_CONFIG 0x12
> +#define AD5413_REG_TWO_STAGE_READBACK_SELECT 0x13
> +#define AD5413_REG_DIGITAL_DIAG_RESULTS 0x14
> +#define AD5413_REG_DIGITAL_DIAG_CAL_MEM_UNREFRESHED_MSK BIT(15)
> +#define AD5413_REG_DIGITAL_DIAG_SLEW_BUSY_MSK BIT(14)
> +#define AD5413_REG_ANALOG_DIAG_RESULTS 0x15
> +#define AD5413_REG_STATUS 0x16
> +#define AD5413_REG_CHIP_ID 0x17
> +#define AD5413_REG_FREQ_MONITOR 0x18
> +#define AD5413_REG_DEVICE_ID_3 0x1C
> +
> +/* Special Key Codes (bits[15:0]) */
> +#define AD5413_REG_KEY_CODE_RESET_1 0x15FA
> +#define AD5413_REG_KEY_CODE_RESET_2 0xAF51
> +#define AD5413_REG_KEY_CODE_CALIB_MEM_REFRESH 0xFCBA
> +
> +/* DIGITAL_DIAG_RESULTS Register (Addr = 0x14) Busy Flags */
> +
> +#define AD5413_WR_FLAG_MSK(x) (0x80u | ((x) & 0x1F))
> +
> +/* CRC-8 polynomial x^8 + x^2 + x + 1, enabled by default on the device */
> +#define AD5413_CRC8_POLY 0x07
> +
> +#define AD5413_FULL_SCALE_MICRO (16383ULL * MICRO)
> +
> +struct ad5413_range {
> + int reg;
> + int min;
> + int max;
> +};
> +
> +struct ad5413_state {
> + struct spi_device *spi;
> + struct regulator_bulk_data supplies[3];
> + struct mutex lock; /* Protects all register access via SPI */
Not necessary to protect data accesses in general as spi will do that.
The point you want to make is about rmw sequences.
> + const struct ad5413_range *out_range;
> + unsigned int slew_time;
> + bool pwr_down;
> + u8 crc8_tbl[CRC8_TABLE_SIZE];
> + __be32 d32[3] __aligned(IIO_DMA_MINALIGN);
As below. I'd throw in a d8 array to simplify the data mangling.
> +};
> +
> +/*
> + * Output ranges corresponding to DAC_CONFIG[3:0] bits.
> + * Defined by the following symbolic identifiers:
> + * AD5413_RANGE_PLUSMINUS_10_5V - ±10.5 V voltage range
> + * AD5413_RANGE_0MA_24MA - 0 mA to 24 mA current range
> + */
> +enum ad5413_output_range {
> + AD5413_RANGE_PLUSMINUS_10_5V = 0x3,
> + AD5413_RANGE_0MA_24MA = 0x9,
> +};
> +
> +static const struct ad5413_range ad5413_voltage_range[] = { {
Given only one element, why the array?
> + .reg = AD5413_RANGE_PLUSMINUS_10_5V,
> + .min = -10500000,
> + .max = 10500000,
> +} };
> +
> +static const struct ad5413_range ad5413_current_range[] = { {
Same on this one.
> + .reg = AD5413_RANGE_0MA_24MA,
> + .min = 0,
> + .max = 24000,
> +} };
> +static int ad5413_spi_reg_read(struct ad5413_state *st, unsigned int addr,
> + u16 *val)
> +{
> + struct spi_transfer t[] = {
> + {
> + .tx_buf = &st->d32[0],
> + .len = 4,
> + .cs_change = 1,
> + },
> + {
> + .tx_buf = &st->d32[1],
> + .rx_buf = &st->d32[2],
> + .len = 4,
> + },
> + };
> + u8 sel[3], nop[3];
> + int ret;
> +
> + /* Two-stage readback: select the register to read back. */
> + sel[0] = AD5413_WR_FLAG_MSK(AD5413_REG_TWO_STAGE_READBACK_SELECT);
> + sel[1] = 0;
> + sel[2] = addr;
> + st->d32[0] = cpu_to_be32((sel[0] << 24) | (sel[1] << 16) |
> + (sel[2] << 8) |
> + crc8(st->crc8_tbl, sel, sizeof(sel), 0));
See below. I suspect similarly overly complicated endian dance.
> +
> + /* NOP frame clocks the selected register contents out on SDO. */
> + nop[0] = AD5413_WR_FLAG_MSK(AD5413_REG_NOP);
> + nop[1] = 0;
> + nop[2] = 0;
> + st->d32[1] = cpu_to_be32((nop[0] << 24) | (nop[1] << 16) |
> + (nop[2] << 8) |
> + crc8(st->crc8_tbl, nop, sizeof(nop), 0));
> +
> + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
> + if (ret < 0)
> + return ret;
> +
> + /*
> + * The readback CRC covers the live FAULT pin status bit, which can
> + * toggle between capture and clock-out, so it is not validated here.
> + */
> + *val = be32_to_cpu(st->d32[2]) >> 8;
> +
> + return 0;
> +}
> +
> +static int ad5413_spi_reg_write(struct ad5413_state *st, unsigned int addr,
> + unsigned int val)
> +{
> + u8 buf[3] = { };
u8 buf[3] = {
[0] = AD...
> +
> + buf[0] = AD5413_WR_FLAG_MSK(addr);
> + buf[1] = val >> 8;
> + buf[2] = val;
This is fun as that's a put_unaligned_be16 which you then do
another endian swap on.
> +
> + st->d32[0] = cpu_to_be32((buf[0] << 24) | (buf[1] << 16) |
> + (buf[2] << 8) |
Ok. I'm lost.
In both cass we start with
buf[0] = AD5413_WR_FLAG_MSK(addr),
buf[1] = (val >> 8) & 0xff;
buf[2] = val & 0xff;
Then you build a native endian integer. So on a little endian cpu that means
v[0] = crc8(buf..)
v[1] = val & 0xff;
v[2] = (val >> 8) & 0xff;
v[3] = AD5413_WR_FLAG_MSK(addr)
d32 is that swapped (pretend it's u8 array)
d32[0] = AD5413_WR_FLAG_MSK(addr);
d32[1] = (val >> 8) & 0xff;
d32[2] = val & 0xff;
d32[3] = crc8(buf..)
on a big endian
v[0] = AD5413_WR_FLAG_MSK(addr)
v[1] = (val >> 8) & 0xff;
v[2] = val & 0xff;
v[3] = crc8(buf..)
d32 is not swapped. (again pretend u8 array
d32[0] = AD5413_WR_FLAG_MSK(addr);
d32[1] = (val >> 8) & 0xff;
d32[2] = val & 0xff;
d32[3] = crc8(buf..)
(snap which is good).
Fun bit, is the 3 bytes are right for computing the crc8 so... This is all
equivalent of:
static int ad5413_spi_reg_write(struct ad5413_state *st, unsigned int addr,
unsigned int val)
{
st->d8[0] = AD5413_WR_FLAG_MSK(addr);
put_unaligned_be16(&st->d8[1], val);
st->d8[3] = crc8(st->crc8_tbl, buf, 3, 0);
return spi_write(st->spi, st->d8, sizeof(st->d8));
}
As long as you have a st->d8[4] array to use (and I didn't unwind
that all incorrectly).
> + crc8(st->crc8_tbl, buf, sizeof(buf), 0));
> +
> + return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0]));
> +}
> +
> +static int ad5413_spi_write_mask(struct ad5413_state *st, unsigned int addr,
> + unsigned long mask, unsigned int val)
> +{
> + u16 regval;
> + int ret;
> +
> + ret = ad5413_spi_reg_read(st, addr, ®val);
> + if (ret)
> + return ret;
> +
> + return ad5413_spi_reg_write(st, addr, (regval & ~mask) | val);
> +}
> +
> +static int ad5413_slew_rate_config(struct ad5413_state *st)
> +{
> + unsigned int sr_clk_idx = 0, sr_step_idx = 0;
> + int i, res;
> + s64 diff_new, diff_old = S64_MAX;
> + u64 sr_step, calc_slew_time;
> +
> + /*
> + * The slew time can be determined by using the formula:
> + * Slew Time = (Full Scale Out / (Step Size x Update Clk Freq))
> + * where Slew time is expressed in microseconds
> + * Given the desired slew time, the following algorithm determines the
> + * best match for the step size and the update clock frequency.
> + */
> + for (i = 0; i < ARRAY_SIZE(ad5413_sr_clk); i++) {
for (int i = 0;
is acceptable these days and makes the scope explicit.
> + /*
> + * Go through each valid update clock freq and determine a raw
> + * value for the step size by using the formula:
> + * Step Size = Full Scale Out / (Update Clk Freq * Slew Time)
> + */
> + sr_step = div64_u64(AD5413_FULL_SCALE_MICRO,
> + (u64)ad5413_sr_clk[i] * st->slew_time);
> + /*
> + * After a raw value for step size was determined, find the
> + * closest valid match
> + */
> + res = find_closest(sr_step, ad5413_sr_step, ARRAY_SIZE(ad5413_sr_step));
> +
> + /* Calculate the slew time */
> + calc_slew_time = div_u64(AD5413_FULL_SCALE_MICRO,
> + ad5413_sr_step[res] * ad5413_sr_clk[i]);
> + /*
> + * Determine with how many microseconds the calculated slew time
> + * is different from the desired slew time and store the diff
> + * for the next iteration
> + */
> + diff_new = abs(st->slew_time - calc_slew_time);
> + if (diff_new < diff_old) {
> + diff_old = diff_new;
> + sr_clk_idx = i;
> + sr_step_idx = res;
> + }
> + }
> +
> + return ad5413_slew_rate_set(st, sr_clk_idx, sr_step_idx);
> +}
> +
> +static int ad5413_internal_buffers_en(struct ad5413_state *st, bool enable)
> +{
> + int ret;
> +
> + ret = ad5413_spi_write_mask(st,
> + AD5413_REG_DAC_CONFIG,
> + AD5413_REG_DAC_CONFIG_INT_EN_MSK,
> + FIELD_PREP(AD5413_REG_DAC_CONFIG_INT_EN_MSK,
> + enable));
> + if (ret < 0)
> + return ret;
I'd not expect a function with that name to return > 0 so perhaps
just if (ret)?
> +
> + /* Wait to allow time for the internal calibrations to complete */
The function name and parameters are probably clear enough that you can
drop the comment.
> + return ad5413_wait_for_task_complete(st,
> + AD5413_REG_DIGITAL_DIAG_RESULTS,
> + AD5413_REG_DIGITAL_DIAG_CAL_MEM_UNREFRESHED_MSK);
> +}
> +static int ad5413_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long info)
> +{
> + struct ad5413_state *st = iio_priv(indio_dev);
> + int max, min, ret;
> + u16 regval;
> +
> + switch (info) {
> + case IIO_CHAN_INFO_RAW: {
> + guard(mutex)(&st->lock);
> + ret = ad5413_spi_reg_read(st, AD5413_REG_DAC_INPUT, ®val);
> + if (ret)
> + return ret;
> +
> + /* 14-bit DAC data is in bits [15:2] of the register. */
> + *val = regval >> 2;
> + return IIO_VAL_INT;
> + }
> + case IIO_CHAN_INFO_SCALE:
> + min = st->out_range->min;
> + max = st->out_range->max;
> + *val = (max - min) / 1000;
> + *val2 = 14;
> + return IIO_VAL_FRACTIONAL_LOG2;
> + case IIO_CHAN_INFO_OFFSET:
> + min = st->out_range->min;
> + max = st->out_range->max;
> + *val = div_s64((s64)min * BIT(14), max - min);
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int ad5413_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val, int val2,
> + long info)
> +{
> + struct ad5413_state *st = iio_priv(indio_dev);
> + int ret;
> +
> + switch (info) {
> + case IIO_CHAN_INFO_RAW: {
> + guard(mutex)(&st->lock);
> + /* 14-bit DAC data is in bits [15:2] of the register. */
> + ret = ad5413_spi_reg_write(st, AD5413_REG_DAC_INPUT, val << 2);
> + return ret;
return ad54...
> + }
> + default:
> + return -EINVAL;
> + }
> +}
> +static ssize_t ad5413_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
> + struct iio_chan_spec const *chan,
> + const char *buf, size_t len)
> +{
> + struct ad5413_state *st = iio_priv(indio_dev);
> + bool pwr_down;
> + unsigned int dac_config_mode, val;
> + unsigned long dac_config_msk;
Where nothing else dictates ordering, reverse xmas tree preferred.
> + int ret;
> +
> + ret = kstrtobool(buf, &pwr_down);
> + if (ret)
> + return ret;
> +
> + val = pwr_down ? 0 : 1;
> +
> + dac_config_mode = FIELD_PREP(AD5413_REG_DAC_CONFIG_OUT_EN_MSK, (val)) |
> + FIELD_PREP(AD5413_REG_DAC_CONFIG_INT_EN_MSK, (val));
> + dac_config_msk = AD5413_REG_DAC_CONFIG_OUT_EN_MSK |
> + AD5413_REG_DAC_CONFIG_INT_EN_MSK;
> +
> + guard(mutex)(&st->lock);
> + ret = ad5413_spi_write_mask(st, AD5413_REG_DAC_CONFIG, dac_config_msk,
> + dac_config_mode);
> + if (ret < 0)
> + return ret;
> +
> + st->pwr_down = pwr_down;
> +
> + return len;
> +}
> +static const struct iio_chan_spec ad5413_voltage_ch[] = {
> + AD5413_DAC_CHAN(IIO_VOLTAGE),
> +};
> +
> +static const struct iio_chan_spec ad5413_current_ch[] = {
> + AD5413_DAC_CHAN(IIO_CURRENT),
Why an array? For these, given you are just copying them in place
maybe use a macro that has (struct iio_chan_spec) { values }
so that you can just have the assignment in code without the
need for these two template arrays.
> +};
> +
> +static int ad5413_parse_fw(struct ad5413_state *st)
> +{
> + struct device *dev = &st->spi->dev;
> + int ret;
> +
> + if (device_property_read_bool(dev, "adi,chan-current-mode"))
> + st->out_range = &ad5413_current_range[0];
I ask this above, but it was motivated by this. If there is only
one structure why do we want an array for these?
> + else
> + st->out_range = &ad5413_voltage_range[0];
> +
> + st->slew_time = 0;
> + if (device_property_present(dev, "adi,slew-time-us")) {
> + ret = device_property_read_u32(dev,
> + "adi,slew-time-us",
> + &st->slew_time);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to read adi,slew-time-us\n");
> +
> + if (st->slew_time > 1 * MICRO)
> + return dev_err_probe(dev, -EINVAL, "adi,slew-time-us out of range\n");
> + }
> +
> + return 0;
> +}
> +
> +static int ad5413_init(struct ad5413_state *st)
> +{
> + struct reset_control *reset;
> + u16 regval;
> + int ret;
> +
> + reset = devm_reset_control_get_optional_exclusive(&st->spi->dev, NULL);
> + if (IS_ERR(reset))
> + return PTR_ERR(reset);
> +
> + if (reset) {
> + /* Hard reset via the reset controller */
> + ret = reset_control_assert(reset);
> + if (ret)
> + return ret;
> +
> + fsleep(100);
For most sleeps a comment that refers to the datasheet is useful
so if people hit problems in future they understand why the sleeps take
particular values. If the datasheet is uninformative on a particular
value just say something about having found it to work in practice.
> +
> + ret = reset_control_deassert(reset);
> + if (ret)
> + return ret;
> +
> + fsleep(100);
> + } else {
> + /*
> + * CRC is enabled by default after power-on/reset. The SPI
> + * helpers always append the CRC byte, so the device accepts
> + * these soft-reset key frames without any bootstrap step.
> + */
> + ret = ad5413_spi_reg_write(st, AD5413_REG_KEY,
> + AD5413_REG_KEY_CODE_RESET_1);
> + if (ret)
> + return ret;
> +
> + ret = ad5413_spi_reg_write(st, AD5413_REG_KEY,
> + AD5413_REG_KEY_CODE_RESET_2);
> + if (ret)
> + return ret;
> +
> + fsleep(100);
> + }
> +
> + ret = ad5413_calib_mem_refresh(st);
> + if (ret < 0)
> + return ret;
> +
> + ret = ad5413_spi_reg_read(st, AD5413_REG_DIGITAL_DIAG_RESULTS, ®val);
> + if (ret)
> + return ret;
> +
> + ret = ad5413_spi_reg_write(st, AD5413_REG_DIGITAL_DIAG_RESULTS, regval);
Whilst it doesn't matter in practice, I'd rather this only wrote the write 1 to clear
bits rather than also writing the read only ones up the top. Acts as documentation
about what this is doing.
> + if (ret < 0)
> + return ret;
> +
> + ret = ad5413_set_out_range(st, st->out_range->reg);
> + if (ret < 0)
> + return ret;
> +
> + if (st->slew_time) {
> + ret = ad5413_slew_rate_config(st);
> + if (ret < 0)
> + return ret;
> + }
> +
> + ret = ad5413_internal_buffers_en(st, 1);
> + if (ret < 0)
> + return ret;
> +
> + return ad5413_spi_write_mask(st,
> + AD5413_REG_DAC_CONFIG,
> + AD5413_REG_DAC_CONFIG_OUT_EN_MSK,
> + FIELD_PREP(AD5413_REG_DAC_CONFIG_OUT_EN_MSK, 1));
> +}
}
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-16 23:40 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-16 19:23 [PATCH 0/2] iio: dac: add support for AD5413 Bruce Tsao via B4 Relay
2026-10-16 19:23 ` [PATCH 1/2] dt-bindings: iio: dac: Document Analog Devices AD5413 single-channel DAC Bruce Tsao via B4 Relay
2026-10-16 19:36 ` sashiko-bot
2026-10-16 23:02 ` Jonathan Cameron
2026-10-16 19:23 ` [PATCH 2/2] iio: dac: add AD5413 driver Bruce Tsao via B4 Relay
2026-10-16 19:33 ` sashiko-bot
2026-10-16 23:40 ` Jonathan Cameron
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox