]>
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> | |
d05f1570 SW |
32 | #include <linux/of.h> |
33 | #include <linux/of_device.h> | |
1da177e4 LT |
34 | #include <linux/serial_reg.h> |
35 | #include <linux/serial.h> | |
18a8bd94 | 36 | #include <linux/serial_8250.h> |
1da177e4 LT |
37 | #include <asm/io.h> |
38 | #include <asm/serial.h> | |
39 | ||
f3fb7ef3 | 40 | static unsigned int __init serial8250_early_in(struct uart_port *port, int offset) |
1da177e4 | 41 | { |
1917ac76 SG |
42 | switch (port->iotype) { |
43 | case UPIO_MEM: | |
1da177e4 | 44 | return readb(port->membase + offset); |
1917ac76 SG |
45 | case UPIO_MEM32: |
46 | return readl(port->membase + (offset << 2)); | |
c627f2ce KC |
47 | case UPIO_MEM32BE: |
48 | return ioread32be(port->membase + (offset << 2)); | |
1917ac76 | 49 | case UPIO_PORT: |
1da177e4 | 50 | return inb(port->iobase + offset); |
1917ac76 SG |
51 | default: |
52 | return 0; | |
53 | } | |
1da177e4 LT |
54 | } |
55 | ||
f3fb7ef3 | 56 | static void __init serial8250_early_out(struct uart_port *port, int offset, int value) |
1da177e4 | 57 | { |
1917ac76 SG |
58 | switch (port->iotype) { |
59 | case UPIO_MEM: | |
1da177e4 | 60 | writeb(value, port->membase + offset); |
1917ac76 SG |
61 | break; |
62 | case UPIO_MEM32: | |
63 | writel(value, port->membase + (offset << 2)); | |
64 | break; | |
c627f2ce KC |
65 | case UPIO_MEM32BE: |
66 | iowrite32be(value, port->membase + (offset << 2)); | |
67 | break; | |
1917ac76 | 68 | case UPIO_PORT: |
1da177e4 | 69 | outb(value, port->iobase + offset); |
1917ac76 SG |
70 | break; |
71 | } | |
1da177e4 LT |
72 | } |
73 | ||
74 | #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) | |
75 | ||
76 | static void __init wait_for_xmitr(struct uart_port *port) | |
77 | { | |
78 | unsigned int status; | |
79 | ||
80 | for (;;) { | |
ed71871b | 81 | status = serial8250_early_in(port, UART_LSR); |
1da177e4 LT |
82 | if ((status & BOTH_EMPTY) == BOTH_EMPTY) |
83 | return; | |
84 | cpu_relax(); | |
85 | } | |
86 | } | |
87 | ||
bf2cdef3 | 88 | static void __init serial_putc(struct uart_port *port, int c) |
1da177e4 LT |
89 | { |
90 | wait_for_xmitr(port); | |
ed71871b | 91 | serial8250_early_out(port, UART_TX, c); |
1da177e4 LT |
92 | } |
93 | ||
ce2e204f AC |
94 | static void __init early_serial8250_write(struct console *console, |
95 | const char *s, unsigned int count) | |
1da177e4 | 96 | { |
d0d654ce PH |
97 | struct earlycon_device *device = console->data; |
98 | struct uart_port *port = &device->port; | |
1da177e4 LT |
99 | unsigned int ier; |
100 | ||
a4c639b0 | 101 | /* Save the IER and disable interrupts preserving the UUE bit */ |
ed71871b | 102 | ier = serial8250_early_in(port, UART_IER); |
5567c37d | 103 | if (ier) |
a4c639b0 | 104 | serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); |
1da177e4 | 105 | |
bf2cdef3 | 106 | uart_console_write(port, s, count, serial_putc); |
1da177e4 LT |
107 | |
108 | /* Wait for transmitter to become empty and restore the IER */ | |
109 | wait_for_xmitr(port); | |
5567c37d VG |
110 | |
111 | if (ier) | |
112 | serial8250_early_out(port, UART_IER, ier); | |
1da177e4 LT |
113 | } |
114 | ||
d2fd6810 | 115 | static void __init init_port(struct earlycon_device *device) |
1da177e4 LT |
116 | { |
117 | struct uart_port *port = &device->port; | |
118 | unsigned int divisor; | |
119 | unsigned char c; | |
a4c639b0 | 120 | unsigned int ier; |
1da177e4 | 121 | |
ed71871b | 122 | serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ |
a4c639b0 RH |
123 | ier = serial8250_early_in(port, UART_IER); |
124 | serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */ | |
ed71871b NC |
125 | serial8250_early_out(port, UART_FCR, 0); /* no fifo */ |
126 | serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ | |
1da177e4 | 127 | |
b15d5380 | 128 | divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); |
ed71871b NC |
129 | c = serial8250_early_in(port, UART_LCR); |
130 | serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); | |
131 | serial8250_early_out(port, UART_DLL, divisor & 0xff); | |
132 | serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); | |
133 | serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); | |
1da177e4 LT |
134 | } |
135 | ||
1c5841e8 | 136 | int __init early_serial8250_setup(struct earlycon_device *device, |
d2fd6810 | 137 | const char *options) |
1da177e4 | 138 | { |
d2fd6810 | 139 | if (!(device->port.membase || device->port.iobase)) |
cd385e9a | 140 | return -ENODEV; |
1da177e4 | 141 | |
60efcf04 | 142 | if (!device->baud) { |
0e3e143e PH |
143 | struct uart_port *port = &device->port; |
144 | unsigned int ier; | |
145 | ||
0e3e143e PH |
146 | /* assume the device was initialized, only mask interrupts */ |
147 | ier = serial8250_early_in(port, UART_IER); | |
148 | serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); | |
149 | } else | |
150 | init_port(device); | |
18a8bd94 | 151 | |
d2fd6810 | 152 | device->con->write = early_serial8250_write; |
18a8bd94 | 153 | return 0; |
1da177e4 | 154 | } |
d2fd6810 RH |
155 | EARLYCON_DECLARE(uart8250, early_serial8250_setup); |
156 | EARLYCON_DECLARE(uart, early_serial8250_setup); | |
d05f1570 SW |
157 | OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup); |
158 | OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); |