Skip to content

Commit b752c4e

Browse files
committed
drivers: dac: Add driver support for TI MSPM0 G-Series DAC module
Add support for the DAC module on TI’s MSPM0 G-Series MCUs. The DAC supports 8-bit and 12-bit resolution, with configurable input data format (binary or two’s complement). Signed-off-by: Santhosh Charles <[email protected]>
1 parent f07f9c0 commit b752c4e

File tree

5 files changed

+250
-0
lines changed

5 files changed

+250
-0
lines changed

drivers/dac/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ zephyr_library_sources_ifdef(CONFIG_DAC_MCUX_GAU dac_mcux_gau.c)
3030
zephyr_library_sources_ifdef(CONFIG_DAC_TEST dac_test.c)
3131
zephyr_library_sources_ifdef(CONFIG_DAC_MAX22017 dac_max22017.c)
3232
zephyr_library_sources_ifdef(CONFIG_DAC_RENESAS_RA dac_renesas_ra.c)
33+
zephyr_library_sources_ifdef(CONFIG_DAC_MSPM0 dac_mspm0.c)

drivers/dac/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,6 @@ source "drivers/dac/Kconfig.renesas_ra"
6969

7070
source "drivers/dac/Kconfig.samd5x"
7171

72+
source "drivers/dac/Kconfig.mspm0"
73+
7274
endif # DAC

drivers/dac/Kconfig.mspm0

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# TI MSPM0 DAC Driver Configuration
2+
3+
# Copyright (c) 2025 Linumiz GmbH
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config DAC_MSPM0
7+
bool "TI MSPM0 Digital To Analog Converter (DAC) Driver"
8+
default y
9+
depends on DT_HAS_TI_MSPM0_DAC_ENABLED
10+
select USE_MSPM0_DL_DAC12
11+
help
12+
Enable support for the DAC (Digital-to-Analog Converter)
13+
peripheral driver for Texas Instruments MSPM0 G-Series MCUs.
14+
15+
config DAC_MSPM0_DFM_TWOS_COMPLEMENT
16+
bool "Select two's complement as input data format"
17+
default n
18+
depends on DAC_MSPM0
19+
help
20+
Enable two's complement representation for DAC input
21+
data. If disabled, binary representation will be used.
22+
23+
config DAC_MSPM0_TIMEOUT_MS
24+
int "DAC MSPM0 module ready timeout in milliseconds"
25+
default 50
26+
help
27+
Timeout in milliseconds to wait for the DAC module to be
28+
ready after enabling.

drivers/dac/dac_mspm0.c

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Copyright (c) 2025 Linumiz GmbH
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ti_mspm0_dac
8+
9+
#include <zephyr/device.h>
10+
#include <zephyr/devicetree.h>
11+
#include <zephyr/drivers/dac.h>
12+
#include <zephyr/irq.h>
13+
#include <zephyr/kernel.h>
14+
15+
/* TI Driverlib includes */
16+
#include <ti/devices/msp/peripherals/hw_dac12.h>
17+
#include <ti/driverlib/dl_dac12.h>
18+
19+
/* 12-bit DAC Binary Representation Range */
20+
#define DAC12_BINARY_MAX 4095
21+
#define DAC12_BINARY_MIN 0
22+
23+
/* 12-bit DAC Two's Complement Representation Range */
24+
#define DAC12_TWOS_COMP_MAX 2047
25+
#define DAC12_TWOS_COMP_MIN (-2048)
26+
27+
/* 8-bit DAC Binary Representation Range */
28+
#define DAC8_BINARY_MAX 255
29+
#define DAC8_BINARY_MIN 0
30+
31+
/* 8-bit DAC Two's Complement Representation Range */
32+
#define DAC8_TWOS_COMP_MAX 127
33+
#define DAC8_TWOS_COMP_MIN (-128)
34+
35+
/* Timeout to wait until the DAC module is ready after enabling */
36+
#define DAC_MOD_RDY_TIMEOUT K_MSEC(CONFIG_DAC_MSPM0_TIMEOUT_MS)
37+
38+
struct dac_mspm0_config {
39+
DAC12_Regs *dac_base;
40+
DL_DAC12_REPRESENTATION dac_repr;
41+
void (*irq_config_func)(const struct device *dev);
42+
};
43+
44+
struct dac_mspm0_data {
45+
uint8_t resolution;
46+
struct k_sem mod_rdy;
47+
};
48+
49+
static void dac_mspm0_isr(const struct device *dev)
50+
{
51+
const struct dac_mspm0_config *config = dev->config;
52+
struct dac_mspm0_data *data = dev->data;
53+
DAC12_Regs *dac12 = config->dac_base;
54+
55+
if (DL_DAC12_getPendingInterrupt(dac12) == DL_DAC12_IIDX_MODULE_READY) {
56+
k_sem_give(&data->mod_rdy);
57+
}
58+
}
59+
60+
static int dac_mspm0_channel_setup(const struct device *dev,
61+
const struct dac_channel_cfg *channel_cfg)
62+
{
63+
const struct dac_mspm0_config *config = dev->config;
64+
struct dac_mspm0_data *data = dev->data;
65+
DAC12_Regs *dac12 = config->dac_base;
66+
67+
if (channel_cfg->channel_id != 0) {
68+
return -EINVAL;
69+
}
70+
71+
if (channel_cfg->resolution != 8 && channel_cfg->resolution != 12) {
72+
return -ENOTSUP;
73+
}
74+
75+
k_sem_reset(&data->mod_rdy);
76+
77+
/* Ensure DAC is disabled before configuration */
78+
if (DL_DAC12_isEnabled(dac12)) {
79+
DL_DAC12_disable(dac12);
80+
}
81+
82+
DL_DAC12_configDataFormat(dac12, config->dac_repr,
83+
(channel_cfg->resolution == 12) ? DL_DAC12_RESOLUTION_12BIT
84+
: DL_DAC12_RESOLUTION_8BIT);
85+
86+
/* buffered must be true to enable amplifier for output drive */
87+
DL_DAC12_setAmplifier(dac12,
88+
(channel_cfg->buffered) ? DL_DAC12_AMP_ON : DL_DAC12_AMP_OFF_0V);
89+
90+
DL_DAC12_setReferenceVoltageSource(dac12, DL_DAC12_VREF_SOURCE_VDDA_VSSA);
91+
92+
/* internal must be true to route output to OPA, ADC, COMP and DAC_OUT pin */
93+
if (channel_cfg->internal) {
94+
DL_DAC12_enableOutputPin(dac12);
95+
} else {
96+
DL_DAC12_disableOutputPin(dac12);
97+
}
98+
99+
DL_DAC12_enable(dac12);
100+
if (!DL_DAC12_isEnabled(dac12)) {
101+
return -EAGAIN;
102+
}
103+
104+
if (k_sem_take(&data->mod_rdy, DAC_MOD_RDY_TIMEOUT) != 0) {
105+
return -ETIMEDOUT;
106+
}
107+
108+
data->resolution = channel_cfg->resolution;
109+
DL_DAC12_performSelfCalibrationBlocking(dac12);
110+
111+
return 0;
112+
}
113+
114+
static int dac_mspm0_write_value(const struct device *dev, uint8_t channel, uint32_t value)
115+
{
116+
const struct dac_mspm0_config *config = dev->config;
117+
struct dac_mspm0_data *data = dev->data;
118+
DAC12_Regs *dac12 = config->dac_base;
119+
120+
if (!DL_DAC12_isEnabled(dac12)) {
121+
return -EAGAIN;
122+
}
123+
124+
if (channel != 0) {
125+
return -EINVAL;
126+
}
127+
128+
if (data->resolution == 12) {
129+
if ((config->dac_repr == DL_DAC12_REPRESENTATION_BINARY) &&
130+
(value > DAC12_BINARY_MAX || value < DAC12_BINARY_MIN)) {
131+
return -EINVAL;
132+
}
133+
134+
if ((config->dac_repr == DL_DAC12_REPRESENTATION_TWOS_COMPLEMENT) &&
135+
((int32_t)value > DAC12_TWOS_COMP_MAX ||
136+
(int32_t)value < DAC12_TWOS_COMP_MIN)) {
137+
return -EINVAL;
138+
}
139+
140+
DL_DAC12_output12(dac12, value);
141+
142+
} else if (data->resolution == 8) {
143+
if ((config->dac_repr == DL_DAC12_REPRESENTATION_BINARY) &&
144+
(value > DAC8_BINARY_MAX || value < DAC8_BINARY_MIN)) {
145+
return -EINVAL;
146+
}
147+
148+
if ((config->dac_repr == DL_DAC12_REPRESENTATION_TWOS_COMPLEMENT) &&
149+
((int32_t)value > DAC8_TWOS_COMP_MAX || (int32_t)value < DAC8_TWOS_COMP_MIN)) {
150+
return -EINVAL;
151+
}
152+
153+
DL_DAC12_output8(dac12, (uint8_t)value);
154+
155+
} else {
156+
/* Setup must be done with valid resolution */
157+
return -EINVAL;
158+
}
159+
160+
return 0;
161+
}
162+
163+
static int dac_mspm0_init(const struct device *dev)
164+
{
165+
const struct dac_mspm0_config *config = dev->config;
166+
struct dac_mspm0_data *data = dev->data;
167+
DAC12_Regs *dac12 = config->dac_base;
168+
169+
DL_DAC12_enablePower(dac12);
170+
if (!DL_DAC12_isPowerEnabled(dac12)) {
171+
return -EAGAIN;
172+
}
173+
174+
DL_DAC12_disableInterrupt(dac12, DL_DAC12_INTERRUPT_MODULE_READY);
175+
DL_DAC12_clearInterruptStatus(dac12, DL_DAC12_INTERRUPT_MODULE_READY);
176+
177+
config->irq_config_func(dev);
178+
DL_DAC12_enableInterrupt(dac12, DL_DAC12_INTERRUPT_MODULE_READY);
179+
180+
return 0;
181+
}
182+
183+
static DEVICE_API(dac, dac_mspm0_driver_api) = {
184+
.channel_setup = dac_mspm0_channel_setup,
185+
.write_value = dac_mspm0_write_value
186+
};
187+
188+
#define DAC_MSPM0_DEFINE(id) \
189+
\
190+
static void dac_mspm0_irq_config_##id(const struct device *dev) \
191+
{ \
192+
IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), dac_mspm0_isr, \
193+
DEVICE_DT_INST_GET(id), 0); \
194+
irq_enable(DT_INST_IRQN(id)); \
195+
}; \
196+
\
197+
static const struct dac_mspm0_config dac_mspm0_config_##id = { \
198+
.dac_base = (DAC12_Regs *)DT_INST_REG_ADDR(id), \
199+
\
200+
.dac_repr = IS_ENABLED(CONFIG_DAC_MSPM0_DFM_TWOS_COMPLEMENT) \
201+
? DL_DAC12_REPRESENTATION_TWOS_COMPLEMENT \
202+
: DL_DAC12_REPRESENTATION_BINARY, \
203+
.irq_config_func = dac_mspm0_irq_config_##id, \
204+
}; \
205+
\
206+
static struct dac_mspm0_data dac_mspm0_data_##id = { \
207+
/* Config at channel setup */ \
208+
.resolution = 0, \
209+
.mod_rdy = Z_SEM_INITIALIZER(dac_mspm0_data_##id.mod_rdy, 0, 1), \
210+
}; \
211+
\
212+
DEVICE_DT_INST_DEFINE(id, &dac_mspm0_init, NULL, &dac_mspm0_data_##id, \
213+
&dac_mspm0_config_##id, POST_KERNEL, CONFIG_DAC_INIT_PRIORITY, \
214+
&dac_mspm0_driver_api);
215+
216+
DT_INST_FOREACH_STATUS_OKAY(DAC_MSPM0_DEFINE)

modules/Kconfig.mspm0

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ config USE_MSPM0_DL_UART
1414

1515
config USE_MSPM0_DL_TIMER
1616
bool
17+
18+
config USE_MSPM0_DL_DAC12
19+
bool

0 commit comments

Comments
 (0)