From 030a8b183089dbb5ee7f0dd7d40b3186fde46a1f Mon Sep 17 00:00:00 2001 From: Jan Kubiznak Date: Wed, 10 Apr 2024 13:32:02 +0200 Subject: [PATCH] drivers: dac: dac_ad569x: Support for AD569x DACs. Added support for Analog Devices AD5691 / AD5692 / AD5693 DACs. Signed-off-by: Jan Kubiznak --- drivers/dac/CMakeLists.txt | 1 + drivers/dac/Kconfig | 2 + drivers/dac/Kconfig.ad569x | 10 ++ drivers/dac/dac_ad569x.c | 192 ++++++++++++++++++++++++ dts/bindings/dac/adi,ad5691.yaml | 8 + dts/bindings/dac/adi,ad5692.yaml | 8 + dts/bindings/dac/adi,ad5693.yaml | 8 + dts/bindings/dac/adi,ad569x-base.yaml | 61 ++++++++ tests/drivers/build_all/dac/app.overlay | 27 ++++ 9 files changed, 317 insertions(+) create mode 100644 drivers/dac/Kconfig.ad569x create mode 100644 drivers/dac/dac_ad569x.c create mode 100644 dts/bindings/dac/adi,ad5691.yaml create mode 100644 dts/bindings/dac/adi,ad5692.yaml create mode 100644 dts/bindings/dac/adi,ad5693.yaml create mode 100644 dts/bindings/dac/adi,ad569x-base.yaml diff --git a/drivers/dac/CMakeLists.txt b/drivers/dac/CMakeLists.txt index cf71fff980..50cd660ac5 100644 --- a/drivers/dac/CMakeLists.txt +++ b/drivers/dac/CMakeLists.txt @@ -21,4 +21,5 @@ zephyr_library_sources_ifdef(CONFIG_DAC_GD32 dac_gd32.c) zephyr_library_sources_ifdef(CONFIG_DAC_ESP32 dac_esp32.c) zephyr_library_sources_ifdef(CONFIG_DAC_AD559X dac_ad559x.c) zephyr_library_sources_ifdef(CONFIG_DAC_AD56XX dac_ad56xx.c) +zephyr_library_sources_ifdef(CONFIG_DAC_AD569X dac_ad569x.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE dac_handlers.c) diff --git a/drivers/dac/Kconfig b/drivers/dac/Kconfig index 4f1d95d548..08680b9640 100644 --- a/drivers/dac/Kconfig +++ b/drivers/dac/Kconfig @@ -57,4 +57,6 @@ source "drivers/dac/Kconfig.ad56xx" source "drivers/dac/Kconfig.ad559x" +source "drivers/dac/Kconfig.ad569x" + endif # DAC diff --git a/drivers/dac/Kconfig.ad569x b/drivers/dac/Kconfig.ad569x new file mode 100644 index 0000000000..e40e8fc8e3 --- /dev/null +++ b/drivers/dac/Kconfig.ad569x @@ -0,0 +1,10 @@ +# Copyright (c) 2024 Jan Kubiznak +# SPDX -License-Identifier: Apache-2.0 + +config DAC_AD569X + bool "Analog Devices AD5691 / AD5692 / AD5693 DAC driver" + default y + select I2C + depends on DT_HAS_ADI_AD5691_ENABLED || DT_HAS_ADI_AD5692_ENABLED || DT_HAS_ADI_AD5693_ENABLED + help + Enable the driver for the Analog Devices AD569X. diff --git a/drivers/dac/dac_ad569x.c b/drivers/dac/dac_ad569x.c new file mode 100644 index 0000000000..13b7d705b3 --- /dev/null +++ b/drivers/dac/dac_ad569x.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2024 Jan Kubiznak + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(dac_ad569x, CONFIG_DAC_LOG_LEVEL); + +#define AD569X_CTRL_GAIN(x) FIELD_PREP(BIT(11), x) +#define AD569X_CTRL_REF(x) FIELD_PREP(BIT(12), x) +#define AD569X_CTRL_PD(x) FIELD_PREP(BIT_MASK(2) << 13, x) +#define AD569X_CTRL_RESET(x) FIELD_PREP(BIT(15), x) + +#define AD569X_CMD_WRITE 0x10 +#define AD569X_CMD_UPDATE 0x20 +#define AD569X_CMD_WRITE_AND_UPDATE 0x30 +#define AD569X_CMD_CONFIGURE 0x40 + +#define AD569X_CTRL_NO_RESET 0x00 +#define AD569X_CTRL_PERFORM_RESET 0x01 + +struct ad569x_config { + struct i2c_dt_spec bus; + uint8_t resolution; + uint8_t gain; + uint8_t voltage_reference; + uint8_t power_down_mode; +}; + +static int ad569x_write(const struct device *dev, uint8_t command, uint16_t value) +{ + const struct ad569x_config *config = dev->config; + + uint8_t tx_data[3]; + + tx_data[0] = command; + sys_put_be16(value, tx_data + 1); + + return i2c_write_dt(&config->bus, tx_data, sizeof(tx_data)); +} + +static int ad569x_read(const struct device *dev, uint16_t *value) +{ + const struct ad569x_config *config = dev->config; + + uint8_t rx_data[2]; + int ret; + + ret = i2c_read_dt(&config->bus, rx_data, sizeof(rx_data)); + if (ret != 0) { + return ret; + } + + *value = sys_get_be16(rx_data); + + return ret; +} + +static int ad569x_channel_setup(const struct device *dev, const struct dac_channel_cfg *channel_cfg) +{ + const struct ad569x_config *config = dev->config; + + if (channel_cfg->channel_id > 0) { + LOG_ERR("invalid channel %d", channel_cfg->channel_id); + return -EINVAL; + } + + if (channel_cfg->resolution != config->resolution) { + LOG_ERR("invalid resolution %d", channel_cfg->resolution); + return -EINVAL; + } + + return 0; +} + +static int ad569x_sw_reset(const struct device *dev) +{ + uint16_t reg = AD569X_CTRL_RESET(AD569X_CTRL_PERFORM_RESET); + int ret; + + LOG_DBG("reset %s", dev->name); + + /* Ignore return value, since device gives NAK after receiving RESET request */ + ad569x_write(dev, AD569X_CMD_CONFIGURE, reg); + + /* Check that DAC output is reset */ + ret = ad569x_read(dev, ®); + if (ret != 0) { + LOG_ERR("failed to read value"); + return ret; + } + + if (reg != 0) { + LOG_ERR("failed to reset DAC output"); + ret = -EIO; + } + + return 0; +} + +static int ad569x_write_value(const struct device *dev, uint8_t channel, uint32_t value) +{ + const struct ad569x_config *config = dev->config; + + if (channel > 0) { + LOG_ERR("invalid channel %d", channel); + return -EINVAL; + } + + if (value > (BIT(config->resolution) - 1)) { + LOG_ERR("invalid value %d", value); + return -EINVAL; + } + + return ad569x_write(dev, AD569X_CMD_WRITE_AND_UPDATE, value); +} + +static int ad569x_init(const struct device *dev) +{ + const struct ad569x_config *config = dev->config; + int ret; + + if (!i2c_is_ready_dt(&config->bus)) { + return -ENODEV; + } + + ret = ad569x_sw_reset(dev); + if (ret != 0) { + LOG_ERR("failed to perform sw reset"); + return ret; + } + + LOG_DBG("configure %s: gain %d, voltage reference %d, power down mode %d", dev->name, + config->gain, config->voltage_reference, config->power_down_mode); + + uint16_t ctrl_reg = AD569X_CTRL_GAIN(config->gain) | + AD569X_CTRL_REF(config->voltage_reference) | + AD569X_CTRL_PD(config->power_down_mode); + + ret = ad569x_write(dev, AD569X_CMD_CONFIGURE, ctrl_reg); + if (ret != 0) { + LOG_ERR("failed to configure the device"); + return ret; + } + + return 0; +} + +static const struct dac_driver_api ad569x_driver_api = { + .channel_setup = ad569x_channel_setup, + .write_value = ad569x_write_value, +}; + +#define INST_DT_AD569X(index, name, res) \ + static const struct ad569x_config config_##name##_##index = { \ + .bus = I2C_DT_SPEC_INST_GET(index), \ + .resolution = res, \ + .gain = DT_INST_ENUM_IDX(index, gain), \ + .voltage_reference = DT_INST_ENUM_IDX(index, voltage_reference), \ + .power_down_mode = DT_INST_ENUM_IDX(index, power_down_mode), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(index, ad569x_init, NULL, NULL, &config_##name##_##index, \ + POST_KERNEL, CONFIG_DAC_INIT_PRIORITY, &ad569x_driver_api); + +#define DT_DRV_COMPAT adi_ad5691 +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#define DAC_AD5691_RESOLUTION 12 +DT_INST_FOREACH_STATUS_OKAY_VARGS(INST_DT_AD569X, DT_DRV_COMPAT, DAC_AD5691_RESOLUTION) +#endif +#undef DT_DRV_COMPAT + +#define DT_DRV_COMPAT adi_ad5692 +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#define DAC_AD5692_RESOLUTION 14 +DT_INST_FOREACH_STATUS_OKAY_VARGS(INST_DT_AD569X, DT_DRV_COMPAT, DAC_AD5692_RESOLUTION) +#endif +#undef DT_DRV_COMPAT + +#define DT_DRV_COMPAT adi_ad5693 +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) +#define DAC_AD5693_RESOLUTION 16 +DT_INST_FOREACH_STATUS_OKAY_VARGS(INST_DT_AD569X, DT_DRV_COMPAT, DAC_AD5693_RESOLUTION) +#endif +#undef DT_DRV_COMPAT diff --git a/dts/bindings/dac/adi,ad5691.yaml b/dts/bindings/dac/adi,ad5691.yaml new file mode 100644 index 0000000000..11cc67fbd4 --- /dev/null +++ b/dts/bindings/dac/adi,ad5691.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Jan Kubiznak +# SPDX-License-Identifier: Apache-2.0 + +description: Driver for AD5691 (12-bit) DAC. + +compatible: "adi,ad5691" + +include: adi,ad569x-base.yaml diff --git a/dts/bindings/dac/adi,ad5692.yaml b/dts/bindings/dac/adi,ad5692.yaml new file mode 100644 index 0000000000..46ef4d624d --- /dev/null +++ b/dts/bindings/dac/adi,ad5692.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Jan Kubiznak +# SPDX-License-Identifier: Apache-2.0 + +description: Driver for AD5692 (14-bit) DAC. + +compatible: "adi,ad5692" + +include: adi,ad569x-base.yaml diff --git a/dts/bindings/dac/adi,ad5693.yaml b/dts/bindings/dac/adi,ad5693.yaml new file mode 100644 index 0000000000..7d92552485 --- /dev/null +++ b/dts/bindings/dac/adi,ad5693.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Jan Kubiznak +# SPDX-License-Identifier: Apache-2.0 + +description: Driver for AD5693 (16-bit) DAC. + +compatible: "adi,ad5693" + +include: adi,ad569x-base.yaml diff --git a/dts/bindings/dac/adi,ad569x-base.yaml b/dts/bindings/dac/adi,ad569x-base.yaml new file mode 100644 index 0000000000..52fa0e742f --- /dev/null +++ b/dts/bindings/dac/adi,ad569x-base.yaml @@ -0,0 +1,61 @@ +# Copyright (c) 2024 Jan Kubiznak +# SPDX-License-Identifier: Apache-2.0 + +include: [dac-controller.yaml] + +properties: + "#io-channel-cells": + const: 1 + + resolution: + type: int + required: true + description: DAC resolution. + + voltage-reference-mv: + type: int + required: true + description: DAC reference voltage in mV. + + voltage-reference: + type: string + default: "internal" + enum: + - "internal" + - "external" + description: | + DAC voltage reference select. + - Internal voltage reference - 2.5V (reg: 0). + - External voltage reference (reg: 1). + The default corresponds to the reset value of the register field. + + gain: + type: string + default: "gain-1" + enum: + - "gain-1" + - "gain-2" + description: | + Gain selection bit. + - Gain of 1 (reg: 0). + - Gain of 2 (reg: 1). + The default corresponds to the reset value of the register field. + + power-down-mode: + type: string + default: "normal" + enum: + - "normal" + - "power-down-1k" + - "power-down-100k" + - "power-down-3-state" + description: | + Power-down mode select. + - Normal mode (reg: 0). + - 1 kOhm output impedance (reg: 1). + - 100 kOhm output impedance (reg: 2). + - Three-state output impedance (reg: 3). + The default corresponds to the reset value of the register field. + +io-channel-cells: + - output diff --git a/tests/drivers/build_all/dac/app.overlay b/tests/drivers/build_all/dac/app.overlay index ea90be6512..ec86ed2b26 100644 --- a/tests/drivers/build_all/dac/app.overlay +++ b/tests/drivers/build_all/dac/app.overlay @@ -76,6 +76,33 @@ voltage-reference = "internal"; output-gain = "mul2"; }; + + test_ad5691: ad5691@4a { + status = "okay"; + compatible = "adi,ad5691"; + reg = <0x4a>; + resolution = <12>; + voltage-reference-mv = <2500>; + #io-channel-cells = < 1 >; + }; + + test_ad5692: ad5692@4b { + status = "okay"; + compatible = "adi,ad5692"; + reg = <0x4b>; + resolution = <14>; + voltage-reference-mv = <2500>; + #io-channel-cells = < 1 >; + }; + + test_ad5693: ad5693@4c { + status = "okay"; + compatible = "adi,ad5693"; + reg = <0x4c>; + resolution = <16>; + voltage-reference-mv = <2500>; + #io-channel-cells = < 1 >; + }; }; test_spi: spi@33334444 {