aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/early_printk.c
diff options
context:
space:
mode:
authorStuart R. Anderson <stuart.r.anderson@intel.com>2015-01-20 07:07:57 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-02-02 13:11:27 -0500
commitea9e9d8029020d438b0717ffddf65140fda16051 (patch)
treef711ae69a62aad6b1d186f2e581c5147e25fcdaf /arch/x86/kernel/early_printk.c
parentd2b6f44779d3be22d32a5697bd30b59367fd2b33 (diff)
Specify PCI based UART for earlyprintk
Add support for specifying PCI based UARTs for earlyprintk using a syntax like "earlyprintk=pciserial,00:18.1,115200", where 00:18.1 is the BDF of a UART device. [Slightly tidied from Stuart's original patch] Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch/x86/kernel/early_printk.c')
-rw-r--r--arch/x86/kernel/early_printk.c182
1 files changed, 166 insertions, 16 deletions
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
index de814dfb8aee..a62536a1be88 100644
--- a/arch/x86/kernel/early_printk.c
+++ b/arch/x86/kernel/early_printk.c
@@ -19,6 +19,7 @@
19#include <linux/usb/ehci_def.h> 19#include <linux/usb/ehci_def.h>
20#include <linux/efi.h> 20#include <linux/efi.h>
21#include <asm/efi.h> 21#include <asm/efi.h>
22#include <asm/pci_x86.h>
22 23
23/* Simple VGA output */ 24/* Simple VGA output */
24#define VGABASE (__ISA_IO_base + 0xb8000) 25#define VGABASE (__ISA_IO_base + 0xb8000)
@@ -76,7 +77,7 @@ static struct console early_vga_console = {
76 77
77/* Serial functions loosely based on a similar package from Klaus P. Gerlicher */ 78/* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
78 79
79static int early_serial_base = 0x3f8; /* ttyS0 */ 80static unsigned long early_serial_base = 0x3f8; /* ttyS0 */
80 81
81#define XMTRDY 0x20 82#define XMTRDY 0x20
82 83
@@ -94,13 +95,40 @@ static int early_serial_base = 0x3f8; /* ttyS0 */
94#define DLL 0 /* Divisor Latch Low */ 95#define DLL 0 /* Divisor Latch Low */
95#define DLH 1 /* Divisor latch High */ 96#define DLH 1 /* Divisor latch High */
96 97
98static void mem32_serial_out(unsigned long addr, int offset, int value)
99{
100 uint32_t *vaddr = (uint32_t *)addr;
101 /* shift implied by pointer type */
102 writel(value, vaddr + offset);
103}
104
105static unsigned int mem32_serial_in(unsigned long addr, int offset)
106{
107 uint32_t *vaddr = (uint32_t *)addr;
108 /* shift implied by pointer type */
109 return readl(vaddr + offset);
110}
111
112static unsigned int io_serial_in(unsigned long addr, int offset)
113{
114 return inb(addr + offset);
115}
116
117static void io_serial_out(unsigned long addr, int offset, int value)
118{
119 outb(value, addr + offset);
120}
121
122static unsigned int (*serial_in)(unsigned long addr, int offset) = io_serial_in;
123static void (*serial_out)(unsigned long addr, int offset, int value) = io_serial_out;
124
97static int early_serial_putc(unsigned char ch) 125static int early_serial_putc(unsigned char ch)
98{ 126{
99 unsigned timeout = 0xffff; 127 unsigned timeout = 0xffff;
100 128
101 while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) 129 while ((serial_in(early_serial_base, LSR) & XMTRDY) == 0 && --timeout)
102 cpu_relax(); 130 cpu_relax();
103 outb(ch, early_serial_base + TXR); 131 serial_out(early_serial_base, TXR, ch);
104 return timeout ? 0 : -1; 132 return timeout ? 0 : -1;
105} 133}
106 134
@@ -114,13 +142,28 @@ static void early_serial_write(struct console *con, const char *s, unsigned n)
114 } 142 }
115} 143}
116 144
145static __init void early_serial_hw_init(unsigned divisor)
146{
147 unsigned char c;
148
149 serial_out(early_serial_base, LCR, 0x3); /* 8n1 */
150 serial_out(early_serial_base, IER, 0); /* no interrupt */
151 serial_out(early_serial_base, FCR, 0); /* no fifo */
152 serial_out(early_serial_base, MCR, 0x3); /* DTR + RTS */
153
154 c = serial_in(early_serial_base, LCR);
155 serial_out(early_serial_base, LCR, c | DLAB);
156 serial_out(early_serial_base, DLL, divisor & 0xff);
157 serial_out(early_serial_base, DLH, (divisor >> 8) & 0xff);
158 serial_out(early_serial_base, LCR, c & ~DLAB);
159}
160
117#define DEFAULT_BAUD 9600 161#define DEFAULT_BAUD 9600
118 162
119static __init void early_serial_init(char *s) 163static __init void early_serial_init(char *s)
120{ 164{
121 unsigned char c;
122 unsigned divisor; 165 unsigned divisor;
123 unsigned baud = DEFAULT_BAUD; 166 unsigned long baud = DEFAULT_BAUD;
124 char *e; 167 char *e;
125 168
126 if (*s == ',') 169 if (*s == ',')
@@ -145,24 +188,124 @@ static __init void early_serial_init(char *s)
145 s++; 188 s++;
146 } 189 }
147 190
148 outb(0x3, early_serial_base + LCR); /* 8n1 */ 191 if (*s) {
149 outb(0, early_serial_base + IER); /* no interrupt */ 192 if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
150 outb(0, early_serial_base + FCR); /* no fifo */ 193 baud = DEFAULT_BAUD;
151 outb(0x3, early_serial_base + MCR); /* DTR + RTS */ 194 }
195
196 /* Convert from baud to divisor value */
197 divisor = 115200 / baud;
198
199 /* These will always be IO based ports */
200 serial_in = io_serial_in;
201 serial_out = io_serial_out;
202
203 /* Set up the HW */
204 early_serial_hw_init(divisor);
205}
206
207#ifdef CONFIG_PCI
208/*
209 * early_pci_serial_init()
210 *
211 * This function is invoked when the early_printk param starts with "pciserial"
212 * The rest of the param should be ",B:D.F,baud" where B, D & F describe the
213 * location of a PCI device that must be a UART device.
214 */
215static __init void early_pci_serial_init(char *s)
216{
217 unsigned divisor;
218 unsigned long baud = DEFAULT_BAUD;
219 u8 bus, slot, func;
220 uint32_t classcode, bar0;
221 uint16_t cmdreg;
222 char *e;
223
224
225 /*
226 * First, part the param to get the BDF values
227 */
228 if (*s == ',')
229 ++s;
230
231 if (*s == 0)
232 return;
233
234 bus = (u8)simple_strtoul(s, &e, 16);
235 s = e;
236 if (*s != ':')
237 return;
238 ++s;
239 slot = (u8)simple_strtoul(s, &e, 16);
240 s = e;
241 if (*s != '.')
242 return;
243 ++s;
244 func = (u8)simple_strtoul(s, &e, 16);
245 s = e;
152 246
247 /* A baud might be following */
248 if (*s == ',')
249 s++;
250
251 /*
252 * Second, find the device from the BDF
253 */
254 cmdreg = read_pci_config(bus, slot, func, PCI_COMMAND);
255 classcode = read_pci_config(bus, slot, func, PCI_CLASS_REVISION);
256 bar0 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
257
258 /*
259 * Verify it is a UART type device
260 */
261 if (((classcode >> 16 != PCI_CLASS_COMMUNICATION_MODEM) &&
262 (classcode >> 16 != PCI_CLASS_COMMUNICATION_SERIAL)) ||
263 (((classcode >> 8) & 0xff) != 0x02)) /* 16550 I/F at BAR0 */
264 return;
265
266 /*
267 * Determine if it is IO or memory mapped
268 */
269 if (bar0 & 0x01) {
270 /* it is IO mapped */
271 serial_in = io_serial_in;
272 serial_out = io_serial_out;
273 early_serial_base = bar0&0xfffffffc;
274 write_pci_config(bus, slot, func, PCI_COMMAND,
275 cmdreg|PCI_COMMAND_IO);
276 } else {
277 /* It is memory mapped - assume 32-bit alignment */
278 serial_in = mem32_serial_in;
279 serial_out = mem32_serial_out;
280 /* WARNING! assuming the address is always in the first 4G */
281 early_serial_base =
282 (unsigned long)early_ioremap(bar0 & 0xfffffff0, 0x10);
283 write_pci_config(bus, slot, func, PCI_COMMAND,
284 cmdreg|PCI_COMMAND_MEMORY);
285 }
286
287 /*
288 * Lastly, initalize the hardware
289 */
153 if (*s) { 290 if (*s) {
154 baud = simple_strtoul(s, &e, 0); 291 if (strcmp(s, "nocfg") == 0)
155 if (baud == 0 || s == e) 292 /* Sometimes, we want to leave the UART alone
293 * and assume the BIOS has set it up correctly.
294 * "nocfg" tells us this is the case, and we
295 * should do no more setup.
296 */
297 return;
298 if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
156 baud = DEFAULT_BAUD; 299 baud = DEFAULT_BAUD;
157 } 300 }
158 301
302 /* Convert from baud to divisor value */
159 divisor = 115200 / baud; 303 divisor = 115200 / baud;
160 c = inb(early_serial_base + LCR); 304
161 outb(c | DLAB, early_serial_base + LCR); 305 /* Set up the HW */
162 outb(divisor & 0xff, early_serial_base + DLL); 306 early_serial_hw_init(divisor);
163 outb((divisor >> 8) & 0xff, early_serial_base + DLH);
164 outb(c & ~DLAB, early_serial_base + LCR);
165} 307}
308#endif
166 309
167static struct console early_serial_console = { 310static struct console early_serial_console = {
168 .name = "earlyser", 311 .name = "earlyser",
@@ -210,6 +353,13 @@ static int __init setup_early_printk(char *buf)
210 early_serial_init(buf + 4); 353 early_serial_init(buf + 4);
211 early_console_register(&early_serial_console, keep); 354 early_console_register(&early_serial_console, keep);
212 } 355 }
356#ifdef CONFIG_PCI
357 if (!strncmp(buf, "pciserial", 9)) {
358 early_pci_serial_init(buf + 9);
359 early_console_register(&early_serial_console, keep);
360 buf += 9; /* Keep from match the above "serial" */
361 }
362#endif
213 if (!strncmp(buf, "vga", 3) && 363 if (!strncmp(buf, "vga", 3) &&
214 boot_params.screen_info.orig_video_isVGA == 1) { 364 boot_params.screen_info.orig_video_isVGA == 1) {
215 max_xpos = boot_params.screen_info.orig_video_cols; 365 max_xpos = boot_params.screen_info.orig_video_cols;