Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Early serial console for 8250/16550 devices | |
3 | * | |
4 | * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. | |
5 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, | |
12 | * and on early_printk.c by Andi Kleen. | |
13 | * | |
14 | * This is for use before the serial driver has initialized, in | |
15 | * particular, before the UARTs have been discovered and named. | |
16 | * Instead of specifying the console device as, e.g., "ttyS0", | |
17 | * we locate the device directly by its MMIO or I/O port address. | |
18 | * | |
19 | * The user can specify the device directly, e.g., | |
18a8bd94 YL |
20 | * earlycon=uart8250,io,0x3f8,9600n8 |
21 | * earlycon=uart8250,mmio,0xff5e0000,115200n8 | |
1917ac76 | 22 | * earlycon=uart8250,mmio32,0xff5e0000,115200n8 |
18a8bd94 YL |
23 | * or |
24 | * console=uart8250,io,0x3f8,9600n8 | |
25 | * console=uart8250,mmio,0xff5e0000,115200n8 | |
1917ac76 | 26 | * console=uart8250,mmio32,0xff5e0000,115200n8 |
1da177e4 LT |
27 | */ |
28 | ||
29 | #include <linux/tty.h> | |
30 | #include <linux/init.h> | |
31 | #include <linux/console.h> | |
32 | #include <linux/serial_core.h> | |
33 | #include <linux/serial_reg.h> | |
34 | #include <linux/serial.h> | |
18a8bd94 | 35 | #include <linux/serial_8250.h> |
1da177e4 LT |
36 | #include <asm/io.h> |
37 | #include <asm/serial.h> | |
38 | ||
d2fd6810 | 39 | static struct earlycon_device *early_device; |
1da177e4 | 40 | |
ed71871b | 41 | unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset) |
1da177e4 | 42 | { |
1917ac76 SG |
43 | switch (port->iotype) { |
44 | case UPIO_MEM: | |
1da177e4 | 45 | return readb(port->membase + offset); |
1917ac76 SG |
46 | case UPIO_MEM32: |
47 | return readl(port->membase + (offset << 2)); | |
48 | case UPIO_PORT: | |
1da177e4 | 49 | return inb(port->iobase + offset); |
1917ac76 SG |
50 | default: |
51 | return 0; | |
52 | } | |
1da177e4 LT |
53 | } |
54 | ||
ed71871b | 55 | void __weak __init serial8250_early_out(struct uart_port *port, int offset, int value) |
1da177e4 | 56 | { |
1917ac76 SG |
57 | switch (port->iotype) { |
58 | case UPIO_MEM: | |
1da177e4 | 59 | writeb(value, port->membase + offset); |
1917ac76 SG |
60 | break; |
61 | case UPIO_MEM32: | |
62 | writel(value, port->membase + (offset << 2)); | |
63 | break; | |
64 | case UPIO_PORT: | |
1da177e4 | 65 | outb(value, port->iobase + offset); |
1917ac76 SG |
66 | break; |
67 | } | |
1da177e4 LT |
68 | } |
69 | ||
70 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | |
71 | ||
72 | static void __init wait_for_xmitr(struct uart_port *port) | |
73 | { | |
74 | unsigned int status; | |
75 | ||
76 | for (;;) { | |
ed71871b | 77 | status = serial8250_early_in(port, UART_LSR); |
1da177e4 LT |
78 | if ((status & BOTH_EMPTY) == BOTH_EMPTY) |
79 | return; | |
80 | cpu_relax(); | |
81 | } | |
82 | } | |
83 | ||
bf2cdef3 | 84 | static void __init serial_putc(struct uart_port *port, int c) |
1da177e4 LT |
85 | { |
86 | wait_for_xmitr(port); | |
ed71871b | 87 | serial8250_early_out(port, UART_TX, c); |
1da177e4 LT |
88 | } |
89 | ||
ce2e204f AC |
90 | static void __init early_serial8250_write(struct console *console, |
91 | const char *s, unsigned int count) | |
1da177e4 | 92 | { |
d2fd6810 | 93 | struct uart_port *port = &early_device->port; |
1da177e4 LT |
94 | unsigned int ier; |
95 | ||
96 | /* Save the IER and disable interrupts */ | |
ed71871b NC |
97 | ier = serial8250_early_in(port, UART_IER); |
98 | serial8250_early_out(port, UART_IER, 0); | |
1da177e4 | 99 | |
bf2cdef3 | 100 | uart_console_write(port, s, count, serial_putc); |
1da177e4 LT |
101 | |
102 | /* Wait for transmitter to become empty and restore the IER */ | |
103 | wait_for_xmitr(port); | |
ed71871b | 104 | serial8250_early_out(port, UART_IER, ier); |
1da177e4 LT |
105 | } |
106 | ||
107 | static unsigned int __init probe_baud(struct uart_port *port) | |
108 | { | |
109 | unsigned char lcr, dll, dlm; | |
110 | unsigned int quot; | |
111 | ||
ed71871b NC |
112 | lcr = serial8250_early_in(port, UART_LCR); |
113 | serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB); | |
114 | dll = serial8250_early_in(port, UART_DLL); | |
115 | dlm = serial8250_early_in(port, UART_DLM); | |
116 | serial8250_early_out(port, UART_LCR, lcr); | |
1da177e4 LT |
117 | |
118 | quot = (dlm << 8) | dll; | |
119 | return (port->uartclk / 16) / quot; | |
120 | } | |
121 | ||
d2fd6810 | 122 | static void __init init_port(struct earlycon_device *device) |
1da177e4 LT |
123 | { |
124 | struct uart_port *port = &device->port; | |
125 | unsigned int divisor; | |
126 | unsigned char c; | |
127 | ||
ed71871b NC |
128 | serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ |
129 | serial8250_early_out(port, UART_IER, 0); /* no interrupt */ | |
130 | serial8250_early_out(port, UART_FCR, 0); /* no fifo */ | |
131 | serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ | |
1da177e4 | 132 | |
b15d5380 | 133 | divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); |
ed71871b NC |
134 | c = serial8250_early_in(port, UART_LCR); |
135 | serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); | |
136 | serial8250_early_out(port, UART_DLL, divisor & 0xff); | |
137 | serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); | |
138 | serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); | |
1da177e4 LT |
139 | } |
140 | ||
d2fd6810 RH |
141 | static int __init early_serial8250_setup(struct earlycon_device *device, |
142 | const char *options) | |
1da177e4 | 143 | { |
d2fd6810 | 144 | if (!(device->port.membase || device->port.iobase)) |
1da177e4 LT |
145 | return 0; |
146 | ||
60efcf04 | 147 | if (!device->baud) { |
d2fd6810 | 148 | device->baud = probe_baud(&device->port); |
60efcf04 RH |
149 | snprintf(device->options, sizeof(device->options), "%u", |
150 | device->baud); | |
151 | } | |
1da177e4 LT |
152 | |
153 | init_port(device); | |
18a8bd94 | 154 | |
d2fd6810 RH |
155 | early_device = device; |
156 | device->con->write = early_serial8250_write; | |
18a8bd94 | 157 | return 0; |
1da177e4 | 158 | } |
d2fd6810 RH |
159 | EARLYCON_DECLARE(uart8250, early_serial8250_setup); |
160 | EARLYCON_DECLARE(uart, early_serial8250_setup); | |
1da177e4 | 161 | |
fe1cf8af RH |
162 | int __init setup_early_serial8250_console(char *cmdline) |
163 | { | |
164 | char match[] = "uart8250"; | |
165 | ||
166 | if (cmdline && cmdline[4] == ',') | |
167 | match[4] = '\0'; | |
168 | ||
169 | return setup_earlycon(cmdline, match, early_serial8250_setup); | |
170 | } | |
171 | ||
b6b1d877 | 172 | int serial8250_find_port_for_earlycon(void) |
1da177e4 | 173 | { |
d2fd6810 RH |
174 | struct earlycon_device *device = early_device; |
175 | struct uart_port *port = device ? &device->port : NULL; | |
18a8bd94 YL |
176 | int line; |
177 | int ret; | |
1da177e4 | 178 | |
d2fd6810 | 179 | if (!port || (!port->membase && !port->iobase)) |
18a8bd94 | 180 | return -ENODEV; |
1da177e4 | 181 | |
18a8bd94 | 182 | line = serial8250_find_port(port); |
1da177e4 | 183 | if (line < 0) |
18a8bd94 | 184 | return -ENODEV; |
1da177e4 | 185 | |
18a8bd94 YL |
186 | ret = update_console_cmdline("uart", 8250, |
187 | "ttyS", line, device->options); | |
188 | if (ret < 0) | |
189 | ret = update_console_cmdline("uart", 0, | |
190 | "ttyS", line, device->options); | |
1da177e4 | 191 | |
18a8bd94 | 192 | return ret; |
1da177e4 | 193 | } |