diff options
author | Stuart R. Anderson <stuart.r.anderson@intel.com> | 2015-01-20 07:07:57 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-02-02 13:11:27 -0500 |
commit | ea9e9d8029020d438b0717ffddf65140fda16051 (patch) | |
tree | f711ae69a62aad6b1d186f2e581c5147e25fcdaf /arch/x86/kernel/early_printk.c | |
parent | d2b6f44779d3be22d32a5697bd30b59367fd2b33 (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.c | 182 |
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 | ||
79 | static int early_serial_base = 0x3f8; /* ttyS0 */ | 80 | static 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 | ||
98 | static 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 | |||
105 | static 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 | |||
112 | static unsigned int io_serial_in(unsigned long addr, int offset) | ||
113 | { | ||
114 | return inb(addr + offset); | ||
115 | } | ||
116 | |||
117 | static void io_serial_out(unsigned long addr, int offset, int value) | ||
118 | { | ||
119 | outb(value, addr + offset); | ||
120 | } | ||
121 | |||
122 | static unsigned int (*serial_in)(unsigned long addr, int offset) = io_serial_in; | ||
123 | static void (*serial_out)(unsigned long addr, int offset, int value) = io_serial_out; | ||
124 | |||
97 | static int early_serial_putc(unsigned char ch) | 125 | static 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 | ||
145 | static __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 | ||
119 | static __init void early_serial_init(char *s) | 163 | static __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 | */ | ||
215 | static __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 | ||
167 | static struct console early_serial_console = { | 310 | static 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; |