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 	return 0;
70 }
71 
ltc166x_write_value(const struct device * dev,uint8_t channel,uint32_t value)72 static int ltc166x_write_value(const struct device *dev, uint8_t channel,
73 				uint32_t value)
74 {
75 	const struct ltc166x_config *config = dev->config;
76 
77 	if (channel > config->nchannels - 1) {
78 		LOG_ERR("unsupported channel %d", channel);
79 		return -ENOTSUP;
80 	}
81 
82 	if (value >= (1 << config->resolution)) {
83 		LOG_ERR("Value %d out of range", value);
84 		return -EINVAL;
85 	}
86 
87 	return ltc166x_reg_write(dev, channel + 1, value);
88 }
89 
ltc166x_init(const struct device * dev)90 static int ltc166x_init(const struct device *dev)
91 {
92 	const struct ltc166x_config *config = dev->config;
93 
94 	if (!spi_is_ready_dt(&config->bus)) {
95 		LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
96 		return -ENODEV;
97 	}
98 	return 0;
99 }
100 
101 static const struct dac_driver_api ltc166x_driver_api = {
102 	.channel_setup = ltc166x_channel_setup,
103 	.write_value = ltc166x_write_value,
104 };
105 
106 
107 #define INST_DT_LTC166X(inst, t) DT_INST(inst, lltc_ltc##t)
108 
109 #define LTC166X_DEVICE(t, n, res, nchan) \
110 	static const struct ltc166x_config ltc##t##_config_##n = { \
111 		.bus = SPI_DT_SPEC_GET(INST_DT_LTC166X(n, t), \
112 			SPI_OP_MODE_MASTER | \
113 			SPI_WORD_SET(8), 0), \
114 		.resolution = res, \
115 		.nchannels = nchan, \
116 	}; \
117 	DEVICE_DT_DEFINE(INST_DT_LTC166X(n, t), \
118 			    &ltc166x_init, NULL, \
119 			    NULL, \
120 			    &ltc##t##_config_##n, POST_KERNEL, \
121 			    CONFIG_DAC_LTC166X_INIT_PRIORITY, \
122 			    &ltc166x_driver_api)
123 
124 /*
125  * LTC1660: 10-bit
126  */
127 #define LTC1660_DEVICE(n) LTC166X_DEVICE(1660, n, 10, 8)
128 
129 /*
130  * LTC1665: 8-bit
131  */
132 #define LTC1665_DEVICE(n) LTC166X_DEVICE(1665, n, 8, 8)
133 
134 #define CALL_WITH_ARG(arg, expr) expr(arg)
135 
136 #define INST_DT_LTC166X_FOREACH(t, inst_expr) \
137 	LISTIFY(DT_NUM_INST_STATUS_OKAY(lltc_ltc##t), \
138 		     CALL_WITH_ARG, (), inst_expr)
139 
140 INST_DT_LTC166X_FOREACH(1660, LTC1660_DEVICE);
141 INST_DT_LTC166X_FOREACH(1665, LTC1665_DEVICE);
142