1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2024-08-25     RT-Thread    First version for MCXC444
9  * 2024-09-03     yandld       Updated to support multiple UARTs
10  */
11 
12 #include <rtthread.h>
13 #include <rtdevice.h>
14 #include "drv_uart.h"
15 
16 #include "fsl_lpuart.h"
17 #include "fsl_port.h"
18 #include "fsl_clock.h"
19 
20 #ifdef RT_USING_SERIAL
21 
22 struct mcxc444_uart
23 {
24     LPUART_Type *uart_base;
25     IRQn_Type irqn;
26     struct rt_serial_device *serial;
27     char *device_name;
28 };
29 
30 static void uart_isr(struct rt_serial_device *serial);
31 
32 #define UART_DEVICE(uart_base, irq_name, device_name) \
33     {                                                 \
34         uart_base,                                    \
35         irq_name,                                     \
36         RT_NULL,                                      \
37         device_name,                                  \
38     }
39 
40 static const struct mcxc444_uart uarts[] = {
41 #ifdef BSP_USING_UART0
42     UART_DEVICE(LPUART0, LPUART0_IRQn, "uart0"),
43 #endif
44 #ifdef BSP_USING_UART1
45     UART_DEVICE(LPUART1, LPUART1_IRQn, "uart1"),
46 #endif
47 
48 };
49 
50 #define UART_COUNT (sizeof(uarts) / sizeof(uarts[0]))
51 
52 static struct rt_serial_device serial_devices[UART_COUNT];
53 
mcxc444_configure(struct rt_serial_device * serial,struct serial_configure * cfg)54 static rt_err_t mcxc444_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
55 {
56     struct mcxc444_uart *uart = (struct mcxc444_uart *)serial->parent.user_data;
57     lpuart_config_t config;
58 
59     LPUART_GetDefaultConfig(&config);
60     config.baudRate_Bps = cfg->baud_rate;
61     config.enableTx = true;
62     config.enableRx = true;
63 
64     switch (cfg->data_bits)
65     {
66     case DATA_BITS_8:
67         config.dataBitsCount = kLPUART_EightDataBits;
68         break;
69     default:
70         return RT_ERROR;
71     }
72 
73     switch (cfg->stop_bits)
74     {
75     case STOP_BITS_1:
76         config.stopBitCount = kLPUART_OneStopBit;
77         break;
78     case STOP_BITS_2:
79         config.stopBitCount = kLPUART_TwoStopBit;
80         break;
81     default:
82         return RT_ERROR;
83     }
84 
85     switch (cfg->parity)
86     {
87     case PARITY_NONE:
88         config.parityMode = kLPUART_ParityDisabled;
89         break;
90     case PARITY_ODD:
91         config.parityMode = kLPUART_ParityOdd;
92         break;
93     case PARITY_EVEN:
94         config.parityMode = kLPUART_ParityEven;
95         break;
96     default:
97         return RT_ERROR;
98     }
99 
100     if (uart->uart_base == LPUART0)
101     {
102         CLOCK_SetLpuart0Clock(0x1U);
103     }
104     else if (uart->uart_base == LPUART1)
105     {
106         CLOCK_SetLpuart1Clock(0x1U);
107     }
108 
109     LPUART_Init(uart->uart_base, &config, CLOCK_GetFreq(kCLOCK_McgIrc48MClk));
110 
111     return RT_EOK;
112 }
113 
mcxc444_control(struct rt_serial_device * serial,int cmd,void * arg)114 static rt_err_t mcxc444_control(struct rt_serial_device *serial, int cmd, void *arg)
115 {
116     struct mcxc444_uart *uart = (struct mcxc444_uart *)serial->parent.user_data;
117 
118     switch (cmd)
119     {
120     case RT_DEVICE_CTRL_CLR_INT:
121         LPUART_DisableInterrupts(uart->uart_base, kLPUART_RxDataRegFullInterruptEnable);
122         DisableIRQ(uart->irqn);
123         break;
124     case RT_DEVICE_CTRL_SET_INT:
125         LPUART_EnableInterrupts(uart->uart_base, kLPUART_RxDataRegFullInterruptEnable);
126         EnableIRQ(uart->irqn);
127         break;
128     }
129 
130     return RT_EOK;
131 }
132 
mcxc444_putc(struct rt_serial_device * serial,char ch)133 static int mcxc444_putc(struct rt_serial_device *serial, char ch)
134 {
135     struct mcxc444_uart *uart = (struct mcxc444_uart *)serial->parent.user_data;
136 
137     LPUART_WriteByte(uart->uart_base, ch);
138     while (!(LPUART_GetStatusFlags(uart->uart_base) & kLPUART_TxDataRegEmptyFlag));
139 
140     return 1;
141 }
142 
mcxc444_getc(struct rt_serial_device * serial)143 static int mcxc444_getc(struct rt_serial_device *serial)
144 {
145     int ch;
146     struct mcxc444_uart *uart = (struct mcxc444_uart *)serial->parent.user_data;
147 
148     if (LPUART_GetStatusFlags(uart->uart_base) & kLPUART_RxDataRegFullFlag)
149     {
150         ch = LPUART_ReadByte(uart->uart_base);
151         return ch;
152     }
153     else
154     {
155         return -1;
156     }
157 }
158 
uart_isr(struct rt_serial_device * serial)159 static void uart_isr(struct rt_serial_device *serial)
160 {
161     uint32_t status;
162 
163     struct mcxc444_uart *uart = (struct mcxc444_uart *)serial->parent.user_data;
164 
165     status = LPUART_GetStatusFlags(uart->uart_base);
166 
167     if (status & kLPUART_RxDataRegFullFlag)
168     {
169         rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
170     }
171 
172     if (status & kLPUART_RxOverrunFlag)
173     {
174         LPUART_ClearStatusFlags(uart->uart_base, kLPUART_RxOverrunFlag);
175     }
176 }
177 
178 static const struct rt_uart_ops mcxc444_uart_ops =
179 {
180     mcxc444_configure,
181     mcxc444_control,
182     mcxc444_putc,
183     mcxc444_getc,
184 };
185 
186 #ifdef BSP_USING_UART0
LPUART0_IRQHandler(void)187 void LPUART0_IRQHandler(void)
188 {
189     uart_isr(&serial_devices[0]);
190 }
191 #endif
192 
193 #ifdef BSP_USING_UART1
LPUART1_IRQHandler(void)194 void LPUART1_IRQHandler(void)
195 {
196     uart_isr(&serial_devices[1]);
197 }
198 #endif
199 
rt_hw_uart_init(void)200 int rt_hw_uart_init(void)
201 {
202     struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
203 
204     for (rt_size_t i = 0; i < UART_COUNT; i++)
205     {
206         serial_devices[i].ops = &mcxc444_uart_ops;
207         serial_devices[i].config = config;
208 
209         rt_hw_serial_register(&serial_devices[i], uarts[i].device_name,
210                               RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
211                               (void *)&uarts[i]);
212     }
213 
214     return 0;
215 }
216 
217 INIT_BOARD_EXPORT(rt_hw_uart_init);
218 
219 #endif /* RT_USING_SERIAL */
220