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