1 /*
2  * Driver for Linear Technology LTC1660/LTC1665  DAC
3  *
4  * Copyright (C) 2023 Marcus Folkesson <marcus.folkesson@gmail.com>
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/drivers/dac.h>
12 #include <zephyr/logging/log.h>
13 
14 LOG_MODULE_REGISTER(dac_ltc166x, CONFIG_DAC_LOG_LEVEL);
15 
16 #define LTC166X_REG_MASK               GENMASK(15, 12)
17 #define LTC166X_DATA8_MASK             GENMASK(11, 4)
18 #define LTC166X_DATA10_MASK            GENMASK(12, 2)
19 
20 struct ltc166x_config {
21 	struct spi_dt_spec bus;
22 	uint8_t resolution;
23 	uint8_t nchannels;
24 };
25 
ltc166x_reg_write(const struct device * dev,uint8_t addr,uint32_t data)26 static int ltc166x_reg_write(const struct device *dev, uint8_t addr,
27 			uint32_t data)
28 {
29 	const struct ltc166x_config *config = dev->config;
30 	uint16_t regval;
31 
32 	regval = FIELD_PREP(LTC166X_REG_MASK, addr);
33 
34 	if (config->resolution == 10) {
35 		regval |= FIELD_PREP(LTC166X_DATA10_MASK, data);
36 	} else {
37 		regval |= FIELD_PREP(LTC166X_DATA8_MASK, data);
38 	}
39 
40 	const struct spi_buf buf = {
41 			.buf = &regval,
42 			.len = sizeof(regval),
43 	};
44 
45 	struct spi_buf_set tx = {
46 		.buffers = &buf,
47 		.count = 1,
48 	};
49 
50 	return spi_write_dt(&config->bus, &tx);
51 }
52 
53 
ltc166x_channel_setup(const struct device * dev,const struct dac_channel_cfg * channel_cfg)54 static int ltc166x_channel_setup(const struct device *dev,
55 				   const struct dac_channel_cfg *channel_cfg)
56 {
57 	const struct ltc166x_config *config = dev->config;
58 
59 	if (channel_cfg->channel_id > config->nchannels - 1) {
60 		LOG_ERR("Unsupported channel %d", channel_cfg->channel_id);
61 		return -ENOTSUP;
62 	}
63 
64 	if (channel_cfg->resolution != config->resolution) {
65 		LOG_ERR("Unsupported resolution %d", channel_cfg->resolution);
66 		return -ENOTSUP;
67 	}
68 
69 	if (channel_cfg->internal) {
70 		LOG_ERR("Internal channels not supported");
71 		return -ENOTSUP;
72 	}
73 
74 	return 0;
75 }
76 
ltc166x_write_value(const struct device * dev,uint8_t channel,uint32_t value)77 static int ltc166x_write_value(const struct device *dev, uint8_t channel,
78 				uint32_t value)
79 {
80 	const struct ltc166x_config *config = dev->config;
81 
82 	if (channel > config->nchannels - 1) {
83 		LOG_ERR("unsupported channel %d", channel);
84 		return -ENOTSUP;
85 	}
86 
87 	if (value >= (1 << config->resolution)) {
88 		LOG_ERR("Value %d out of range", value);
89 		return -EINVAL;
90 	}
91 
92 	return ltc166x_reg_write(dev, channel + 1, value);
93 }
94 
ltc166x_init(const struct device * dev)95 static int ltc166x_init(const struct device *dev)
96 {
97 	const struct ltc166x_config *config = dev->config;
98 
99 	if (!spi_is_ready_dt(&config->bus)) {
100 		LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
101 		return -ENODEV;
102 	}
103 	return 0;
104 }
105 
106 static DEVICE_API(dac, ltc166x_driver_api) = {
107 	.channel_setup = ltc166x_channel_setup,
108 	.write_value = ltc166x_write_value,
109 };
110 
111 
112 #define INST_DT_LTC166X(inst, t) DT_INST(inst, lltc_ltc##t)
113 
114 #define LTC166X_DEVICE(t, n, res, nchan) \
115 	static const struct ltc166x_config ltc##t##_config_##n = { \
116 		.bus = SPI_DT_SPEC_GET(INST_DT_LTC166X(n, t), \
117 			SPI_OP_MODE_MASTER | \
118 			SPI_WORD_SET(8), 0), \
119 		.resolution = res, \
120 		.nchannels = nchan, \
121 	}; \
122 	DEVICE_DT_DEFINE(INST_DT_LTC166X(n, t), \
123 			    &ltc166x_init, NULL, \
124 			    NULL, \
125 			    &ltc##t##_config_##n, POST_KERNEL, \
126 			    CONFIG_DAC_LTC166X_INIT_PRIORITY, \
127 			    &ltc166x_driver_api)
128 
129 /*
130  * LTC1660: 10-bit
131  */
132 #define LTC1660_DEVICE(n) LTC166X_DEVICE(1660, n, 10, 8)
133 
134 /*
135  * LTC1665: 8-bit
136  */
137 #define LTC1665_DEVICE(n) LTC166X_DEVICE(1665, n, 8, 8)
138 
139 #define CALL_WITH_ARG(arg, expr) expr(arg)
140 
141 #define INST_DT_LTC166X_FOREACH(t, inst_expr) \
142 	LISTIFY(DT_NUM_INST_STATUS_OKAY(lltc_ltc##t), \
143 		     CALL_WITH_ARG, (), inst_expr)
144 
145 INST_DT_LTC166X_FOREACH(1660, LTC1660_DEVICE);
146 INST_DT_LTC166X_FOREACH(1665, LTC1665_DEVICE);
147