1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Early serial console for 8250/16550 devices |
4 | * |
5 | * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. |
6 | * Bjorn Helgaas <bjorn.helgaas@hp.com> |
7 | * |
8 | * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, |
9 | * and on early_printk.c by Andi Kleen. |
10 | * |
11 | * This is for use before the serial driver has initialized, in |
12 | * particular, before the UARTs have been discovered and named. |
13 | * Instead of specifying the console device as, e.g., "ttyS0", |
14 | * we locate the device directly by its MMIO or I/O port address. |
15 | * |
16 | * The user can specify the device directly, e.g., |
17 | * earlycon=uart8250,io,0x3f8,9600n8 |
18 | * earlycon=uart8250,mmio,0xff5e0000,115200n8 |
19 | * earlycon=uart8250,mmio32,0xff5e0000,115200n8 |
20 | * or |
21 | * console=uart8250,io,0x3f8,9600n8 |
22 | * console=uart8250,mmio,0xff5e0000,115200n8 |
23 | * console=uart8250,mmio32,0xff5e0000,115200n8 |
24 | */ |
25 | |
26 | #include <linux/tty.h> |
27 | #include <linux/init.h> |
28 | #include <linux/console.h> |
29 | #include <linux/of.h> |
30 | #include <linux/serial_reg.h> |
31 | #include <linux/serial.h> |
32 | #include <linux/serial_8250.h> |
33 | #include <asm/io.h> |
34 | #include <asm/serial.h> |
35 | |
36 | static unsigned int serial8250_early_in(struct uart_port *port, int offset) |
37 | { |
38 | offset <<= port->regshift; |
39 | |
40 | switch (port->iotype) { |
41 | case UPIO_MEM: |
42 | return readb(addr: port->membase + offset); |
43 | case UPIO_MEM16: |
44 | return readw(addr: port->membase + offset); |
45 | case UPIO_MEM32: |
46 | return readl(addr: port->membase + offset); |
47 | case UPIO_MEM32BE: |
48 | return ioread32be(port->membase + offset); |
49 | case UPIO_PORT: |
50 | return inb(port: port->iobase + offset); |
51 | default: |
52 | return 0; |
53 | } |
54 | } |
55 | |
56 | static void serial8250_early_out(struct uart_port *port, int offset, int value) |
57 | { |
58 | offset <<= port->regshift; |
59 | |
60 | switch (port->iotype) { |
61 | case UPIO_MEM: |
62 | writeb(val: value, addr: port->membase + offset); |
63 | break; |
64 | case UPIO_MEM16: |
65 | writew(val: value, addr: port->membase + offset); |
66 | break; |
67 | case UPIO_MEM32: |
68 | writel(val: value, addr: port->membase + offset); |
69 | break; |
70 | case UPIO_MEM32BE: |
71 | iowrite32be(value, port->membase + offset); |
72 | break; |
73 | case UPIO_PORT: |
74 | outb(value, port: port->iobase + offset); |
75 | break; |
76 | } |
77 | } |
78 | |
79 | static void serial_putc(struct uart_port *port, unsigned char c) |
80 | { |
81 | unsigned int status; |
82 | |
83 | serial8250_early_out(port, UART_TX, value: c); |
84 | |
85 | for (;;) { |
86 | status = serial8250_early_in(port, UART_LSR); |
87 | if (uart_lsr_tx_empty(lsr: status)) |
88 | break; |
89 | cpu_relax(); |
90 | } |
91 | } |
92 | |
93 | static void early_serial8250_write(struct console *console, |
94 | const char *s, unsigned int count) |
95 | { |
96 | struct earlycon_device *device = console->data; |
97 | struct uart_port *port = &device->port; |
98 | |
99 | uart_console_write(port, s, count, putchar: serial_putc); |
100 | } |
101 | |
102 | #ifdef CONFIG_CONSOLE_POLL |
103 | static int early_serial8250_read(struct console *console, |
104 | char *s, unsigned int count) |
105 | { |
106 | struct earlycon_device *device = console->data; |
107 | struct uart_port *port = &device->port; |
108 | unsigned int status; |
109 | int num_read = 0; |
110 | |
111 | while (num_read < count) { |
112 | status = serial8250_early_in(port, UART_LSR); |
113 | if (!(status & UART_LSR_DR)) |
114 | break; |
115 | s[num_read++] = serial8250_early_in(port, UART_RX); |
116 | } |
117 | |
118 | return num_read; |
119 | } |
120 | #else |
121 | #define early_serial8250_read NULL |
122 | #endif |
123 | |
124 | static void __init init_port(struct earlycon_device *device) |
125 | { |
126 | struct uart_port *port = &device->port; |
127 | unsigned int divisor; |
128 | unsigned char c; |
129 | unsigned int ier; |
130 | |
131 | serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8); /* 8n1 */ |
132 | ier = serial8250_early_in(port, UART_IER); |
133 | serial8250_early_out(port, UART_IER, value: ier & UART_IER_UUE); /* no interrupt */ |
134 | serial8250_early_out(port, UART_FCR, value: 0); /* no fifo */ |
135 | serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); |
136 | |
137 | if (port->uartclk) { |
138 | divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); |
139 | c = serial8250_early_in(port, UART_LCR); |
140 | serial8250_early_out(port, UART_LCR, value: c | UART_LCR_DLAB); |
141 | serial8250_early_out(port, UART_DLL, value: divisor & 0xff); |
142 | serial8250_early_out(port, UART_DLM, value: (divisor >> 8) & 0xff); |
143 | serial8250_early_out(port, UART_LCR, value: c & ~UART_LCR_DLAB); |
144 | } |
145 | } |
146 | |
147 | int __init early_serial8250_setup(struct earlycon_device *device, |
148 | const char *options) |
149 | { |
150 | if (!(device->port.membase || device->port.iobase)) |
151 | return -ENODEV; |
152 | |
153 | if (!device->baud) { |
154 | struct uart_port *port = &device->port; |
155 | unsigned int ier; |
156 | |
157 | /* assume the device was initialized, only mask interrupts */ |
158 | ier = serial8250_early_in(port, UART_IER); |
159 | serial8250_early_out(port, UART_IER, value: ier & UART_IER_UUE); |
160 | } else |
161 | init_port(device); |
162 | |
163 | device->con->write = early_serial8250_write; |
164 | device->con->read = early_serial8250_read; |
165 | return 0; |
166 | } |
167 | EARLYCON_DECLARE(uart8250, early_serial8250_setup); |
168 | EARLYCON_DECLARE(uart, early_serial8250_setup); |
169 | OF_EARLYCON_DECLARE(ns16550, "ns16550" , early_serial8250_setup); |
170 | OF_EARLYCON_DECLARE(ns16550a, "ns16550a" , early_serial8250_setup); |
171 | OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart" , early_serial8250_setup); |
172 | OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart" , early_serial8250_setup); |
173 | |
174 | #ifdef CONFIG_SERIAL_8250_OMAP |
175 | |
176 | static int __init early_omap8250_setup(struct earlycon_device *device, |
177 | const char *options) |
178 | { |
179 | struct uart_port *port = &device->port; |
180 | |
181 | if (!(device->port.membase || device->port.iobase)) |
182 | return -ENODEV; |
183 | |
184 | port->regshift = 2; |
185 | device->con->write = early_serial8250_write; |
186 | return 0; |
187 | } |
188 | |
189 | OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart" , early_omap8250_setup); |
190 | OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart" , early_omap8250_setup); |
191 | OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart" , early_omap8250_setup); |
192 | OF_EARLYCON_DECLARE(omap8250, "ti,am654-uart" , early_omap8250_setup); |
193 | |
194 | #endif |
195 | |