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  * 2019-07-15     yandld      The first version for MCXN
9  */
10 
11 #include <rtthread.h>
12 #include "drv_uart.h"
13 
14 #include "fsl_lpuart.h"
15 #include "fsl_common.h"
16 
17 #ifdef RT_USING_SERIAL
18 
19 
20 
21 #include <rtdevice.h>
22 
23 /* lpc uart driver */
24 struct mcx_uart
25 {
26     struct rt_serial_device     *serial;  /* Select serial device */
27     LPUART_Type                 *uart_base; /* serial base */
28     IRQn_Type                   irqn; /* serial interrupt */
29     clock_name_t                clock_src; /* serial RTC */
30     clock_attach_id_t           clock_attach_id; /* RTC ID */
31     clock_ip_name_t             clock_ip_name; /* serial clock name */
32     clock_div_name_t            clock_div_name; /* serial clock div */
33     char *device_name; /* serial device name */
34 };
35 
36 static void uart_isr(struct rt_serial_device *serial);
37 
38 #if defined(BSP_USING_UART2)
39 struct rt_serial_device serial2;
40 
LP_FLEXCOMM2_IRQHandler(void)41 void LP_FLEXCOMM2_IRQHandler(void)
42 {
43     uart_isr(&serial2); /* Serial interrupt handling function */
44 }
45 #endif /* BSP_USING_UART2 */
46 
47 #if defined(BSP_USING_UART4)
48 struct rt_serial_device serial4;
49 
LP_FLEXCOMM4_IRQHandler(void)50 void LP_FLEXCOMM4_IRQHandler(void)
51 {
52     uart_isr(&serial4); /* Serial interrupt handling function */
53 }
54 #endif /* BSP_USING_UART4 */
55 
56 #if defined(BSP_USING_UART5)
57 struct rt_serial_device serial5;
58 
LP_FLEXCOMM5_IRQHandler(void)59 void LP_FLEXCOMM5_IRQHandler(void)
60 {
61     uart_isr(&serial5); /* Serial interrupt handling function */
62 }
63 #endif /* BSP_USING_UART5 */
64 
65 #if defined(BSP_USING_UART6) /* same UART4 */
66 struct rt_serial_device serial6;
67 
LP_FLEXCOMM6_IRQHandler(void)68 void LP_FLEXCOMM6_IRQHandler(void)
69 {
70     uart_isr(&serial6);
71 }
72 #endif /* BSP_USING_UART6 */
73 
74 static const struct mcx_uart uarts[] = /* Initializes the above structure */
75 {
76 #ifdef BSP_USING_UART2
77     {
78         &serial2,
79         LPUART2,
80         LP_FLEXCOMM2_IRQn,
81         kCLOCK_Fro12M,
82         kFRO12M_to_FLEXCOMM2,
83         kCLOCK_LPFlexComm2,
84         kCLOCK_DivFlexcom2Clk,
85         "uart2",
86     },
87 #endif
88 #ifdef BSP_USING_UART4
89     {
90         &serial4,
91         LPUART4,
92         LP_FLEXCOMM4_IRQn,
93         kCLOCK_Fro12M,
94         kFRO12M_to_FLEXCOMM4,
95         kCLOCK_LPFlexComm4,
96         kCLOCK_DivFlexcom4Clk,
97         "uart4",
98     },
99 #endif
100 #ifdef BSP_USING_UART5
101     {
102         &serial5,
103         LPUART5,
104         LP_FLEXCOMM5_IRQn,
105         kCLOCK_Fro12M,
106         kFRO12M_to_FLEXCOMM5,
107         kCLOCK_LPFlexComm5,
108         kCLOCK_DivFlexcom5Clk,
109         "uart5",
110     },
111 #endif
112 #ifdef BSP_USING_UART6
113     {
114         &serial6,
115         LPUART6,
116         LP_FLEXCOMM6_IRQn,
117         kCLOCK_Fro12M,
118         kFRO12M_to_FLEXCOMM6,
119         kCLOCK_LPFlexComm6,
120         kCLOCK_DivFlexcom6Clk,
121         "uart6",
122     },
123 #endif
124 };
125 
126 /**
127  * Configuring the serial port Module.
128  *
129  * @param serial device
130  * @param Configure the serial port configuration structure to set the TX RX features
131  */
mcx_configure(struct rt_serial_device * serial,struct serial_configure * cfg)132 static rt_err_t mcx_configure(struct rt_serial_device *serial, struct serial_configure *cfg) /* Configuring the serial port Module */
133 {
134     struct mcx_uart *uart; /* Serial port hardware structure, calling the structure initialized above */
135     lpuart_config_t config;/* It contains basic configuration parameters of the serial port, such as baud rate, data bit, stop bit, and parity check */
136 
137     RT_ASSERT(serial != RT_NULL); /* assert */
138     RT_ASSERT(cfg != RT_NULL);
139 
140     uart = (struct mcx_uart *)serial->parent.user_data;
141 
142     CLOCK_SetClkDiv(uart->clock_div_name, 1u);
143     CLOCK_AttachClk(uart->clock_attach_id);
144     CLOCK_EnableClock(uart->clock_ip_name);
145 
146     LPUART_GetDefaultConfig(&config);
147     config.baudRate_Bps = cfg->baud_rate;
148 
149     switch (cfg->data_bits)
150     {
151         case DATA_BITS_7:
152             config.dataBitsCount = kLPUART_SevenDataBits;
153             break;
154 
155         default:
156             config.dataBitsCount = kLPUART_EightDataBits;
157             break;
158     }
159 
160     config.enableTx     = true;
161     config.enableRx     = true;
162 
163     LPUART_Init(uart->uart_base, &config, CLOCK_GetFreq(uart->clock_src));
164 
165     return RT_EOK;
166 }
167 
168 /**
169  * Serial Control Function.
170  *
171  * @param serial device struct
172  * @param control Cmd
173  * @param Parameters passed to the control command
174  */
mcx_control(struct rt_serial_device * serial,int cmd,void * arg)175 static rt_err_t mcx_control(struct rt_serial_device *serial, int cmd, void *arg)/* serial control */
176 {
177     struct mcx_uart *uart = (struct mcx_uart *)serial->parent.user_data; /* Convert the type to struct mcx_uart */
178 
179     RT_ASSERT(uart != RT_NULL); /* Assert */
180 
181     switch (cmd)
182     {
183     case RT_DEVICE_CTRL_CLR_INT:
184         /* disable rx irq */
185         LPUART_DisableInterrupts(uart->uart_base, kLPUART_RxDataRegFullInterruptEnable);
186         DisableIRQ(uart->irqn);
187         break;
188     case RT_DEVICE_CTRL_SET_INT:
189         /* enable rx irq */
190         LPUART_EnableInterrupts(uart->uart_base, kLPUART_RxDataRegFullInterruptEnable);
191         EnableIRQ(uart->irqn);
192         break;
193     }
194 
195 
196     return RT_EOK;
197 }
198 
199 /**
200  * Sends a single character function to a serial device.
201  *
202  * @param serial device struct
203  * @param The serial port character you want to send
204  */
mcx_putc(struct rt_serial_device * serial,char ch)205 static int mcx_putc(struct rt_serial_device *serial, char ch)
206 {
207     struct mcx_uart *uart = (struct mcx_uart *)serial->parent.user_data;
208 
209     while(!(kLPUART_TxDataRegEmptyFlag & LPUART_GetStatusFlags(uart->uart_base)));
210     LPUART_WriteByte(uart->uart_base, ch);
211 
212     return 1;
213 }
214 
mcx_getc(struct rt_serial_device * serial)215 static int mcx_getc(struct rt_serial_device *serial)
216 {
217     struct mcx_uart *uart = (struct mcx_uart *)serial->parent.user_data;
218 
219     if (kLPUART_RxDataRegFullInterruptEnable & LPUART_GetStatusFlags(uart->uart_base))
220 /* Check whether the receive cache is full and read the status flag bit of the status register
221    This flag is read, indicating that there is data in the cache and can be read */
222     {
223         return LPUART_ReadByte(uart->uart_base);
224     }
225     else
226     {
227         return -1;
228     }
229 }
230 
231 /**
232  * Uart common interrupt process. This need add to uart ISR.
233  *
234  * @param serial serial device
235  */
uart_isr(struct rt_serial_device * serial)236 static void uart_isr(struct rt_serial_device *serial)
237 {
238     struct mcx_uart *uart; /* Create a serial port hardware structure variable */
239 
240     RT_ASSERT(serial != RT_NULL);
241 
242     uart = (struct mcx_uart *) serial->parent.user_data;
243     RT_ASSERT(uart != RT_NULL);
244 
245     /* enter interrupt */
246     rt_interrupt_enter();
247 
248     /* UART in mode Receiver -------------------------------------------------*/
249     rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
250 
251     /* leave interrupt */
252     rt_interrupt_leave();
253 }
254 
255 static const struct rt_uart_ops mcx_uart_ops =
256 {
257     mcx_configure,
258     mcx_control,
259     mcx_putc,
260     mcx_getc,
261 };
262 
rt_hw_uart_init(void)263 int rt_hw_uart_init(void)
264 {
265     struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; /* initial struct [115200,8,1,NONE] */
266     int i;
267 /* Registers loops for multiple serial devices */
268     for (i = 0; i < sizeof(uarts) / sizeof(uarts[0]); i++) /* sizeof(uarts) / sizeof(uarts[0] : Calculate the number of struct mcx_uart serial ports */
269     {
270         uarts[i].serial->ops    = &mcx_uart_ops;
271         uarts[i].serial->config = config;
272 
273 /**
274  * register UART device.
275  *
276  * @param Indicates the structure of the serial port device to be registered
277  * @param device name
278  * @param Flag bit mask
279  * @param A pointer to the current device that is used as user private data at registration
280  */
281         rt_hw_serial_register(uarts[i].serial, uarts[i].device_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, (void *)&uarts[i]);
282     }
283 
284     return 0;
285 }
286 
287 INIT_BOARD_EXPORT(rt_hw_uart_init); /* RT-Thread Automatic initialization mechanism */
288 
289 #endif /*BSP_USING_SERIAL */
290