drivers: dac: dac_ad569x: Support for AD569x DACs.

Added support for Analog Devices AD5691 / AD5692 / AD5693 DACs.

Signed-off-by: Jan Kubiznak <jan.kubiznak@deveritec.com>
This commit is contained in:
Jan Kubiznak 2024-04-10 13:32:02 +02:00 committed by Carles Cufí
parent 65abd2186c
commit 030a8b1830
9 changed files with 317 additions and 0 deletions

View file

@ -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)

View file

@ -57,4 +57,6 @@ source "drivers/dac/Kconfig.ad56xx"
source "drivers/dac/Kconfig.ad559x"
source "drivers/dac/Kconfig.ad569x"
endif # DAC

View file

@ -0,0 +1,10 @@
# Copyright (c) 2024 Jan Kubiznak <jan.kubiznak@deveritec.com>
# 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.

192
drivers/dac/dac_ad569x.c Normal file
View file

@ -0,0 +1,192 @@
/*
* Copyright (c) 2024 Jan Kubiznak <jan.kubiznak@deveritec.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/dac.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
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, &reg);
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

View file

@ -0,0 +1,8 @@
# Copyright (c) 2024 Jan Kubiznak <jan.kubiznak@deveritec.com>
# SPDX-License-Identifier: Apache-2.0
description: Driver for AD5691 (12-bit) DAC.
compatible: "adi,ad5691"
include: adi,ad569x-base.yaml

View file

@ -0,0 +1,8 @@
# Copyright (c) 2024 Jan Kubiznak <jan.kubiznak@deveritec.com>
# SPDX-License-Identifier: Apache-2.0
description: Driver for AD5692 (14-bit) DAC.
compatible: "adi,ad5692"
include: adi,ad569x-base.yaml

View file

@ -0,0 +1,8 @@
# Copyright (c) 2024 Jan Kubiznak <jan.kubiznak@deveritec.com>
# SPDX-License-Identifier: Apache-2.0
description: Driver for AD5693 (16-bit) DAC.
compatible: "adi,ad5693"
include: adi,ad569x-base.yaml

View file

@ -0,0 +1,61 @@
# Copyright (c) 2024 Jan Kubiznak <jan.kubiznak@deveritec.com>
# 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

View file

@ -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 {