Skip to content

Commit 8d0d1fa

Browse files
committed
drivers: added serial driver for MSPM0
added the Serial (uart) driver for MSPM0 family Signed-off-by: Jackson Farley <[email protected]>
1 parent cf5480f commit 8d0d1fa

File tree

5 files changed

+356
-0
lines changed

5 files changed

+356
-0
lines changed

drivers/serial/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_MCUX_LPSCI uart_mcux_lpsci.c)
4343
zephyr_library_sources_ifdef(CONFIG_UART_MCUX_LPUART uart_mcux_lpuart.c)
4444
zephyr_library_sources_ifdef(CONFIG_UART_MIV uart_miv.c)
4545
zephyr_library_sources_ifdef(CONFIG_UART_MSP432P4XX uart_msp432p4xx.c)
46+
zephyr_library_sources_ifdef(CONFIG_UART_MSPM0 uart_mspm0.c)
4647
zephyr_library_sources_ifdef(CONFIG_UART_NEORV32 uart_neorv32.c)
4748
zephyr_library_sources_ifdef(CONFIG_UART_NPCX uart_npcx.c)
4849
zephyr_library_sources_ifdef(CONFIG_UART_NRFX_UART uart_nrfx_uart.c)

drivers/serial/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ rsource "Kconfig.mcux_lpsci"
183183
rsource "Kconfig.mcux_lpuart"
184184
rsource "Kconfig.miv"
185185
rsource "Kconfig.msp432p4xx"
186+
rsource "Kconfig.mspm0"
186187
rsource "Kconfig.native_posix"
187188
rsource "Kconfig.native_tty"
188189
rsource "Kconfig.neorv32"

drivers/serial/Kconfig.mspm0

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright (c) 2024 Texas Instruments
3+
4+
config UART_MSPM0
5+
bool "MSPM0 UART driver"
6+
default y
7+
depends on DT_HAS_TI_MSPM0_UART_ENABLED
8+
select SERIAL_HAS_DRIVER
9+
select SERIAL_SUPPORT_INTERRUPT
10+
select PINCTRL
11+
help
12+
This option enables the TI MSPM0 UART driver.

drivers/serial/uart_mspm0.c

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
/*
2+
* Copyright (c) 2024 Texas Instruments
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#define DT_DRV_COMPAT ti_mspm0_uart
8+
9+
/* Zephyr includes */
10+
#include <zephyr/kernel.h>
11+
#include <zephyr/drivers/pinctrl.h>
12+
#include <zephyr/drivers/uart.h>
13+
#include <zephyr/drivers/clock_control.h>
14+
#include <zephyr/drivers/clock_control/mspm0_clock_control.h>
15+
#include <zephyr/irq.h>
16+
#include <soc.h>
17+
18+
/* Driverlib includes */
19+
#include <ti/driverlib/dl_uart_main.h>
20+
21+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
22+
#define UART_MSPM0_TX_INTERRUPTS (DL_UART_MAIN_INTERRUPT_TX | DL_UART_MAIN_INTERRUPT_EOT_DONE)
23+
#define UART_MSPM0_RX_INTERRUPTS (DL_UART_MAIN_INTERRUPT_RX)
24+
#endif
25+
26+
struct uart_mspm0_config {
27+
UART_Regs *regs;
28+
uint32_t clock_frequency;
29+
uint32_t current_speed;
30+
const struct mspm0_clockSys *clock_subsys;
31+
const struct pinctrl_dev_config *pinctrl;
32+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
33+
uart_irq_config_func_t irq_config_func;
34+
#endif
35+
};
36+
37+
struct uart_mspm0_data {
38+
/* UART clock structure */
39+
DL_UART_Main_ClockConfig clock_config;
40+
/* UART config structure */
41+
DL_UART_Main_Config config;
42+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
43+
uint32_t interruptState; /* Masked Interrupt Status when called by irq_update */
44+
uart_irq_callback_user_data_t cb; /* Callback function pointer */
45+
void *cb_data; /* Callback function arg */
46+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
47+
};
48+
49+
50+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
51+
static void uart_mspm0_isr(const struct device *dev);
52+
#define MSPM0_INTERRUPT_CALLBACK_FN(index) .cb = NULL,
53+
#else
54+
#define MSPM0_INTERRUPT_CALLBACK_FN(index)
55+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
56+
57+
static int uart_mspm0_init(const struct device *dev)
58+
{
59+
const struct uart_mspm0_config *config = dev->config;
60+
struct uart_mspm0_data *data = dev->data;
61+
const struct device *const clk_dev = DEVICE_DT_GET(DT_NODELABEL(clkmux));
62+
uint32_t clock_rate;
63+
int ret;
64+
65+
/* Reset power */
66+
DL_UART_Main_reset(config->regs);
67+
DL_UART_Main_enablePower(config->regs);
68+
delay_cycles(POWER_STARTUP_DELAY);
69+
70+
/* Init UART pins */
71+
ret = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT);
72+
if (ret < 0) {
73+
return ret;
74+
}
75+
76+
/* Set UART configs */
77+
DL_UART_Main_setClockConfig(config->regs,
78+
(DL_UART_Main_ClockConfig *)&data->clock_config);
79+
DL_UART_Main_init(config->regs, (DL_UART_Main_Config *)&data->config);
80+
81+
/*
82+
* Configure baud rate by setting oversampling and baud rate divisor
83+
* from the device tree data current-speed
84+
*/
85+
ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t)config->clock_subsys,
86+
&clock_rate);
87+
88+
if (ret < 0) {
89+
return ret;
90+
}
91+
92+
DL_UART_Main_configBaudRate(config->regs, clock_rate, config->current_speed);
93+
94+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
95+
config->irq_config_func(dev);
96+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
97+
98+
/* Enable UART */
99+
DL_UART_Main_enable(config->regs);
100+
101+
return 0;
102+
}
103+
104+
static int uart_mspm0_poll_in(const struct device *dev, unsigned char *c)
105+
{
106+
const struct uart_mspm0_config *config = dev->config;
107+
108+
return (DL_UART_Main_receiveDataCheck(config->regs, c)) ? 0 : -1;
109+
}
110+
111+
static void uart_mspm0_poll_out(const struct device *dev, unsigned char c)
112+
{
113+
const struct uart_mspm0_config *config = dev->config;
114+
115+
DL_UART_Main_transmitDataBlocking(config->regs, c);
116+
}
117+
118+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
119+
120+
static int uart_mspm0_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
121+
{
122+
const struct uart_mspm0_config *config = dev->config;
123+
124+
return (int)DL_UART_Main_fillTXFIFO(config->regs, (uint8_t *)tx_data, size);
125+
}
126+
127+
static int uart_mspm0_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
128+
{
129+
const struct uart_mspm0_config *config = dev->config;
130+
131+
return (int)DL_UART_Main_drainRXFIFO(config->regs, rx_data, size);
132+
}
133+
134+
static void uart_mspm0_irq_rx_enable(const struct device *dev)
135+
{
136+
const struct uart_mspm0_config *config = dev->config;
137+
138+
DL_UART_Main_enableInterrupt(config->regs, UART_MSPM0_RX_INTERRUPTS);
139+
}
140+
141+
static void uart_mspm0_irq_rx_disable(const struct device *dev)
142+
{
143+
const struct uart_mspm0_config *config = dev->config;
144+
145+
DL_UART_Main_disableInterrupt(config->regs, UART_MSPM0_RX_INTERRUPTS);
146+
}
147+
148+
static int uart_mspm0_irq_rx_ready(const struct device *dev)
149+
{
150+
struct uart_mspm0_data *const dev_data = dev->data;
151+
152+
return ((dev_data->interruptState & DL_UART_MAIN_INTERRUPT_RX) == DL_UART_MAIN_INTERRUPT_RX)
153+
? 1
154+
: 0;
155+
}
156+
157+
static void uart_mspm0_irq_tx_enable(const struct device *dev)
158+
{
159+
const struct uart_mspm0_config *config = dev->config;
160+
161+
DL_UART_Main_enableInterrupt(config->regs, UART_MSPM0_TX_INTERRUPTS);
162+
}
163+
164+
static void uart_mspm0_irq_tx_disable(const struct device *dev)
165+
{
166+
const struct uart_mspm0_config *config = dev->config;
167+
168+
DL_UART_Main_disableInterrupt(config->regs, UART_MSPM0_TX_INTERRUPTS);
169+
}
170+
171+
static int uart_mspm0_irq_tx_ready(const struct device *dev)
172+
{
173+
const struct uart_mspm0_config *config = dev->config;
174+
struct uart_mspm0_data *const dev_data = dev->data;
175+
176+
return (((dev_data->interruptState & DL_UART_MAIN_INTERRUPT_TX) ==
177+
DL_UART_MAIN_INTERRUPT_TX) ||
178+
DL_UART_Main_isTXFIFOEmpty(config->regs))
179+
? 1
180+
: 0;
181+
}
182+
183+
static int uart_mspm0_irq_tx_complete(const struct device *dev)
184+
{
185+
struct uart_mspm0_data *const dev_data = dev->data;
186+
187+
return ((dev_data->interruptState & DL_UART_MAIN_INTERRUPT_EOT_DONE) ==
188+
DL_UART_MAIN_INTERRUPT_EOT_DONE)
189+
? 1
190+
: 0;
191+
}
192+
193+
static int uart_mspm0_irq_is_pending(const struct device *dev)
194+
{
195+
struct uart_mspm0_data *const dev_data = dev->data;
196+
197+
return ((dev_data->interruptState != 0) ? 1 : 0);
198+
}
199+
200+
static int uart_mspm0_irq_update(const struct device *dev)
201+
{
202+
const struct uart_mspm0_config *config = dev->config;
203+
struct uart_mspm0_data *const dev_data = dev->data;
204+
205+
dev_data->interruptState = DL_UART_Main_getEnabledInterruptStatus(
206+
config->regs, UART_MSPM0_RX_INTERRUPTS | UART_MSPM0_TX_INTERRUPTS);
207+
208+
/*
209+
* Clear interrupts explicitly after storing all in the update. Interrupts
210+
* can be re-set by the MIS during the ISR should they be available.
211+
*/
212+
213+
DL_UART_Main_clearInterruptStatus(config->regs, dev_data->interruptState);
214+
215+
return 1;
216+
}
217+
218+
static void uart_mspm0_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
219+
void *cb_data)
220+
{
221+
struct uart_mspm0_data *const dev_data = dev->data;
222+
223+
/* Set callback function and data */
224+
dev_data->cb = cb;
225+
dev_data->cb_data = cb_data;
226+
}
227+
228+
/**
229+
* @brief Interrupt service routine.
230+
*
231+
* This simply calls the callback function, if one exists.
232+
*
233+
* @param arg Argument to ISR.
234+
*/
235+
static void uart_mspm0_isr(const struct device *dev)
236+
{
237+
const struct uart_mspm0_config *config = dev->config;
238+
struct uart_mspm0_data *const dev_data = dev->data;
239+
uint32_t int_status;
240+
241+
/* Perform callback if defined */
242+
if (dev_data->cb) {
243+
dev_data->cb(dev, dev_data->cb_data);
244+
} else {
245+
/* error, callback necessary in order to make progress. Clear interrupts
246+
* temporarily.
247+
*/
248+
int_status = DL_UART_Main_getEnabledInterruptStatus(
249+
config->regs, UART_MSPM0_TX_INTERRUPTS | UART_MSPM0_RX_INTERRUPTS);
250+
DL_UART_Main_clearInterruptStatus(config->regs, int_status);
251+
}
252+
}
253+
254+
#define MSP_UART_IRQ_REGISTER(index) \
255+
static void uart_mspm0_##index##_irq_register(const struct device *dev) \
256+
{ \
257+
IRQ_CONNECT(DT_INST_IRQN(index), DT_INST_IRQ(index, priority), uart_mspm0_isr, \
258+
DEVICE_DT_INST_GET(index), 0); \
259+
irq_enable(DT_INST_IRQN(index)); \
260+
}
261+
#else
262+
263+
#define MSP_UART_IRQ_REGISTER(index)
264+
265+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
266+
267+
static const struct uart_driver_api uart_mspm0_driver_api = {
268+
.poll_in = uart_mspm0_poll_in,
269+
.poll_out = uart_mspm0_poll_out,
270+
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
271+
.fifo_fill = uart_mspm0_fifo_fill,
272+
.fifo_read = uart_mspm0_fifo_read,
273+
.irq_tx_enable = uart_mspm0_irq_tx_enable,
274+
.irq_tx_disable = uart_mspm0_irq_tx_disable,
275+
.irq_tx_ready = uart_mspm0_irq_tx_ready,
276+
.irq_rx_enable = uart_mspm0_irq_rx_enable,
277+
.irq_rx_disable = uart_mspm0_irq_rx_disable,
278+
.irq_tx_complete = uart_mspm0_irq_tx_complete,
279+
.irq_rx_ready = uart_mspm0_irq_rx_ready,
280+
.irq_is_pending = uart_mspm0_irq_is_pending,
281+
.irq_update = uart_mspm0_irq_update,
282+
.irq_callback_set = uart_mspm0_irq_callback_set,
283+
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
284+
};
285+
286+
#define MSPM0_UART_INIT_FN(index) \
287+
\
288+
PINCTRL_DT_INST_DEFINE(index); \
289+
\
290+
static const struct mspm0_clockSys mspm0_uart_clockSys##index = \
291+
MSPM0_CLOCK_SUBSYS_FN(index); \
292+
\
293+
MSP_UART_IRQ_REGISTER(index) \
294+
\
295+
static const struct uart_mspm0_config uart_mspm0_cfg_##index = { \
296+
.regs = (UART_Regs *)DT_INST_REG_ADDR(index), \
297+
.current_speed = DT_INST_PROP(index, current_speed), \
298+
.pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \
299+
.clock_subsys = &mspm0_uart_clockSys##index, \
300+
IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, \
301+
(.irq_config_func = uart_mspm0_##index##_irq_register,)) }; \
302+
\
303+
static struct uart_mspm0_data uart_mspm0_data_##index = { \
304+
.clock_config = {.clockSel = (DT_INST_CLOCKS_CELL(index, bus) & \
305+
MSPM0_CLOCK_SEL_MASK), \
306+
.divideRatio = DL_UART_MAIN_CLOCK_DIVIDE_RATIO_1}, \
307+
.config = \
308+
{ \
309+
.mode = DL_UART_MAIN_MODE_NORMAL, \
310+
.direction = DL_UART_MAIN_DIRECTION_TX_RX, \
311+
.flowControl = (DT_INST_PROP(index, hw_flow_control) \
312+
? DL_UART_MAIN_FLOW_CONTROL_RTS_CTS \
313+
: DL_UART_MAIN_FLOW_CONTROL_NONE), \
314+
.parity = DL_UART_MAIN_PARITY_NONE, \
315+
.wordLength = DL_UART_MAIN_WORD_LENGTH_8_BITS, \
316+
.stopBits = DL_UART_MAIN_STOP_BITS_ONE, \
317+
}, \
318+
MSPM0_INTERRUPT_CALLBACK_FN(index)}; \
319+
\
320+
DEVICE_DT_INST_DEFINE(index, &uart_mspm0_init, NULL, &uart_mspm0_data_##index, \
321+
&uart_mspm0_cfg_##index, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
322+
&uart_mspm0_driver_api);
323+
324+
DT_INST_FOREACH_STATUS_OKAY(MSPM0_UART_INIT_FN)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
description: TI MSPM0 UART
2+
3+
compatible: "ti,mspm0-uart"
4+
5+
include: [uart-controller.yaml, pinctrl-device.yaml, base.yaml]
6+
7+
properties:
8+
reg:
9+
required: true
10+
11+
interrupts:
12+
required: true
13+
14+
pinctrl-0:
15+
required: true
16+
17+
pinctrl-names:
18+
required: true

0 commit comments

Comments
 (0)