/* * 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; } if (channel_cfg->internal) { LOG_ERR("Internal channels not supported"); return -ENOTSUP; } 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"); return -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; } value <<= 16 - config->resolution; 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