1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2013-05-18     Bernard      The first version for LPC40xx
9  * 2014-12-16     RT_learning  The first version for LPC5410x
10  */
11 
12 
13 #include <rthw.h>
14 #include <rtthread.h>
15 #include <rtdevice.h>
16 
17 #include "chip.h"
18 
19 static uint32_t _UART_DivClk(uint32_t pclk, uint32_t m);
20 static uint32_t _UART_GetHighDiv(uint32_t val, uint8_t strict);
21 static int32_t _CalcErr(uint32_t n, uint32_t d, uint32_t *prev);
22 static ErrorCode_t _UART_CalcDiv(UART_BAUD_T *ub);
23 static void _UART_CalcMul(UART_BAUD_T *ub);
24 
25 struct lpc_uart
26 {
27     LPC_USART_T *UART;
28     IRQn_Type UART_IRQn;
29 };
30 
lpc_configure(struct rt_serial_device * serial,struct serial_configure * cfg)31 static rt_err_t lpc_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
32 {
33     struct lpc_uart *uart;
34 
35     UART_BAUD_T baud;
36     UART_CFG_T UART_cfg;
37 
38     RT_ASSERT(serial != RT_NULL);
39     uart = (struct lpc_uart *)serial->parent.user_data;
40 
41     /* Initialize UART Configuration parameter structure to default state:
42      * Baudrate = 115200 b
43      * 8 data bit
44      * 1 Stop bit
45      * None parity
46      */
47 
48     /* Set up baudrate parameters */
49     baud.clk = Chip_Clock_GetAsyncSyscon_ClockRate();   /* Clock frequency */
50     baud.baud = cfg->baud_rate; /* Required baud rate */
51     baud.ovr = 0;   /* Set the oversampling to the recommended rate */
52     baud.mul = baud.div = 0;
53 
54     if(!baud.mul)
55     {
56         _UART_CalcMul(&baud);
57     }
58     _UART_CalcDiv(&baud);
59 
60     /* Set fractional control register */
61     LPC_ASYNC_SYSCON->FRGCTRL = ((uint32_t) baud.mul << 8) | 0xFF;
62 
63     /* Configure the UART */
64     UART_cfg.cfg = UART_CFG_8BIT;
65     UART_cfg.div = baud.div;    /* Use the calculated div value */
66     UART_cfg.ovr = baud.ovr;    /* Use oversampling rate from baud */
67     UART_cfg.res = UART_BIT_DLY(cfg->baud_rate);
68 
69     /* P254,255,246 */
70     uart->UART->OSR = (UART_cfg.ovr - 1) & 0x0F;
71     uart->UART->BRG = (UART_cfg.div - 1) & 0xFFFF;
72     uart->UART->CFG = UART_CFG_ENABLE | (UART_cfg.cfg & ~UART_CFG_RES);
73 
74     return RT_EOK;
75 }
76 
lpc_control(struct rt_serial_device * serial,int cmd,void * arg)77 static rt_err_t lpc_control(struct rt_serial_device *serial, int cmd, void *arg)
78 {
79     struct lpc_uart *uart;
80 
81     RT_ASSERT(serial != RT_NULL);
82     uart = (struct lpc_uart *)serial->parent.user_data;
83 
84     switch (cmd)
85     {
86     case RT_DEVICE_CTRL_CLR_INT:
87         /* disable rx irq */
88         uart->UART->INTENCLR &= ~0x01;
89         break;
90     case RT_DEVICE_CTRL_SET_INT:
91         /* enable rx irq */
92         uart->UART->INTENSET |= 0x01;
93         break;
94     }
95 
96     return RT_EOK;
97 }
98 
lpc_putc(struct rt_serial_device * serial,char c)99 static int lpc_putc(struct rt_serial_device *serial, char c)
100 {
101     struct lpc_uart *uart;
102 
103     uart = (struct lpc_uart *)serial->parent.user_data;
104     while(!(uart->UART->STAT & (0x01<<2)));
105 
106     uart->UART->TXDAT = c ;
107 
108 
109     return 1;
110 }
111 
112 
113 
lpc_getc(struct rt_serial_device * serial)114 static int lpc_getc(struct rt_serial_device *serial)
115 {
116     struct lpc_uart *uart;
117 
118     uart = (struct lpc_uart *)serial->parent.user_data;
119     if (uart->UART->STAT & 0x01)
120         return (uart->UART->RXDAT);
121     else
122         return -1;
123 }
124 
125 static const struct rt_uart_ops lpc_uart_ops =
126 {
127     lpc_configure,
128     lpc_control,
129     lpc_putc,
130     lpc_getc,
131 };
132 
133 
134 /* UART0 device driver structure */
135 struct lpc_uart uart0 =
136 {
137     LPC_USART0,
138     UART0_IRQn,
139 };
140 struct rt_serial_device serial0;
141 
142 
143 
UART0_IRQHandler(void)144 void UART0_IRQHandler(void)
145 {
146     volatile  uint32_t  INTSTAT, tmp;
147     /* enter interrupt */
148     rt_interrupt_enter();
149 
150     INTSTAT = LPC_USART0->INTSTAT;
151 
152     INTSTAT &= 0x01;
153     switch (INTSTAT)
154     {
155     case 0x01:
156         rt_hw_serial_isr(&serial0, RT_SERIAL_EVENT_RX_IND);
157         break;
158     default :
159         tmp = LPC_USART0->INTSTAT;
160         RT_UNUSED(tmp);
161         break;
162     }
163     /* leave interrupt */
164     rt_interrupt_leave();
165 }
166 
rt_hw_uart_init(void)167 void rt_hw_uart_init(void)
168 {
169     struct lpc_uart *uart;
170     struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
171 
172     uart = &uart0;
173 
174     serial0.ops    = &lpc_uart_ops;
175     serial0.config = config;
176     serial0.parent.user_data = uart;
177 
178     /* Enable IOCON clock  Then your cfg will effective P38 */
179     LPC_SYSCON->AHBCLKCTRLSET[0] = (1UL << 13);
180 
181     /* Setup UART TX,RX Pin configuration  cfg Pin as Tx, Rx */
182     /*  P63,P77
183             Selects pin function 1      IOCON_FUNC1
184             No addition pin function    IOCON_MODE_INACT
185             Enables digital function by setting 1 to bit 7(default) IOCON_DIGITAL_EN
186     */
187     LPC_IOCON->PIO[0][0] = (0x1 | (0x0 << 3) | (0x1 << 7));
188     LPC_IOCON->PIO[0][1] = (0x1 | (0x0 << 3) | (0x1 << 7));
189 
190 
191     /* Enable asynchronous APB bridge and subsystem P30 */
192     LPC_SYSCON->ASYNCAPBCTRL = 0x01;
193 
194     /* The UART clock rate is the main system clock divided by this value P59 */
195     LPC_ASYNC_SYSCON->ASYNCCLKDIV = 1;                                      /* Set Async clock divider to 1 */
196 
197       /* Enable peripheral clock(asynchronous APB) to UART0  P57*/
198     LPC_ASYNC_SYSCON->ASYNCAPBCLKCTRLSET = (1 << 0x01);
199 
200     /* Controls the clock for the Fractional Rate Generator used with the USARTs P57*/
201     LPC_ASYNC_SYSCON->ASYNCAPBCLKCTRLSET = (1 << 0x0F);     /* Enable clock to Fractional divider */
202 
203     /* preemption = 1, sub-priority = 1 */
204     NVIC_SetPriority(uart->UART_IRQn, ((0x01 << 3) | 0x01));
205 
206     /* Enable Interrupt for UART channel */
207     NVIC_EnableIRQ(uart->UART_IRQn);
208 
209     /* register UART0 device */
210     rt_hw_serial_register(&serial0, "uart0",
211                           RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
212                           uart);
213 }
214 
215 
216 /* PRIVATE: Division logic to divide without integer overflow */
_UART_DivClk(uint32_t pclk,uint32_t m)217 static uint32_t _UART_DivClk(uint32_t pclk, uint32_t m)
218 {
219     uint32_t q, r, u = pclk >> 24, l = pclk << 8;
220     m = m + 256;
221     q = (1 << 24) / m;
222     r = (1 << 24) - (q * m);
223     return ((q * u) << 8) + (((r * u) << 8) + l) / m;
224 }
225 
226 /* PRIVATE: Get highest Over sampling value */
_UART_GetHighDiv(uint32_t val,uint8_t strict)227 static uint32_t _UART_GetHighDiv(uint32_t val, uint8_t strict)
228 {
229     int32_t i, max = strict ? 16 : 5;
230     for (i = 16; i >= max; i--)
231     {
232         if (!(val % i))
233         {
234             return i;
235         }
236     }
237     return 0;
238 }
239 
240 /* Calculate error difference */
_CalcErr(uint32_t n,uint32_t d,uint32_t * prev)241 static int32_t _CalcErr(uint32_t n, uint32_t d, uint32_t *prev)
242 {
243     uint32_t err = n - (n / d) * d;
244     uint32_t herr = ((n / d) + 1) * d - n;
245     if (herr < err) {
246         err = herr;
247     }
248 
249     if (*prev <= err) {
250         return 0;
251     }
252     *prev = err;
253     return (herr == err) + 1;
254 }
255 
256 /* Calculate the base DIV value */
_UART_CalcDiv(UART_BAUD_T * ub)257 static ErrorCode_t _UART_CalcDiv(UART_BAUD_T *ub)
258 {
259     int32_t i = 0;
260     uint32_t perr = ~0UL;
261 
262     if (!ub->div) {
263         i = ub->ovr ? ub->ovr : 16;
264     }
265 
266     for (; i > 4; i--) {
267         int32_t tmp = _CalcErr(ub->clk, ub->baud * i, &perr);
268 
269         /* Continue when no improvement seen in err value */
270         if (!tmp) {
271             continue;
272         }
273 
274         ub->div = tmp - 1;
275         if (ub->ovr == i) {
276             break;
277         }
278         ub->ovr = i;
279     }
280 
281     if (!ub->ovr) {
282         return ERR_UART_BAUDRATE;
283     }
284 
285     ub->div += ub->clk / (ub->baud * ub->ovr);
286     if (!ub->div) {
287         return ERR_UART_BAUDRATE;
288     }
289 
290     ub->baud = ub->clk / (ub->div * ub->ovr);
291     return LPC_OK;
292 }
293 
294 /* Calculate the best MUL value */
_UART_CalcMul(UART_BAUD_T * ub)295 static void _UART_CalcMul(UART_BAUD_T *ub)
296 {
297     uint32_t m, perr = ~0UL, pclk = ub->clk, ovr = ub->ovr;
298 
299     /* If clock is UART's base clock calculate only the divider */
300     for (m = 0; m < 256; m++) {
301         uint32_t ov = ovr, x, v, tmp;
302 
303         /* Get clock and calculate error */
304         x = _UART_DivClk(pclk, m);
305         tmp = _CalcErr(x, ub->baud, &perr);
306         v = (x / ub->baud) + tmp - 1;
307 
308         /* Update if new error is better than previous best */
309         if (!tmp || (ovr && (v % ovr)) ||
310             (!ovr && ((ov = _UART_GetHighDiv(v, ovr)) == 0))) {
311             continue;
312         }
313 
314         ub->ovr = ov;
315         ub->mul = m;
316         ub->clk = x;
317         ub->div = tmp - 1;
318     }
319 }
320