Commit 030a8b18 authored by Jan Kubiznak's avatar Jan Kubiznak Committed by Carles Cufi
Browse files

drivers: dac: dac_ad569x: Support for AD569x DACs.



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

Signed-off-by: default avatarJan Kubiznak <jan.kubiznak@deveritec.com>
parent 65abd218
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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)
+2 −0
Original line number Diff line number Diff line
@@ -57,4 +57,6 @@ source "drivers/dac/Kconfig.ad56xx"

source "drivers/dac/Kconfig.ad559x"

source "drivers/dac/Kconfig.ad569x"

endif # DAC
+10 −0
Original line number Diff line number Diff line
# 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 −0
Original line number Diff line number Diff line
/*
 * 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
+8 −0
Original line number Diff line number Diff line
# 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
Loading