Skip to content

Commit 0dffe7c

Browse files
Jeppe Odgaardkartben
authored andcommitted
drivers: dac: Add TI DAC161S997 driver
Initial DAC driver for TI DAC161S997. This is a 1 channel 16 bit DAC designed for 4-20 mA loops. Signed-off-by: Jeppe Odgaard <[email protected]>
1 parent da076a9 commit 0dffe7c

File tree

7 files changed

+422
-0
lines changed

7 files changed

+422
-0
lines changed

drivers/dac/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ zephyr_library_sources_ifdef(CONFIG_DAC_MCUX_DAC32 dac_mcux_dac32.c)
1010
zephyr_library_sources_ifdef(CONFIG_DAC_STM32 dac_stm32.c)
1111
zephyr_library_sources_ifdef(CONFIG_DAC_SAM dac_sam.c)
1212
zephyr_library_sources_ifdef(CONFIG_DAC_SAM0 dac_sam0.c)
13+
zephyr_library_sources_ifdef(CONFIG_DAC_DAC161S997 dac_dac161s997.c)
1314
zephyr_library_sources_ifdef(CONFIG_DAC_DACX0501 dac_dacx0501.c)
1415
zephyr_library_sources_ifdef(CONFIG_DAC_DACX0508 dac_dacx0508.c)
1516
zephyr_library_sources_ifdef(CONFIG_DAC_DACX3608 dac_dacx3608.c)

drivers/dac/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ source "drivers/dac/Kconfig.sam"
3737

3838
source "drivers/dac/Kconfig.sam0"
3939

40+
source "drivers/dac/Kconfig.dac161s997"
41+
4042
source "drivers/dac/Kconfig.dacx0501"
4143

4244
source "drivers/dac/Kconfig.dacx0508"

drivers/dac/Kconfig.dac161s997

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2025 Vitrolife A/S
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config DAC_DAC161S997
6+
bool "TI DAC161S997 DAC driver"
7+
default y
8+
select SPI
9+
depends on DT_HAS_TI_DAC161S997_ENABLED
10+
help
11+
Enable the driver for the TI DAC161S997.

drivers/dac/dac_dac161s997.c

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/*
2+
* Copyright (c) 2025 Vitrolife A/S
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ti_dac161s997
8+
9+
#include <zephyr/kernel.h>
10+
#include <zephyr/drivers/gpio.h>
11+
#include <zephyr/drivers/spi.h>
12+
#include <zephyr/drivers/dac.h>
13+
#include <zephyr/drivers/dac/dac161s997.h>
14+
#include <zephyr/logging/log.h>
15+
#include <zephyr/sys/byteorder.h>
16+
#include <zephyr/sys/util_macro.h>
17+
18+
LOG_MODULE_REGISTER(dac_dac161s997, CONFIG_DAC_LOG_LEVEL);
19+
20+
#define DAC161S997_CHANNELS 1
21+
#define DAC161S997_RESOLUTION 16
22+
23+
enum dac161s997_reg {
24+
DAC161S997_REG_XFER = 1,
25+
DAC161S997_REG_NOP,
26+
DAC161S997_REG_WR_MODE,
27+
DAC161S997_REG_DACCODE,
28+
DAC161S997_REG_ERR_CONFIG,
29+
DAC161S997_REG_ERR_LOW,
30+
DAC161S997_REG_ERR_HIGH,
31+
DAC161S997_REG_RESET,
32+
DAC161S997_REG_STATUS,
33+
};
34+
35+
struct dac161s997_config {
36+
struct spi_dt_spec bus;
37+
const struct gpio_dt_spec gpio_errb;
38+
};
39+
40+
struct dac161s997_data {
41+
const struct device *dev;
42+
struct k_sem lock;
43+
struct gpio_callback gpio_errb_cb;
44+
struct k_work gpio_errb_work;
45+
dac161s997_error_callback_t error_cb;
46+
};
47+
48+
int dac161s997_set_error_callback(const struct device *dev, dac161s997_error_callback_t cb)
49+
{
50+
const struct dac161s997_config *config = dev->config;
51+
struct dac161s997_data *data = dev->data;
52+
int ret;
53+
54+
ret = k_sem_take(&data->lock, K_FOREVER);
55+
if (ret != 0) {
56+
return ret;
57+
}
58+
59+
if (config->gpio_errb.port != NULL) {
60+
data->error_cb = cb;
61+
} else {
62+
ret = -ENOTSUP;
63+
}
64+
65+
k_sem_give(&data->lock);
66+
67+
return ret;
68+
}
69+
70+
static int dac161s997_read_reg(const struct device *dev, enum dac161s997_reg reg, uint16_t *val)
71+
{
72+
const struct dac161s997_config *config = dev->config;
73+
int res;
74+
75+
uint8_t reg_read = BIT(7) | reg;
76+
uint8_t tx_buf[3] = {reg_read};
77+
const struct spi_buf tx_buffers[] = {{.buf = tx_buf, .len = ARRAY_SIZE(tx_buf)}};
78+
const struct spi_buf_set tx_bufs = {.buffers = tx_buffers, .count = ARRAY_SIZE(tx_buffers)};
79+
uint8_t rx_buf[3];
80+
const struct spi_buf rx_buffers[] = {{.buf = rx_buf, .len = ARRAY_SIZE(rx_buf)}};
81+
const struct spi_buf_set rx_bufs = {.buffers = rx_buffers, .count = ARRAY_SIZE(rx_buffers)};
82+
83+
res = spi_write_dt(&config->bus, &tx_bufs);
84+
if (res != 0) {
85+
LOG_ERR("Read 0x%02x setup failed: %d", reg, res);
86+
return res;
87+
}
88+
89+
tx_buf[0] = DAC161S997_REG_NOP;
90+
res = spi_transceive_dt(&config->bus, &tx_bufs, &rx_bufs);
91+
if (res != 0) {
92+
LOG_ERR("Read from 0x%02x failed: %d", reg, res);
93+
return res;
94+
}
95+
96+
if (reg_read != rx_buf[0]) {
97+
LOG_ERR("Read 0x%02x addr mismatch: 0x%02x", reg_read, rx_buf[0]);
98+
return -EIO;
99+
}
100+
101+
*val = sys_get_be16(&rx_buf[1]);
102+
103+
LOG_DBG("Reg 0x%02x: 0x%02x", reg, *val);
104+
105+
return 0;
106+
}
107+
108+
static int dac161s997_write_reg(const struct device *dev, enum dac161s997_reg reg, uint16_t val)
109+
{
110+
const struct dac161s997_config *config = dev->config;
111+
112+
uint8_t tx_buf[3] = {reg, val >> 8, val};
113+
const struct spi_buf tx_buffers[] = {{.buf = tx_buf, .len = ARRAY_SIZE(tx_buf)}};
114+
const struct spi_buf_set tx_bufs = {.buffers = tx_buffers, .count = ARRAY_SIZE(tx_buffers)};
115+
116+
int ret = spi_write_dt(&config->bus, &tx_bufs);
117+
118+
if (ret != 0) {
119+
LOG_ERR("Write to reg 0x%02x failed: %i", reg, ret);
120+
return ret;
121+
}
122+
123+
return 0;
124+
}
125+
126+
static int dac161s997_channel_setup(const struct device *dev,
127+
const struct dac_channel_cfg *channel_cfg)
128+
{
129+
if (channel_cfg->channel_id >= DAC161S997_CHANNELS) {
130+
LOG_ERR("Channel %d is not valid", channel_cfg->channel_id);
131+
return -EINVAL;
132+
}
133+
134+
if (channel_cfg->resolution != DAC161S997_RESOLUTION) {
135+
LOG_ERR("Only %d bit resolution is supported", DAC161S997_RESOLUTION);
136+
return -ENOTSUP;
137+
}
138+
139+
if (channel_cfg->internal) {
140+
LOG_ERR("Internal channels not supported");
141+
return -ENOTSUP;
142+
}
143+
144+
return 0;
145+
}
146+
147+
static int dac161s997_write_value(const struct device *dev, uint8_t channel, uint32_t value)
148+
{
149+
struct dac161s997_data *data = dev->data;
150+
int ret;
151+
152+
if (channel >= DAC161S997_CHANNELS) {
153+
LOG_ERR("Channel %d is not valid", channel);
154+
return -EINVAL;
155+
}
156+
157+
if (value > BIT(DAC161S997_RESOLUTION) - 1) {
158+
LOG_ERR("Value %d out of range", value);
159+
return -EINVAL;
160+
}
161+
162+
ret = k_sem_take(&data->lock, K_FOREVER);
163+
if (ret != 0) {
164+
LOG_WRN("Write value lock failed: %d", ret);
165+
return ret;
166+
}
167+
168+
ret = dac161s997_write_reg(dev, DAC161S997_REG_DACCODE, value);
169+
170+
k_sem_give(&data->lock);
171+
172+
return ret;
173+
}
174+
175+
static int dac161s997_read_status(const struct device *dev, union dac161s997_status *status)
176+
{
177+
int ret;
178+
uint16_t tmp;
179+
180+
ret = dac161s997_read_reg(dev, DAC161S997_REG_STATUS, &tmp);
181+
if (ret == 0) {
182+
status->raw = tmp;
183+
}
184+
185+
return ret;
186+
}
187+
188+
static void dac161s997_gpio_errb_work_handler(struct k_work *work)
189+
{
190+
struct dac161s997_data *data = CONTAINER_OF(work, struct dac161s997_data, gpio_errb_work);
191+
const struct device *dev = data->dev;
192+
union dac161s997_status status;
193+
int ret;
194+
195+
ret = k_sem_take(&data->lock, K_FOREVER);
196+
if (ret != 0) {
197+
LOG_WRN("ERRB handler take lock failed: %d", ret);
198+
return;
199+
}
200+
201+
ret = dac161s997_read_status(dev, &status);
202+
203+
if (data->error_cb != NULL) {
204+
data->error_cb(dev, ret == 0 ? &status : NULL);
205+
}
206+
207+
k_sem_give(&data->lock);
208+
}
209+
210+
static void dac161s997_gpio_errb_cb(const struct device *dev, struct gpio_callback *cb,
211+
gpio_port_pins_t pins)
212+
{
213+
struct dac161s997_data *data = CONTAINER_OF(cb, struct dac161s997_data, gpio_errb_cb);
214+
int ret;
215+
216+
ret = k_work_submit(&data->gpio_errb_work);
217+
if (ret != 1) {
218+
LOG_WRN("ERRB work not queued: %d", ret);
219+
}
220+
}
221+
222+
static int dac161s997_init(const struct device *dev)
223+
{
224+
const struct dac161s997_config *config = dev->config;
225+
struct dac161s997_data *data = dev->data;
226+
union dac161s997_status status;
227+
int ret;
228+
229+
data->dev = dev;
230+
231+
if (!spi_is_ready_dt(&config->bus)) {
232+
LOG_ERR("SPI bus %s not ready", config->bus.bus->name);
233+
return -ENODEV;
234+
}
235+
236+
k_sem_init(&data->lock, 1, 1);
237+
238+
ret = dac161s997_write_reg(dev, DAC161S997_REG_RESET, 0xc33c);
239+
if (ret != 0) {
240+
return ret;
241+
}
242+
ret = dac161s997_write_reg(dev, DAC161S997_REG_NOP, 0);
243+
if (ret != 0) {
244+
return ret;
245+
}
246+
247+
/* Read status to clear any sticky error caused during boot or reboot */
248+
ret = dac161s997_read_status(dev, &status);
249+
if (ret != 0) {
250+
return ret;
251+
}
252+
253+
/* Check that DAC_RES bits are all set */
254+
if (status.dac_resolution != 0x7) {
255+
LOG_ERR("Unexpected DAC resolution value: 0x%02x", status.dac_resolution);
256+
return ret;
257+
}
258+
259+
if (config->gpio_errb.port != NULL) {
260+
if (!gpio_is_ready_dt(&config->gpio_errb)) {
261+
LOG_ERR("ERRB GPIO is not ready");
262+
return -ENODEV;
263+
}
264+
265+
k_work_init(&data->gpio_errb_work, dac161s997_gpio_errb_work_handler);
266+
267+
gpio_init_callback(&data->gpio_errb_cb, dac161s997_gpio_errb_cb,
268+
BIT(config->gpio_errb.pin));
269+
270+
ret = gpio_pin_configure_dt(&config->gpio_errb, GPIO_INPUT);
271+
if (ret != 0) {
272+
LOG_ERR("Configure ERRB GPIO failed: %d", ret);
273+
return ret;
274+
}
275+
276+
ret = gpio_pin_interrupt_configure_dt(&config->gpio_errb, GPIO_INT_EDGE_TO_ACTIVE);
277+
if (ret) {
278+
LOG_ERR("Configure ERRB interrupt failed: %d", ret);
279+
return ret;
280+
}
281+
282+
ret = gpio_add_callback_dt(&config->gpio_errb, &data->gpio_errb_cb);
283+
if (ret != 0) {
284+
LOG_ERR("Configure ERRB callback failed: %d", ret);
285+
return ret;
286+
}
287+
}
288+
289+
return 0;
290+
}
291+
292+
static DEVICE_API(dac, dac161s997_driver_api) = {
293+
.channel_setup = dac161s997_channel_setup,
294+
.write_value = dac161s997_write_value,
295+
};
296+
297+
#define DAC_DAC161S997_INIT(n) \
298+
static const struct dac161s997_config dac161s997_config_##n = { \
299+
.bus = SPI_DT_SPEC_INST_GET(n, SPI_TRANSFER_MSB | SPI_WORD_SET(8), 0), \
300+
.gpio_errb = GPIO_DT_SPEC_INST_GET_OR(n, errb_gpios, {0}), \
301+
}; \
302+
\
303+
static struct dac161s997_data dac161s997_data_##n; \
304+
\
305+
DEVICE_DT_INST_DEFINE(n, dac161s997_init, NULL, &dac161s997_data_##n, \
306+
&dac161s997_config_##n, POST_KERNEL, CONFIG_DAC_INIT_PRIORITY, \
307+
&dac161s997_driver_api);
308+
309+
DT_INST_FOREACH_STATUS_OKAY(DAC_DAC161S997_INIT);

dts/bindings/dac/ti,dac161s997.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright (C) 2025 Vitrolife A/S
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: TI DAC161S997 16-bit 1 channel SPI DAC for 4-20 mA loops
5+
6+
compatible: "ti,dac161s997"
7+
8+
include: [dac-controller.yaml, spi-device.yaml]
9+
10+
properties:
11+
errb-gpios:
12+
type: phandle-array
13+
description: |
14+
DAC error signal output. If set a callback can be set to read what caused the error.

0 commit comments

Comments
 (0)