Skip to content

Commit d4b42c4

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 bcf274b commit d4b42c4

File tree

5 files changed

+353
-0
lines changed

5 files changed

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