1 /*
2 * Copyright (c) 2023 Grinn
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #define DT_DRV_COMPAT adi_ad559x_dac
7
8 #include <zephyr/drivers/dac.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/sys/byteorder.h>
11
12 #include <zephyr/drivers/mfd/ad559x.h>
13
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(dac_ad559x, CONFIG_DAC_LOG_LEVEL);
16
17 #define AD559X_DAC_RESOLUTION 12
18 #define AD559X_DAC_WR_POINTER 0x10
19 #define AD559X_DAC_WR_MSB_BIT BIT(15)
20 #define AD559X_DAC_CHANNEL_SHIFT_VAL 12
21
22 struct dac_ad559x_config {
23 const struct device *mfd_dev;
24 bool double_output_range;
25 };
26
27 struct dac_ad559x_data {
28 uint8_t dac_conf;
29 };
30
dac_ad559x_channel_setup(const struct device * dev,const struct dac_channel_cfg * channel_cfg)31 static int dac_ad559x_channel_setup(const struct device *dev,
32 const struct dac_channel_cfg *channel_cfg)
33 {
34 const struct dac_ad559x_config *config = dev->config;
35 struct dac_ad559x_data *data = dev->data;
36
37 if (channel_cfg->channel_id >= AD559X_PIN_MAX) {
38 LOG_ERR("Invalid channel number %d", channel_cfg->channel_id);
39 return -EINVAL;
40 }
41
42 if (channel_cfg->resolution != AD559X_DAC_RESOLUTION) {
43 LOG_ERR("Invalid resolution %d", channel_cfg->resolution);
44 return -EINVAL;
45 }
46
47 if (channel_cfg->internal) {
48 LOG_ERR("Internal channels not supported");
49 return -ENOTSUP;
50 }
51
52 data->dac_conf |= BIT(channel_cfg->channel_id);
53
54 return mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_LDAC_EN, data->dac_conf);
55 }
56
dac_ad559x_write_value(const struct device * dev,uint8_t channel,uint32_t value)57 static int dac_ad559x_write_value(const struct device *dev, uint8_t channel, uint32_t value)
58 {
59 const struct dac_ad559x_config *config = dev->config;
60 uint16_t msg;
61
62 if (channel >= AD559X_PIN_MAX) {
63 LOG_ERR("Invalid channel number %d", channel);
64 return -EINVAL;
65 }
66
67 if (value >= (1 << AD559X_DAC_RESOLUTION)) {
68 LOG_ERR("Value %d out of range", value);
69 return -EINVAL;
70 }
71
72 if (mfd_ad559x_has_pointer_byte_map(config->mfd_dev)) {
73 return mfd_ad559x_write_reg(config->mfd_dev, AD559X_DAC_WR_POINTER | channel,
74 value);
75 } else {
76 msg = sys_cpu_to_be16(AD559X_DAC_WR_MSB_BIT |
77 channel << AD559X_DAC_CHANNEL_SHIFT_VAL | value);
78
79 return mfd_ad559x_write_raw(config->mfd_dev, (uint8_t *)&msg, sizeof(msg));
80 }
81 }
82
83 static DEVICE_API(dac, dac_ad559x_api) = {
84 .channel_setup = dac_ad559x_channel_setup,
85 .write_value = dac_ad559x_write_value,
86 };
87
dac_ad559x_init(const struct device * dev)88 static int dac_ad559x_init(const struct device *dev)
89 {
90 const struct dac_ad559x_config *config = dev->config;
91 int ret;
92 uint16_t reg_val;
93
94 if (!device_is_ready(config->mfd_dev)) {
95 return -ENODEV;
96 }
97
98 ret = mfd_ad559x_read_reg(config->mfd_dev, AD559X_REG_GEN_CTRL, 0, ®_val);
99 if (ret < 0) {
100 return ret;
101 }
102
103 if (config->double_output_range) {
104 reg_val |= AD559X_DAC_RANGE;
105 } else {
106 reg_val &= ~AD559X_DAC_RANGE;
107 }
108
109 ret = mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_GEN_CTRL, reg_val);
110 if (ret < 0) {
111 return ret;
112 }
113
114 ret = mfd_ad559x_write_reg(config->mfd_dev, AD559X_REG_PD_REF_CTRL, AD559X_EN_REF);
115 if (ret < 0) {
116 return ret;
117 }
118
119 return 0;
120 }
121
122 #define DAC_AD559X_DEFINE(inst) \
123 static const struct dac_ad559x_config dac_ad559x_config##inst = { \
124 .mfd_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
125 .double_output_range = DT_INST_PROP(inst, double_output_range), \
126 }; \
127 \
128 struct dac_ad559x_data dac_ad559x_data##inst; \
129 \
130 DEVICE_DT_INST_DEFINE(inst, dac_ad559x_init, NULL, &dac_ad559x_data##inst, \
131 &dac_ad559x_config##inst, POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, \
132 &dac_ad559x_api);
133
134 DT_INST_FOREACH_STATUS_OKAY(DAC_AD559X_DEFINE)
135