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  * 2022-11-16     GuEe-GUI     first version
9  */
10 
11 #include "8250.h"
12 
13 struct serial8250 early_serial8250 = { 0 };
14 
serial8250_early_in(struct serial8250 * serial,int offset)15 static rt_uint32_t serial8250_early_in(struct serial8250 *serial, int offset)
16 {
17     return serial8250_in(serial, offset);
18 }
19 
serial8250_early_out(struct serial8250 * serial,int offset,int value)20 static void serial8250_early_out(struct serial8250 *serial, int offset, int value)
21 {
22     serial8250_out(serial, offset, value);
23 }
24 
serial8250_early_putc(struct rt_serial_device * raw_serial,char c)25 int serial8250_early_putc(struct rt_serial_device *raw_serial, char c)
26 {
27     if (raw_serial)
28     {
29         /* FIFO and shifting register empty */
30         const int uart_lsr_both_empty = (UART_LSR_TEMT | UART_LSR_THRE);
31         struct serial8250 *serial = rt_container_of(raw_serial, struct serial8250, parent);
32 
33         serial8250_early_out(serial, UART_TX, c);
34 
35         while ((serial8250_early_in(serial, UART_LSR) & uart_lsr_both_empty) != uart_lsr_both_empty)
36         {
37             rt_hw_cpu_relax();
38         }
39     }
40 
41     return 1;
42 }
43 
init_serial(struct serial8250 * serial)44 static void init_serial(struct serial8250 *serial)
45 {
46     unsigned char c;
47     rt_uint32_t ier, divisor;
48 
49     serial8250_early_out(serial, UART_LCR, 0x3);    /* 8n1 */
50     ier = serial8250_early_in(serial, UART_IER);
51     serial8250_early_out(serial, UART_IER, ier & UART_IER_UUE); /* no interrupt */
52     serial8250_early_out(serial, UART_FCR, 0);      /* no fifo */
53     serial8250_early_out(serial, UART_MCR, 0x3);    /* DTR + RTS */
54 
55     if (serial->freq)
56     {
57         divisor = RT_DIV_ROUND_CLOSEST(serial->freq, 16 * serial->parent.config.baud_rate);
58         c = serial8250_early_in(serial, UART_LCR);
59         serial8250_early_out(serial, UART_LCR, c | UART_LCR_DLAB);
60         serial8250_early_out(serial, UART_DLL, divisor & 0xff);
61         serial8250_early_out(serial, UART_DLM, (divisor >> 8) & 0xff);
62         serial8250_early_out(serial, UART_LCR, c & ~UART_LCR_DLAB);
63     }
64 }
65 
serial8250_early_kick(struct rt_fdt_earlycon * con,int why)66 static void serial8250_early_kick(struct rt_fdt_earlycon *con, int why)
67 {
68     struct serial8250 *serial = raw_to_serial8250(con->data);
69 
70     switch (why)
71     {
72     case FDT_EARLYCON_KICK_UPDATE:
73         serial->base = rt_ioremap((void *)con->mmio, con->size);
74         break;
75 
76     case FDT_EARLYCON_KICK_COMPLETED:
77         rt_iounmap(serial->base);
78         break;
79 
80     default:
81         break;
82     }
83 }
84 
serial8250_early_fdt_setup(struct serial8250 * serial,struct rt_fdt_earlycon * con,const char * options)85 rt_err_t serial8250_early_fdt_setup(struct serial8250 *serial, struct rt_fdt_earlycon *con, const char *options)
86 {
87     rt_err_t ret = RT_EOK;
88 
89     if (!serial->base && con)
90     {
91         serial8250_config(serial, options);
92         con->mmio = (rt_ubase_t)serial->base;
93         con->size = serial->size;
94     }
95 
96     if (serial->base && con)
97     {
98         serial->base = rt_ioremap_early((void *)serial->base, serial->size);
99     }
100 
101     if (serial->base && con)
102     {
103         con->console_putc = (typeof(con->console_putc))&serial8250_early_putc;
104         con->console_kick = serial8250_early_kick;
105         con->data = &serial->parent;
106 
107         if (!serial->parent.config.baud_rate)
108         {
109             /* assume the device was initialized, only mask interrupts */
110             rt_uint32_t ier = serial8250_early_in(serial, UART_IER);
111             serial8250_early_out(serial, UART_IER, ier & UART_IER_UUE);
112         }
113         else
114         {
115             init_serial(serial);
116         }
117     }
118     else
119     {
120         ret = -RT_ERROR;
121     }
122 
123     return ret;
124 }
125 
common_init(struct serial8250 * serial,struct rt_fdt_earlycon * con)126 static void common_init(struct serial8250 *serial, struct rt_fdt_earlycon *con)
127 {
128     serial->base = (void *)con->mmio;
129     serial->size = con->size;
130     serial->iotype = PORT_MMIO32;
131 }
132 
common_early_setup(struct rt_fdt_earlycon * con,const char * options)133 static rt_err_t common_early_setup(struct rt_fdt_earlycon *con, const char *options)
134 {
135     struct serial8250 *serial = &early_serial8250;
136 
137     common_init(serial, con);
138     serial->regshift = 2;
139     fdt_getprop_u32(con->fdt, con->nodeoffset, "reg-shift", &serial->regshift, RT_NULL);
140 
141     return serial8250_early_fdt_setup(serial, con, options);
142 }
143 RT_FDT_EARLYCON_EXPORT(bcm2835aux, "uart8250", "brcm,bcm2835-aux-uart", common_early_setup);
144 RT_FDT_EARLYCON_EXPORT(tegra20, "uart8250", "nvidia,tegra20-uart", common_early_setup);
145 RT_FDT_EARLYCON_EXPORT(dw8250, "uart8250", "snps,dw-apb-uart", common_early_setup);
146 RT_FDT_EARLYCON_EXPORT(ns16550a, "uart8250", "ns16550a", common_early_setup);
147 RT_FDT_EARLYCON_EXPORT(ns16550, "uart8250", "ns16550", common_early_setup);
148 
149 #ifdef RT_USING_8250_OMAP
omap8250_early_setup(struct rt_fdt_earlycon * con,const char * options)150 static rt_err_t omap8250_early_setup(struct rt_fdt_earlycon *con, const char *options)
151 {
152     struct serial8250 *serial = &early_serial8250;
153 
154     common_init(serial, con);
155     serial->regshift = 2;
156 
157     return serial8250_early_fdt_setup(serial, con, options);
158 }
159 RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap2-uart", omap8250_early_setup);
160 RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap3-uart", omap8250_early_setup);
161 RT_FDT_EARLYCON_EXPORT(omap8250, "uart8250", "ti,omap4-uart", omap8250_early_setup);
162 #endif /* RT_USING_8250_OMAP */
163