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 = ®val,
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 <c166x_init, NULL, \
119 NULL, \
120 <c##t##_config_##n, POST_KERNEL, \
121 CONFIG_DAC_LTC166X_INIT_PRIORITY, \
122 <c166x_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