Skip to content

Commit 4d8d97b

Browse files
committed
drivers: dma: add ti mspm0 dma driver support
Add DMA driver support for Texas Instruments MSPM0 Family Series. Signed-off-by: Karthikeyan Krishnasamy <[email protected]>
1 parent dd3b113 commit 4d8d97b

File tree

5 files changed

+360
-0
lines changed

5 files changed

+360
-0
lines changed

drivers/dma/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ zephyr_library_sources_ifdef(CONFIG_DMA_XILINX_AXI_DMA dma_xilinx_axi_dma.c)
5353
zephyr_library_sources_ifdef(CONFIG_DMA_NXP_SDMA dma_nxp_sdma.c)
5454
zephyr_library_sources_ifdef(CONFIG_DMA_WCH dma_wch.c)
5555
zephyr_library_sources_ifdef(CONFIG_DMA_TI_CC23X0 dma_ti_cc23x0.c)
56+
zephyr_library_sources_ifdef(CONFIG_DMA_TI_MSPM0 dma_ti_mspm0.c)

drivers/dma/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,6 @@ source "drivers/dma/Kconfig.nxp_sdma"
9696
source "drivers/dma/Kconfig.wch"
9797

9898
source "drivers/dma/Kconfig.ti_cc23x0"
99+
source "drivers/dma/Kconfig.ti_mspm0"
99100

100101
endif # DMA

drivers/dma/Kconfig.ti_mspm0

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright (c) 2025 Linumiz GmbH
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
config DMA_TI_MSPM0
6+
bool "Texas Instruments MSPM0 DMA Controller Driver"
7+
default y
8+
depends on DT_HAS_TI_MSPM0_DMA_ENABLED
9+
select USE_MSPM0_DL_DMA
10+
help
11+
Enable Texas Instruments MSPM0 Family DMA driver.

drivers/dma/dma_ti_mspm0.c

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
/*
2+
* Copyright (c) 2025 Linumiz GmbH
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ti_mspm0_dma
8+
9+
#include <stdio.h>
10+
#include <string.h>
11+
#include <soc.h>
12+
#include <zephyr/kernel.h>
13+
#include <zephyr/device.h>
14+
#include <zephyr/init.h>
15+
#include <zephyr/drivers/dma.h>
16+
#include <zephyr/logging/log.h>
17+
#include <zephyr/irq.h>
18+
#include <driverlib/dl_dma.h>
19+
20+
LOG_MODULE_REGISTER(ti_mspm0_dma, CONFIG_DMA_LOG_LEVEL);
21+
22+
#define DMA_TI_MSPM0_BASE_CHANNEL_NUM 1
23+
24+
/* Data Transfer Width */
25+
#define DMA_TI_MSPM0_DATAWIDTH_BYTE 1
26+
#define DMA_TI_MSPM0_DATAWIDTH_HALF 2
27+
#define DMA_TI_MSPM0_DATAIWDTH_WORD 3
28+
#define DMA_TI_MSPM0_DATAWIDTH_LONG 4
29+
30+
#define DMA_TI_MSPM0_MAX_CHANNEL DT_INST_PROP(0, dma_channels)
31+
BUILD_ASSERT((DMA_TI_MSPM0_MAX_CHANNEL != 0), "dma-channels property required");
32+
33+
struct dma_ti_mspm0_config {
34+
DMA_Regs *regs;
35+
void (*irq_config_func)(void);
36+
};
37+
38+
struct dma_ti_mspm0_channel_data {
39+
dma_callback_t dma_callback;
40+
void *user_data;
41+
uint8_t direction;
42+
bool busy;
43+
};
44+
45+
struct dma_ti_mspm0_data {
46+
struct dma_context dma_ctx;
47+
struct dma_ti_mspm0_channel_data ch_data[DMA_TI_MSPM0_MAX_CHANNEL];
48+
};
49+
50+
static inline int dma_ti_mspm0_get_memory_increment(uint8_t adj,
51+
uint32_t *increment)
52+
{
53+
if (increment == NULL) {
54+
return -EINVAL;
55+
}
56+
57+
switch (adj) {
58+
case DMA_ADDR_ADJ_INCREMENT:
59+
*increment = DL_DMA_ADDR_INCREMENT;
60+
break;
61+
case DMA_ADDR_ADJ_NO_CHANGE:
62+
*increment = DL_DMA_ADDR_UNCHANGED;
63+
break;
64+
case DMA_ADDR_ADJ_DECREMENT:
65+
*increment = DL_DMA_ADDR_DECREMENT;
66+
break;
67+
default:
68+
return -EINVAL;
69+
}
70+
71+
return 0;
72+
}
73+
74+
static inline int dma_ti_mspm0_get_dstdatawidth(uint8_t wd, uint32_t *dwidth)
75+
{
76+
if (dwidth == NULL) {
77+
return -EINVAL;
78+
}
79+
80+
switch (wd) {
81+
case DMA_TI_MSPM0_DATAWIDTH_BYTE:
82+
*dwidth = DL_DMA_WIDTH_BYTE;
83+
break;
84+
case DMA_TI_MSPM0_DATAWIDTH_HALF:
85+
*dwidth = DMA_DMACTL_DMADSTWDTH_HALF;
86+
break;
87+
case DMA_TI_MSPM0_DATAIWDTH_WORD:
88+
*dwidth = DMA_DMACTL_DMADSTWDTH_WORD;
89+
break;
90+
case DMA_TI_MSPM0_DATAWIDTH_LONG:
91+
*dwidth = DMA_DMACTL_DMADSTWDTH_LONG;
92+
break;
93+
default:
94+
return -EINVAL;
95+
}
96+
97+
return 0;
98+
}
99+
100+
static inline int dma_ti_mspm0_get_srcdatawidth(uint8_t wd, uint32_t *dwidth)
101+
{
102+
if (dwidth == NULL) {
103+
return -EINVAL;
104+
}
105+
106+
switch (wd) {
107+
case DMA_TI_MSPM0_DATAWIDTH_BYTE:
108+
*dwidth = DL_DMA_WIDTH_BYTE;
109+
break;
110+
case DMA_TI_MSPM0_DATAWIDTH_HALF:
111+
*dwidth = DMA_DMACTL_DMASRCWDTH_HALF;
112+
break;
113+
case DMA_TI_MSPM0_DATAIWDTH_WORD:
114+
*dwidth = DMA_DMACTL_DMASRCWDTH_WORD;
115+
break;
116+
case DMA_TI_MSPM0_DATAWIDTH_LONG:
117+
*dwidth = DMA_DMACTL_DMASRCWDTH_LONG;
118+
break;
119+
default:
120+
return -EINVAL;
121+
}
122+
123+
return 0;
124+
}
125+
126+
static int dma_ti_mspm0_configure(const struct device *dev, uint32_t channel,
127+
struct dma_config *config)
128+
{
129+
uint32_t temp;
130+
const struct dma_ti_mspm0_config *cfg = dev->config;
131+
struct dma_ti_mspm0_data *dma_data = dev->data;
132+
struct dma_ti_mspm0_channel_data *data = NULL;
133+
struct dma_block_config *b_cfg = config->head_block;
134+
DL_DMA_Config dma_cfg = {0};
135+
136+
if ((config == NULL) || (channel > DMA_TI_MSPM0_MAX_CHANNEL)) {
137+
return -EINVAL;
138+
}
139+
140+
data = &dma_data->ch_data[channel];
141+
142+
if (data->busy != false) {
143+
return -EBUSY;
144+
}
145+
146+
if (config->dest_data_size != config->source_data_size) {
147+
LOG_ERR("Source and Destination data width is not same");
148+
return -EINVAL;
149+
}
150+
151+
if (dma_ti_mspm0_get_memory_increment(b_cfg->source_addr_adj, &temp)) {
152+
LOG_ERR("Invalid Source address increment");
153+
return -EINVAL;
154+
}
155+
156+
dma_cfg.srcIncrement = temp;
157+
158+
if (dma_ti_mspm0_get_memory_increment(b_cfg->dest_addr_adj, &temp)) {
159+
LOG_ERR("Invalid Destination address increment");
160+
return -EINVAL;
161+
}
162+
163+
dma_cfg.destIncrement = temp;
164+
165+
if (dma_ti_mspm0_get_dstdatawidth(config->dest_data_size, &temp)) {
166+
LOG_ERR("Invalid Destination data width");
167+
return -EINVAL;
168+
}
169+
170+
dma_cfg.destWidth = temp;
171+
172+
if (dma_ti_mspm0_get_srcdatawidth(config->source_data_size, &temp)) {
173+
LOG_ERR("Invalid Source data width");
174+
return -EINVAL;
175+
}
176+
177+
dma_cfg.srcWidth = temp;
178+
data->direction = config->channel_direction;
179+
data->dma_callback = config->dma_callback;
180+
data->user_data = config->user_data;
181+
dma_cfg.transferMode = DL_DMA_SINGLE_TRANSFER_MODE,
182+
dma_cfg.extendedMode = DL_DMA_NORMAL_MODE,
183+
dma_cfg.triggerType = DL_DMA_TRIGGER_TYPE_EXTERNAL;
184+
dma_cfg.trigger = config->dma_slot;
185+
186+
DL_DMA_clearInterruptStatus(DMA, channel + DMA_TI_MSPM0_BASE_CHANNEL_NUM);
187+
DL_DMA_setTransferSize(cfg->regs, channel, b_cfg->block_size);
188+
DL_DMA_initChannel(cfg->regs, channel, &dma_cfg);
189+
DL_DMA_setSrcAddr(cfg->regs, channel, b_cfg->source_address);
190+
DL_DMA_setDestAddr(cfg->regs, channel, b_cfg->dest_address);
191+
DL_DMA_enableInterrupt(DMA, channel + DMA_TI_MSPM0_BASE_CHANNEL_NUM);
192+
data->busy = true;
193+
194+
LOG_DBG("DMA Channel %u configured", channel);
195+
196+
return 0;
197+
}
198+
199+
static int dma_ti_mspm0_start(const struct device *dev, const uint32_t channel)
200+
{
201+
const struct dma_ti_mspm0_config *cfg = dev->config;
202+
203+
if (channel > DMA_TI_MSPM0_MAX_CHANNEL) {
204+
return -EINVAL;
205+
}
206+
207+
DL_DMA_enableChannel(cfg->regs, channel);
208+
209+
return 0;
210+
}
211+
212+
static int dma_ti_mspm0_stop(const struct device *dev, const uint32_t channel)
213+
{
214+
const struct dma_ti_mspm0_config *cfg = dev->config;
215+
struct dma_ti_mspm0_data *data = dev->data;
216+
217+
if (channel > DMA_TI_MSPM0_MAX_CHANNEL) {
218+
return -EINVAL;
219+
}
220+
221+
DL_DMA_disableChannel(cfg->regs, channel);
222+
data->ch_data[channel].busy = false;
223+
224+
return 0;
225+
}
226+
227+
static int dma_ti_mspm0_reload(const struct device *dev, uint32_t channel,
228+
uint32_t src_addr, uint32_t dest_addr, size_t size)
229+
{
230+
const struct dma_ti_mspm0_config *cfg = dev->config;
231+
struct dma_ti_mspm0_channel_data *data = NULL;
232+
struct dma_ti_mspm0_data *dma_data = dev->data;
233+
234+
if (channel > DMA_TI_MSPM0_MAX_CHANNEL) {
235+
return -EINVAL;
236+
}
237+
238+
data = &dma_data->ch_data[channel];
239+
switch (data->direction) {
240+
case PERIPHERAL_TO_MEMORY:
241+
DL_DMA_setDestAddr(cfg->regs, channel, dest_addr);
242+
break;
243+
case MEMORY_TO_PERIPHERAL:
244+
DL_DMA_setSrcAddr(cfg->regs, channel, src_addr);
245+
break;
246+
default:
247+
LOG_ERR("Unsupported data direction");
248+
return -ENOTSUP;
249+
}
250+
251+
DL_DMA_setTransferSize(cfg->regs, channel, size);
252+
data->busy = true;
253+
254+
return 0;
255+
}
256+
257+
static int dma_ti_mspm0_get_status(const struct device *dev, uint32_t channel,
258+
struct dma_status *stat)
259+
{
260+
const struct dma_ti_mspm0_config *cfg = dev->config;
261+
struct dma_ti_mspm0_data *dma_data = dev->data;
262+
struct dma_ti_mspm0_channel_data *data;
263+
264+
if (channel > DMA_TI_MSPM0_MAX_CHANNEL) {
265+
return -EINVAL;
266+
}
267+
268+
data = &dma_data->ch_data[channel];
269+
stat->pending_length = DL_DMA_getTransferSize(cfg->regs, channel);
270+
stat->dir = data->direction;
271+
stat->busy = data->busy;
272+
273+
return 0;
274+
}
275+
276+
static inline void dma_ti_mspm0_isr(const struct device *dev)
277+
{
278+
int channel;
279+
const struct dma_ti_mspm0_config *cfg = dev->config;
280+
struct dma_ti_mspm0_data *dma_data = dev->data;
281+
struct dma_ti_mspm0_channel_data *data;
282+
uint32_t key = irq_lock();
283+
284+
channel = DL_DMA_getPendingInterrupt(cfg->regs);
285+
channel -= DMA_TI_MSPM0_BASE_CHANNEL_NUM;
286+
287+
if ((channel < 0) || (channel > DMA_TI_MSPM0_MAX_CHANNEL)) {
288+
goto out;
289+
}
290+
291+
data = &dma_data->ch_data[channel];
292+
DL_DMA_disableChannel(cfg->regs, channel);
293+
294+
if (data->dma_callback != NULL) {
295+
data->busy = false;
296+
data->dma_callback(dev, data->user_data, channel, 0);
297+
}
298+
299+
out:
300+
irq_unlock(key);
301+
}
302+
303+
static int dma_ti_mspm0_init(const struct device *dev)
304+
{
305+
const struct dma_ti_mspm0_config *cfg = dev->config;
306+
307+
if (cfg->irq_config_func != NULL) {
308+
cfg->irq_config_func();
309+
}
310+
311+
return 0;
312+
};
313+
314+
static DEVICE_API(dma, dma_ti_mspm0_api) = {
315+
.config = dma_ti_mspm0_configure,
316+
.start = dma_ti_mspm0_start,
317+
.stop = dma_ti_mspm0_stop,
318+
.reload = dma_ti_mspm0_reload,
319+
.get_status = dma_ti_mspm0_get_status,
320+
};
321+
322+
#define MSPM0_DMA_INIT(inst) \
323+
static inline void dma_ti_mspm0_irq_cfg_##inst(void) \
324+
{ \
325+
irq_disable(DT_INST_IRQN(inst)); \
326+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \
327+
dma_ti_mspm0_isr, DEVICE_DT_INST_GET(inst), 0); \
328+
\
329+
irq_enable(DT_INST_IRQN(inst)); \
330+
} \
331+
\
332+
static const struct dma_ti_mspm0_config dma_cfg_##inst = { \
333+
.regs = (DMA_Regs *)DT_INST_REG_ADDR(inst), \
334+
.irq_config_func = dma_ti_mspm0_irq_cfg_##inst, \
335+
}; \
336+
\
337+
struct dma_ti_mspm0_data dma_data_##inst; \
338+
\
339+
DEVICE_DT_INST_DEFINE(inst, &dma_ti_mspm0_init, NULL, \
340+
&dma_data_##inst, &dma_cfg_##inst, \
341+
PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, \
342+
&dma_ti_mspm0_api);
343+
344+
DT_INST_FOREACH_STATUS_OKAY(MSPM0_DMA_INIT);

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_DMA
19+
bool

0 commit comments

Comments
 (0)