1 /*
2 * Copyright (c) 2023 SILA Embedded Solutions GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdint.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/drivers/dac.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/sys/byteorder.h>
14
15 LOG_MODULE_REGISTER(dac_ad56xx, CONFIG_DAC_LOG_LEVEL);
16
17 /*
18 * These values are actually all way less than 1us, but we can only
19 * wait with 1us precision.
20 *
21 * This should be checked when new types of this series are added to
22 * this implementation.
23 */
24 #define DAC_AD56XX_MINIMUM_PULSE_WIDTH_LOW_IN_US 1
25 #define DAC_AD56XX_PULSE_ACTIVATION_TIME_IN_US 1
26
27 enum ad56xx_command {
28 AD56XX_CMD_WRITE_UPDATE_CHANNEL = 3,
29 AD56XX_CMD_SOFTWARE_RESET = 6,
30 };
31
32 struct ad56xx_config {
33 struct spi_dt_spec bus;
34 const struct gpio_dt_spec gpio_reset;
35 uint8_t resolution;
36
37 const uint8_t *channel_addresses;
38 size_t channel_count;
39 };
40
41 struct ad56xx_data {
42 };
43
ad56xx_write_command(const struct device * dev,enum ad56xx_command command,uint8_t address,uint16_t value)44 static int ad56xx_write_command(const struct device *dev, enum ad56xx_command command,
45 uint8_t address, uint16_t value)
46 {
47 const struct ad56xx_config *config = dev->config;
48 uint8_t buffer_tx[3];
49 uint8_t buffer_rx[ARRAY_SIZE(buffer_tx)];
50 const struct spi_buf tx_buf[] = {{
51 .buf = buffer_tx,
52 .len = ARRAY_SIZE(buffer_tx),
53 }};
54 const struct spi_buf rx_buf[] = {{
55 .buf = buffer_rx,
56 .len = ARRAY_SIZE(buffer_rx),
57 }};
58 const struct spi_buf_set tx = {
59 .buffers = tx_buf,
60 .count = ARRAY_SIZE(tx_buf),
61 };
62 const struct spi_buf_set rx = {
63 .buffers = rx_buf,
64 .count = ARRAY_SIZE(rx_buf),
65 };
66
67 buffer_tx[0] = (command << 4) | address;
68 value = value << (16 - config->resolution);
69 sys_put_be16(value, buffer_tx + 1);
70
71 LOG_DBG("sending to DAC %s command 0x%02X, address 0x%02X and value 0x%04X", dev->name,
72 command, address, value);
73 int result = spi_transceive_dt(&config->bus, &tx, &rx);
74
75 if (result != 0) {
76 LOG_ERR("spi_transceive failed with error %i", result);
77 return result;
78 }
79
80 return 0;
81 }
82
ad56xx_channel_setup(const struct device * dev,const struct dac_channel_cfg * channel_cfg)83 static int ad56xx_channel_setup(const struct device *dev, const struct dac_channel_cfg *channel_cfg)
84 {
85 const struct ad56xx_config *config = dev->config;
86
87 if (channel_cfg->channel_id >= config->channel_count) {
88 LOG_ERR("invalid channel %i", channel_cfg->channel_id);
89 return -EINVAL;
90 }
91
92 if (channel_cfg->resolution != config->resolution) {
93 LOG_ERR("invalid resolution %i", channel_cfg->resolution);
94 return -EINVAL;
95 }
96
97 return 0;
98 }
99
ad56xx_write_value(const struct device * dev,uint8_t channel,uint32_t value)100 static int ad56xx_write_value(const struct device *dev, uint8_t channel, uint32_t value)
101 {
102 const struct ad56xx_config *config = dev->config;
103
104 if (value > BIT(config->resolution) - 1) {
105 LOG_ERR("invalid value %i", value);
106 return -EINVAL;
107 }
108
109 if (channel >= config->channel_count) {
110 LOG_ERR("invalid channel %i", channel);
111 return -EINVAL;
112 }
113
114 return ad56xx_write_command(dev, AD56XX_CMD_WRITE_UPDATE_CHANNEL,
115 config->channel_addresses[channel], value);
116 }
117
ad56xx_init(const struct device * dev)118 static int ad56xx_init(const struct device *dev)
119 {
120 const struct ad56xx_config *config = dev->config;
121 int result;
122
123 if (!spi_is_ready_dt(&config->bus)) {
124 LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
125 return -ENODEV;
126 }
127
128 if (config->gpio_reset.port != NULL) {
129 LOG_DBG("reset %s with GPIO", dev->name);
130 result = gpio_pin_configure_dt(&config->gpio_reset, GPIO_OUTPUT_ACTIVE);
131 if (result != 0) {
132 LOG_ERR("failed to initialize GPIO for reset");
133 return result;
134 }
135
136 k_busy_wait(DAC_AD56XX_MINIMUM_PULSE_WIDTH_LOW_IN_US);
137 gpio_pin_set_dt(&config->gpio_reset, 0);
138 } else {
139 LOG_DBG("reset %s with command", dev->name);
140 result = ad56xx_write_command(dev, AD56XX_CMD_SOFTWARE_RESET, 0, 0);
141 if (result != 0) {
142 LOG_ERR("failed to send reset command");
143 return result;
144 }
145 }
146
147 /*
148 * The pulse activation time is actually defined to start together
149 * with the pulse start. To be on the safe side we add the wait time
150 * on top of the actual pulse.
151 */
152 k_busy_wait(DAC_AD56XX_PULSE_ACTIVATION_TIME_IN_US);
153
154 return 0;
155 }
156
157 static const struct dac_driver_api ad56xx_driver_api = {
158 .channel_setup = ad56xx_channel_setup,
159 .write_value = ad56xx_write_value,
160 };
161
162 BUILD_ASSERT(CONFIG_DAC_AD56XX_INIT_PRIORITY > CONFIG_SPI_INIT_PRIORITY,
163 "CONFIG_DAC_AD56XX_INIT_PRIORITY must be higher than CONFIG_SPI_INIT_PRIORITY");
164
165 #define DAC_AD56XX_INST_DEFINE(index, name, res, channels, channels_count) \
166 static struct ad56xx_data data_##name##_##index; \
167 static const struct ad56xx_config config_##name##_##index = { \
168 .bus = SPI_DT_SPEC_INST_GET( \
169 index, SPI_OP_MODE_MASTER | SPI_MODE_CPHA | SPI_WORD_SET(8), 0), \
170 .resolution = res, \
171 .gpio_reset = GPIO_DT_SPEC_INST_GET_OR(index, reset_gpios, {0}), \
172 .channel_addresses = channels, \
173 .channel_count = channels_count, \
174 }; \
175 DEVICE_DT_INST_DEFINE(index, ad56xx_init, NULL, &data_##name##_##index, \
176 &config_##name##_##index, POST_KERNEL, \
177 CONFIG_DAC_AD56XX_INIT_PRIORITY, &ad56xx_driver_api);
178
179 #define DT_DRV_COMPAT adi_ad5628
180 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
181 static const uint8_t ad5628_channels[] = {
182 0, 1, 2, 3, 4, 5, 6, 7,
183 };
184 #define DAC_AD5628_RESOLUTION 12
185 #define DAC_AD5628_CHANNELS ad5628_channels
186 #define DAC_AD5628_CHANNEL_COUNT ARRAY_SIZE(ad5628_channels)
187 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5628_RESOLUTION,
188 DAC_AD5628_CHANNELS, DAC_AD5628_CHANNEL_COUNT)
189 #endif
190 #undef DT_DRV_COMPAT
191
192 #define DT_DRV_COMPAT adi_ad5648
193 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
194 static const uint8_t ad5648_channels[] = {
195 0, 1, 2, 3, 4, 5, 6, 7,
196 };
197 #define DAC_AD5648_RESOLUTION 14
198 #define DAC_AD5648_CHANNELS ad5648_channels
199 #define DAC_AD5648_CHANNEL_COUNT ARRAY_SIZE(ad5648_channels)
200 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5648_RESOLUTION,
201 DAC_AD5648_CHANNELS, DAC_AD5648_CHANNEL_COUNT)
202 #endif
203 #undef DT_DRV_COMPAT
204
205 #define DT_DRV_COMPAT adi_ad5668
206 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
207 static const uint8_t ad5668_channels[] = {
208 0, 1, 2, 3, 4, 5, 6, 7,
209 };
210 #define DAC_AD5668_RESOLUTION 16
211 #define DAC_AD5668_CHANNELS ad5668_channels
212 #define DAC_AD5668_CHANNEL_COUNT ARRAY_SIZE(ad5668_channels)
213 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5668_RESOLUTION,
214 DAC_AD5668_CHANNELS, DAC_AD5668_CHANNEL_COUNT)
215 #endif
216 #undef DT_DRV_COMPAT
217
218 #define DT_DRV_COMPAT adi_ad5672
219 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
220 static const uint8_t ad5672_channels[] = {
221 0, 1, 2, 3, 4, 5, 6, 7,
222 };
223 #define DAC_AD5672_RESOLUTION 12
224 #define DAC_AD5672_CHANNELS ad5672_channels
225 #define DAC_AD5672_CHANNEL_COUNT ARRAY_SIZE(ad5672_channels)
226 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5672_RESOLUTION,
227 DAC_AD5672_CHANNELS, DAC_AD5672_CHANNEL_COUNT)
228 #endif
229 #undef DT_DRV_COMPAT
230
231 #define DT_DRV_COMPAT adi_ad5674
232 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
233 static const uint8_t ad5674_channels[] = {
234 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
235 };
236 #define DAC_AD5674_RESOLUTION 12
237 #define DAC_AD5674_CHANNELS ad5674_channels
238 #define DAC_AD5674_CHANNEL_COUNT ARRAY_SIZE(ad5674_channels)
239 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5674_RESOLUTION,
240 DAC_AD5674_CHANNELS, DAC_AD5674_CHANNEL_COUNT)
241 #endif
242 #undef DT_DRV_COMPAT
243
244 #define DT_DRV_COMPAT adi_ad5676
245 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
246 static const uint8_t ad5676_channels[] = {
247 0, 1, 2, 3, 4, 5, 6, 7,
248 };
249 #define DAC_AD5676_RESOLUTION 16
250 #define DAC_AD5676_CHANNELS ad5676_channels
251 #define DAC_AD5676_CHANNEL_COUNT ARRAY_SIZE(ad5676_channels)
252 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5676_RESOLUTION,
253 DAC_AD5676_CHANNELS, DAC_AD5676_CHANNEL_COUNT)
254 #endif
255 #undef DT_DRV_COMPAT
256
257 #define DT_DRV_COMPAT adi_ad5679
258 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
259 static const uint8_t ad5679_channels[] = {
260 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
261 };
262 #define DAC_AD5679_RESOLUTION 16
263 #define DAC_AD5679_CHANNELS ad5679_channels
264 #define DAC_AD5679_CHANNEL_COUNT ARRAY_SIZE(ad5679_channels)
265 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5679_RESOLUTION,
266 DAC_AD5679_CHANNELS, DAC_AD5679_CHANNEL_COUNT)
267 #endif
268 #undef DT_DRV_COMPAT
269
270 #define DT_DRV_COMPAT adi_ad5684
271 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
272 static const uint8_t ad5684_channels[] = {
273 1,
274 2,
275 4,
276 8,
277 };
278 #define DAC_AD5684_RESOLUTION 12
279 #define DAC_AD5684_CHANNELS ad5684_channels
280 #define DAC_AD5684_CHANNEL_COUNT ARRAY_SIZE(ad5684_channels)
281 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5684_RESOLUTION,
282 DAC_AD5684_CHANNELS, DAC_AD5684_CHANNEL_COUNT)
283 #endif
284 #undef DT_DRV_COMPAT
285
286 #define DT_DRV_COMPAT adi_ad5686
287 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
288 static const uint8_t ad5686_channels[] = {
289 1,
290 2,
291 4,
292 8,
293 };
294 #define DAC_AD5686_RESOLUTION 16
295 #define DAC_AD5686_CHANNELS ad5686_channels
296 #define DAC_AD5686_CHANNEL_COUNT ARRAY_SIZE(ad5686_channels)
297 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5686_RESOLUTION,
298 DAC_AD5686_CHANNELS, DAC_AD5686_CHANNEL_COUNT)
299 #endif
300 #undef DT_DRV_COMPAT
301
302 #define DT_DRV_COMPAT adi_ad5687
303 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
304 static const uint8_t ad5687_channels[] = {
305 1,
306 8,
307 };
308 #define DAC_AD5687_RESOLUTION 12
309 #define DAC_AD5687_CHANNELS ad5687_channels
310 #define DAC_AD5687_CHANNEL_COUNT ARRAY_SIZE(ad5687_channels)
311 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5687_RESOLUTION,
312 DAC_AD5687_CHANNELS, DAC_AD5687_CHANNEL_COUNT)
313 #endif
314 #undef DT_DRV_COMPAT
315
316 #define DT_DRV_COMPAT adi_ad5689
317 #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
318 static const uint8_t ad5689_channels[] = {
319 1,
320 8,
321 };
322 #define DAC_AD5689_RESOLUTION 16
323 #define DAC_AD5689_CHANNELS ad5689_channels
324 #define DAC_AD5689_CHANNEL_COUNT ARRAY_SIZE(ad5689_channels)
325 DT_INST_FOREACH_STATUS_OKAY_VARGS(DAC_AD56XX_INST_DEFINE, DT_DRV_COMPAT, DAC_AD5689_RESOLUTION,
326 DAC_AD5689_CHANNELS, DAC_AD5689_CHANNEL_COUNT)
327 #endif
328 #undef DT_DRV_COMPAT
329