1 /*
2  * Copyright (c) 2020 M2I Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/spi.h>
9 #include <zephyr/drivers/dac.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/dt-bindings/dac/dacx0508.h>
12 
13 LOG_MODULE_REGISTER(dac_dacx0508, CONFIG_DAC_LOG_LEVEL);
14 
15 #define DACX0508_REG_DEVICE_ID   0x01U
16 #define DACX0508_REG_CONFIG      0x03U
17 #define DACX0508_REG_GAIN        0x04U
18 #define DACX0508_REG_TRIGGER     0x05U
19 #define DACX0508_REG_STATUS      0x07U
20 #define DACX0508_REG_DAC0        0x08U
21 
22 #define DACX0508_MASK_DEVICE_ID_8CH          BIT(11)
23 #define DACX0508_MASK_CONFIG_REF_PWDWN       BIT(8)
24 #define DACX0508_MASK_GAIN_BUFF_GAIN(x)      BIT(x)
25 #define DACX0508_MASK_GAIN_REFDIV_EN         BIT(8)
26 #define DACX0508_MASK_TRIGGER_SOFT_RESET     (BIT(1) | BIT(3))
27 #define DACX0508_MASK_STATUS_REF_ALM         BIT(0)
28 
29 #define DACX0508_READ_CMD       0x80
30 #define DACX0508_POR_DELAY      250
31 #define DACX0508_MAX_CHANNEL    8
32 
33 struct dacx0508_config {
34 	struct spi_dt_spec bus;
35 	uint8_t resolution;
36 	uint8_t reference;
37 	uint8_t gain[8];
38 };
39 
40 struct dacx0508_data {
41 	uint8_t configured;
42 };
43 
dacx0508_reg_read(const struct device * dev,uint8_t addr,uint8_t * data)44 static int dacx0508_reg_read(const struct device *dev, uint8_t addr,
45 			     uint8_t *data)
46 {
47 	const struct dacx0508_config *config = dev->config;
48 	const struct spi_buf buf[2] = {
49 		{
50 			.buf = &addr,
51 			.len = sizeof(addr)
52 		},
53 		{
54 			.buf = data,
55 			.len = 2
56 		}
57 	};
58 	struct spi_buf_set tx = {
59 		.buffers = buf,
60 		.count = ARRAY_SIZE(buf),
61 	};
62 	struct spi_buf_set rx = {
63 		.buffers = buf,
64 		.count = ARRAY_SIZE(buf)
65 	};
66 	uint8_t tmp;
67 	int ret;
68 
69 	if (k_is_in_isr()) {
70 		/* Prevent SPI transactions from an ISR */
71 		return -EWOULDBLOCK;
72 	}
73 
74 	tmp = addr |= DACX0508_READ_CMD;
75 
76 	ret = spi_write_dt(&config->bus, &tx);
77 	if (ret) {
78 		return ret;
79 	}
80 
81 	ret = spi_read_dt(&config->bus, &rx);
82 	if (ret) {
83 		return ret;
84 	}
85 
86 	if (addr != tmp) {
87 		return -EIO;
88 	}
89 
90 	return 0;
91 }
92 
dacx0508_reg_write(const struct device * dev,uint8_t addr,uint8_t * data)93 static int dacx0508_reg_write(const struct device *dev, uint8_t addr,
94 			      	uint8_t *data)
95 {
96 	const struct dacx0508_config *config = dev->config;
97 	const struct spi_buf buf[2] = {
98 		{
99 			.buf = &addr,
100 			.len = sizeof(addr)
101 		},
102 		{
103 			.buf = data,
104 			.len = 2
105 		}
106 	};
107 	struct spi_buf_set tx = {
108 		.buffers = buf,
109 		.count = ARRAY_SIZE(buf),
110 	};
111 
112 	if (k_is_in_isr()) {
113 		/* Prevent SPI transactions from an ISR */
114 		return -EWOULDBLOCK;
115 	}
116 
117 	return spi_write_dt(&config->bus, &tx);
118 }
119 
dacx0508_reg_update(const struct device * dev,uint8_t addr,uint16_t mask,bool setting)120 int dacx0508_reg_update(const struct device *dev, uint8_t addr,
121 			 uint16_t mask, bool setting)
122 {
123 	uint8_t regval[2] = {0, };
124 	uint16_t tmp;
125 	int ret;
126 
127 	ret = dacx0508_reg_read(dev, addr, regval);
128 	if (ret < 0) {
129 		return ret;
130 	}
131 	tmp = (regval[0] << 8) | regval[1];
132 
133 	if (setting) {
134 		tmp |= mask;
135 	} else {
136 		tmp &= ~mask;
137 	}
138 
139 	regval[0] = tmp >> 8;
140 	regval[1] = tmp & 0xFF;
141 
142 	ret = dacx0508_reg_write(dev, addr, regval);
143 	if (ret) {
144 		return ret;
145 	}
146 
147 	return 0;
148 }
149 
dacx0508_channel_setup(const struct device * dev,const struct dac_channel_cfg * channel_cfg)150 static int dacx0508_channel_setup(const struct device *dev,
151 				   const struct dac_channel_cfg *channel_cfg)
152 {
153 	const struct dacx0508_config *config = dev->config;
154 	struct dacx0508_data *data = dev->data;
155 
156 	if (channel_cfg->channel_id > DACX0508_MAX_CHANNEL - 1) {
157 		LOG_ERR("Unsupported channel %d", channel_cfg->channel_id);
158 		return -ENOTSUP;
159 	}
160 
161 	if (channel_cfg->resolution != config->resolution) {
162 		LOG_ERR("Unsupported resolution %d", channel_cfg->resolution);
163 		return -ENOTSUP;
164 	}
165 
166 	if (channel_cfg->internal) {
167 		LOG_ERR("Internal channels not supported");
168 		return -ENOTSUP;
169 	}
170 
171 	data->configured |= BIT(channel_cfg->channel_id);
172 
173 	return 0;
174 }
175 
dacx0508_write_value(const struct device * dev,uint8_t channel,uint32_t value)176 static int dacx0508_write_value(const struct device *dev, uint8_t channel,
177 				uint32_t value)
178 {
179 	const struct dacx0508_config *config = dev->config;
180 	struct dacx0508_data *data = dev->data;
181 	uint8_t regval[2];
182 	int ret;
183 
184 	if (channel > DACX0508_MAX_CHANNEL - 1) {
185 		LOG_ERR("unsupported channel %d", channel);
186 		return -ENOTSUP;
187 	}
188 
189 	if (!(data->configured & BIT(channel))) {
190 		LOG_ERR("Channel not initialized");
191 		return -EINVAL;
192 	}
193 
194 	if (value >= (1 << config->resolution)) {
195 		LOG_ERR("Value %d out of range", value);
196 		return -EINVAL;
197 	}
198 
199 	value <<= (16 - config->resolution);
200 	regval[0] = value >> 8;
201 	regval[1] = value & 0xFF;
202 
203 	ret = dacx0508_reg_write(dev, DACX0508_REG_DAC0 + channel, regval);
204 	if (ret) {
205 		return -EIO;
206 	}
207 
208 	return 0;
209 }
210 
dacx0508_soft_reset(const struct device * dev)211 static int dacx0508_soft_reset(const struct device *dev)
212 {
213 	uint8_t regval[2] = {0, DACX0508_MASK_TRIGGER_SOFT_RESET};
214 	int ret;
215 
216 	ret = dacx0508_reg_write(dev, DACX0508_REG_TRIGGER, regval);
217 	if (ret) {
218 		return -EIO;
219 	}
220 	k_usleep(DACX0508_POR_DELAY);
221 
222 	return 0;
223 }
224 
dacx0508_device_id_check(const struct device * dev)225 static int dacx0508_device_id_check(const struct device *dev)
226 {
227 	const struct dacx0508_config *config = dev->config;
228 	uint8_t regval[2] = {0, };
229 	uint8_t resolution;
230 	uint16_t dev_id;
231 	int ret;
232 
233 	ret = dacx0508_reg_read(dev, DACX0508_REG_DEVICE_ID, regval);
234 	if (ret) {
235 		LOG_ERR("Unable to read Device ID");
236 		return -EIO;
237 	}
238 	dev_id = (regval[0] << 8) | regval[1];
239 
240 	resolution = dev_id >> 12;
241 	if (resolution != (16 - config->resolution) >> 1) {
242 		LOG_ERR("Not match chip resolution");
243 		return -EINVAL;
244 	}
245 
246 	if ((dev_id & DACX0508_MASK_DEVICE_ID_8CH) !=
247 				DACX0508_MASK_DEVICE_ID_8CH) {
248 		LOG_ERR("Support channels mismatch");
249 		return -EINVAL;
250 	}
251 
252 	return 0;
253 }
254 
dacx0508_setup(const struct device * dev)255 static int dacx0508_setup(const struct device *dev)
256 {
257 	const struct dacx0508_config *config = dev->config;
258 	uint8_t regval[2] = {0, }, tmp = 0;
259 	bool ref_pwdwn, refdiv_en;
260 	int ret;
261 
262 	switch (config->reference) {
263 	case DACX0508_REF_INTERNAL_1:
264 		ref_pwdwn = false;
265 		refdiv_en = false;
266 		break;
267 	case DACX0508_REF_INTERNAL_1_2:
268 		ref_pwdwn = false;
269 		refdiv_en = true;
270 		break;
271 	case DACX0508_REF_EXTERNAL_1:
272 		ref_pwdwn = true;
273 		refdiv_en = false;
274 		break;
275 	case DACX0508_REF_EXTERNAL_1_2:
276 		ref_pwdwn = true;
277 		refdiv_en = true;
278 		break;
279 	default:
280 		LOG_ERR("unsupported channel reference type '%d'",
281 			config->reference);
282 		return -ENOTSUP;
283 	}
284 
285 	ret = dacx0508_reg_update(dev, DACX0508_REG_CONFIG,
286 				  DACX0508_MASK_CONFIG_REF_PWDWN, ref_pwdwn);
287 	if (ret) {
288 		LOG_ERR("GAIN Register update failed");
289 		return -EIO;
290 	}
291 
292 	ret = dacx0508_reg_update(dev, DACX0508_REG_GAIN,
293 				  DACX0508_MASK_GAIN_REFDIV_EN, refdiv_en);
294 	if (ret) {
295 		LOG_ERR("GAIN Register update failed");
296 		return -EIO;
297 	}
298 
299 
300 	for (int i = 0; i < 8; i++) {
301 		tmp |= config->gain[i] << i;
302 	}
303 
304 	ret = dacx0508_reg_read(dev, DACX0508_REG_GAIN, regval);
305 	if (ret) {
306 		LOG_ERR("Unable to read GAIN Register");
307 		return -EIO;
308 	}
309 
310 	regval[1] = tmp;
311 	ret = dacx0508_reg_write(dev, DACX0508_REG_GAIN, regval);
312 	if (ret) {
313 		LOG_ERR("Unable to write GAIN Register");
314 		return -EIO;
315 	}
316 
317 	ret = dacx0508_reg_read(dev, DACX0508_REG_STATUS, regval);
318 	if (ret) {
319 		LOG_ERR("Unable to read STATUS Register");
320 		return -EIO;
321 	}
322 	if ((regval[1] & DACX0508_MASK_STATUS_REF_ALM) ==
323 				DACX0508_MASK_STATUS_REF_ALM) {
324 		LOG_ERR("Difference between VREF/DIV and VDD is "
325 			"below the required minimum analog threshold");
326 		return -EIO;
327 	}
328 
329 	return 0;
330 }
331 
dacx0508_init(const struct device * dev)332 static int dacx0508_init(const struct device *dev)
333 {
334 	const struct dacx0508_config *config = dev->config;
335 	struct dacx0508_data *data = dev->data;
336 	int ret;
337 
338 	if (!spi_is_ready_dt(&config->bus)) {
339 		LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
340 		return -ENODEV;
341 	}
342 
343 	ret = dacx0508_soft_reset(dev);
344 	if (ret) {
345 		LOG_ERR("Soft-reset failed");
346 		return ret;
347 	}
348 
349 	ret = dacx0508_device_id_check(dev);
350 	if (ret) {
351 		return ret;
352 	}
353 
354 	ret = dacx0508_setup(dev);
355 	if (ret) {
356 		return ret;
357 	}
358 
359 	data->configured = 0;
360 
361 	return 0;
362 }
363 
364 static DEVICE_API(dac, dacx0508_driver_api) = {
365 	.channel_setup = dacx0508_channel_setup,
366 	.write_value = dacx0508_write_value,
367 };
368 
369 #define INST_DT_DACX0508(inst, t) DT_INST(inst, ti_dac##t)
370 
371 #define DACX0508_DEVICE(t, n, res) \
372 	static struct dacx0508_data dac##t##_data_##n; \
373 	static const struct dacx0508_config dac##t##_config_##n = { \
374 		.bus = SPI_DT_SPEC_GET(INST_DT_DACX0508(n, t), \
375 			SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | \
376 			SPI_WORD_SET(8) | SPI_MODE_CPHA, 0), \
377 		.resolution = res, \
378 		.reference = DT_PROP(INST_DT_DACX0508(n, t), \
379 					     voltage_reference), \
380 		.gain[0] = DT_PROP(INST_DT_DACX0508(n, t), channel0_gain), \
381 		.gain[1] = DT_PROP(INST_DT_DACX0508(n, t), channel1_gain), \
382 		.gain[2] = DT_PROP(INST_DT_DACX0508(n, t), channel2_gain), \
383 		.gain[3] = DT_PROP(INST_DT_DACX0508(n, t), channel3_gain), \
384 		.gain[4] = DT_PROP(INST_DT_DACX0508(n, t), channel4_gain), \
385 		.gain[5] = DT_PROP(INST_DT_DACX0508(n, t), channel5_gain), \
386 		.gain[6] = DT_PROP(INST_DT_DACX0508(n, t), channel6_gain), \
387 		.gain[7] = DT_PROP(INST_DT_DACX0508(n, t), channel7_gain), \
388 	}; \
389 	DEVICE_DT_DEFINE(INST_DT_DACX0508(n, t), \
390 			    &dacx0508_init, NULL, \
391 			    &dac##t##_data_##n, \
392 			    &dac##t##_config_##n, POST_KERNEL, \
393 			    CONFIG_DAC_DACX0508_INIT_PRIORITY, \
394 			    &dacx0508_driver_api);
395 
396 /*
397  * DAC60508: 12-bit
398  */
399 #define DAC60508_DEVICE(n) DACX0508_DEVICE(60508, n, 12)
400 
401 /*
402  * DAC70508: 14-bit
403  */
404 #define DAC70508_DEVICE(n) DACX0508_DEVICE(70508, n, 14)
405 
406 /*
407  * DAC80508: 16-bit
408  */
409 #define DAC80508_DEVICE(n) DACX0508_DEVICE(80508, n, 16)
410 
411 #define CALL_WITH_ARG(arg, expr) expr(arg)
412 
413 #define INST_DT_DACX0508_FOREACH(t, inst_expr) \
414 	LISTIFY(DT_NUM_INST_STATUS_OKAY(ti_dac##t), \
415 		     CALL_WITH_ARG, (), inst_expr)
416 
417 INST_DT_DACX0508_FOREACH(60508, DAC60508_DEVICE);
418 INST_DT_DACX0508_FOREACH(70508, DAC70508_DEVICE);
419 INST_DT_DACX0508_FOREACH(80508, DAC80508_DEVICE);
420