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>
This commit is contained in:
Родитель
d2b6f44779
Коммит
ea9e9d8029
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/usb/ehci_def.h>
|
#include <linux/usb/ehci_def.h>
|
||||||
#include <linux/efi.h>
|
#include <linux/efi.h>
|
||||||
#include <asm/efi.h>
|
#include <asm/efi.h>
|
||||||
|
#include <asm/pci_x86.h>
|
||||||
|
|
||||||
/* Simple VGA output */
|
/* Simple VGA output */
|
||||||
#define VGABASE (__ISA_IO_base + 0xb8000)
|
#define VGABASE (__ISA_IO_base + 0xb8000)
|
||||||
|
@ -76,7 +77,7 @@ static struct console early_vga_console = {
|
||||||
|
|
||||||
/* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
|
/* Serial functions loosely based on a similar package from Klaus P. Gerlicher */
|
||||||
|
|
||||||
static int early_serial_base = 0x3f8; /* ttyS0 */
|
static unsigned long early_serial_base = 0x3f8; /* ttyS0 */
|
||||||
|
|
||||||
#define XMTRDY 0x20
|
#define XMTRDY 0x20
|
||||||
|
|
||||||
|
@ -94,13 +95,40 @@ static int early_serial_base = 0x3f8; /* ttyS0 */
|
||||||
#define DLL 0 /* Divisor Latch Low */
|
#define DLL 0 /* Divisor Latch Low */
|
||||||
#define DLH 1 /* Divisor latch High */
|
#define DLH 1 /* Divisor latch High */
|
||||||
|
|
||||||
|
static void mem32_serial_out(unsigned long addr, int offset, int value)
|
||||||
|
{
|
||||||
|
uint32_t *vaddr = (uint32_t *)addr;
|
||||||
|
/* shift implied by pointer type */
|
||||||
|
writel(value, vaddr + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int mem32_serial_in(unsigned long addr, int offset)
|
||||||
|
{
|
||||||
|
uint32_t *vaddr = (uint32_t *)addr;
|
||||||
|
/* shift implied by pointer type */
|
||||||
|
return readl(vaddr + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int io_serial_in(unsigned long addr, int offset)
|
||||||
|
{
|
||||||
|
return inb(addr + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void io_serial_out(unsigned long addr, int offset, int value)
|
||||||
|
{
|
||||||
|
outb(value, addr + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int (*serial_in)(unsigned long addr, int offset) = io_serial_in;
|
||||||
|
static void (*serial_out)(unsigned long addr, int offset, int value) = io_serial_out;
|
||||||
|
|
||||||
static int early_serial_putc(unsigned char ch)
|
static int early_serial_putc(unsigned char ch)
|
||||||
{
|
{
|
||||||
unsigned timeout = 0xffff;
|
unsigned timeout = 0xffff;
|
||||||
|
|
||||||
while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
|
while ((serial_in(early_serial_base, LSR) & XMTRDY) == 0 && --timeout)
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
outb(ch, early_serial_base + TXR);
|
serial_out(early_serial_base, TXR, ch);
|
||||||
return timeout ? 0 : -1;
|
return timeout ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,13 +142,28 @@ static void early_serial_write(struct console *con, const char *s, unsigned n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __init void early_serial_hw_init(unsigned divisor)
|
||||||
|
{
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
serial_out(early_serial_base, LCR, 0x3); /* 8n1 */
|
||||||
|
serial_out(early_serial_base, IER, 0); /* no interrupt */
|
||||||
|
serial_out(early_serial_base, FCR, 0); /* no fifo */
|
||||||
|
serial_out(early_serial_base, MCR, 0x3); /* DTR + RTS */
|
||||||
|
|
||||||
|
c = serial_in(early_serial_base, LCR);
|
||||||
|
serial_out(early_serial_base, LCR, c | DLAB);
|
||||||
|
serial_out(early_serial_base, DLL, divisor & 0xff);
|
||||||
|
serial_out(early_serial_base, DLH, (divisor >> 8) & 0xff);
|
||||||
|
serial_out(early_serial_base, LCR, c & ~DLAB);
|
||||||
|
}
|
||||||
|
|
||||||
#define DEFAULT_BAUD 9600
|
#define DEFAULT_BAUD 9600
|
||||||
|
|
||||||
static __init void early_serial_init(char *s)
|
static __init void early_serial_init(char *s)
|
||||||
{
|
{
|
||||||
unsigned char c;
|
|
||||||
unsigned divisor;
|
unsigned divisor;
|
||||||
unsigned baud = DEFAULT_BAUD;
|
unsigned long baud = DEFAULT_BAUD;
|
||||||
char *e;
|
char *e;
|
||||||
|
|
||||||
if (*s == ',')
|
if (*s == ',')
|
||||||
|
@ -145,25 +188,125 @@ static __init void early_serial_init(char *s)
|
||||||
s++;
|
s++;
|
||||||
}
|
}
|
||||||
|
|
||||||
outb(0x3, early_serial_base + LCR); /* 8n1 */
|
|
||||||
outb(0, early_serial_base + IER); /* no interrupt */
|
|
||||||
outb(0, early_serial_base + FCR); /* no fifo */
|
|
||||||
outb(0x3, early_serial_base + MCR); /* DTR + RTS */
|
|
||||||
|
|
||||||
if (*s) {
|
if (*s) {
|
||||||
baud = simple_strtoul(s, &e, 0);
|
if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
|
||||||
if (baud == 0 || s == e)
|
|
||||||
baud = DEFAULT_BAUD;
|
baud = DEFAULT_BAUD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert from baud to divisor value */
|
||||||
divisor = 115200 / baud;
|
divisor = 115200 / baud;
|
||||||
c = inb(early_serial_base + LCR);
|
|
||||||
outb(c | DLAB, early_serial_base + LCR);
|
/* These will always be IO based ports */
|
||||||
outb(divisor & 0xff, early_serial_base + DLL);
|
serial_in = io_serial_in;
|
||||||
outb((divisor >> 8) & 0xff, early_serial_base + DLH);
|
serial_out = io_serial_out;
|
||||||
outb(c & ~DLAB, early_serial_base + LCR);
|
|
||||||
|
/* Set up the HW */
|
||||||
|
early_serial_hw_init(divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PCI
|
||||||
|
/*
|
||||||
|
* early_pci_serial_init()
|
||||||
|
*
|
||||||
|
* This function is invoked when the early_printk param starts with "pciserial"
|
||||||
|
* The rest of the param should be ",B:D.F,baud" where B, D & F describe the
|
||||||
|
* location of a PCI device that must be a UART device.
|
||||||
|
*/
|
||||||
|
static __init void early_pci_serial_init(char *s)
|
||||||
|
{
|
||||||
|
unsigned divisor;
|
||||||
|
unsigned long baud = DEFAULT_BAUD;
|
||||||
|
u8 bus, slot, func;
|
||||||
|
uint32_t classcode, bar0;
|
||||||
|
uint16_t cmdreg;
|
||||||
|
char *e;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, part the param to get the BDF values
|
||||||
|
*/
|
||||||
|
if (*s == ',')
|
||||||
|
++s;
|
||||||
|
|
||||||
|
if (*s == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bus = (u8)simple_strtoul(s, &e, 16);
|
||||||
|
s = e;
|
||||||
|
if (*s != ':')
|
||||||
|
return;
|
||||||
|
++s;
|
||||||
|
slot = (u8)simple_strtoul(s, &e, 16);
|
||||||
|
s = e;
|
||||||
|
if (*s != '.')
|
||||||
|
return;
|
||||||
|
++s;
|
||||||
|
func = (u8)simple_strtoul(s, &e, 16);
|
||||||
|
s = e;
|
||||||
|
|
||||||
|
/* A baud might be following */
|
||||||
|
if (*s == ',')
|
||||||
|
s++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Second, find the device from the BDF
|
||||||
|
*/
|
||||||
|
cmdreg = read_pci_config(bus, slot, func, PCI_COMMAND);
|
||||||
|
classcode = read_pci_config(bus, slot, func, PCI_CLASS_REVISION);
|
||||||
|
bar0 = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify it is a UART type device
|
||||||
|
*/
|
||||||
|
if (((classcode >> 16 != PCI_CLASS_COMMUNICATION_MODEM) &&
|
||||||
|
(classcode >> 16 != PCI_CLASS_COMMUNICATION_SERIAL)) ||
|
||||||
|
(((classcode >> 8) & 0xff) != 0x02)) /* 16550 I/F at BAR0 */
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine if it is IO or memory mapped
|
||||||
|
*/
|
||||||
|
if (bar0 & 0x01) {
|
||||||
|
/* it is IO mapped */
|
||||||
|
serial_in = io_serial_in;
|
||||||
|
serial_out = io_serial_out;
|
||||||
|
early_serial_base = bar0&0xfffffffc;
|
||||||
|
write_pci_config(bus, slot, func, PCI_COMMAND,
|
||||||
|
cmdreg|PCI_COMMAND_IO);
|
||||||
|
} else {
|
||||||
|
/* It is memory mapped - assume 32-bit alignment */
|
||||||
|
serial_in = mem32_serial_in;
|
||||||
|
serial_out = mem32_serial_out;
|
||||||
|
/* WARNING! assuming the address is always in the first 4G */
|
||||||
|
early_serial_base =
|
||||||
|
(unsigned long)early_ioremap(bar0 & 0xfffffff0, 0x10);
|
||||||
|
write_pci_config(bus, slot, func, PCI_COMMAND,
|
||||||
|
cmdreg|PCI_COMMAND_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lastly, initalize the hardware
|
||||||
|
*/
|
||||||
|
if (*s) {
|
||||||
|
if (strcmp(s, "nocfg") == 0)
|
||||||
|
/* Sometimes, we want to leave the UART alone
|
||||||
|
* and assume the BIOS has set it up correctly.
|
||||||
|
* "nocfg" tells us this is the case, and we
|
||||||
|
* should do no more setup.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
if (kstrtoul(s, 0, &baud) < 0 || baud == 0)
|
||||||
|
baud = DEFAULT_BAUD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert from baud to divisor value */
|
||||||
|
divisor = 115200 / baud;
|
||||||
|
|
||||||
|
/* Set up the HW */
|
||||||
|
early_serial_hw_init(divisor);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct console early_serial_console = {
|
static struct console early_serial_console = {
|
||||||
.name = "earlyser",
|
.name = "earlyser",
|
||||||
.write = early_serial_write,
|
.write = early_serial_write,
|
||||||
|
@ -210,6 +353,13 @@ static int __init setup_early_printk(char *buf)
|
||||||
early_serial_init(buf + 4);
|
early_serial_init(buf + 4);
|
||||||
early_console_register(&early_serial_console, keep);
|
early_console_register(&early_serial_console, keep);
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_PCI
|
||||||
|
if (!strncmp(buf, "pciserial", 9)) {
|
||||||
|
early_pci_serial_init(buf + 9);
|
||||||
|
early_console_register(&early_serial_console, keep);
|
||||||
|
buf += 9; /* Keep from match the above "serial" */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (!strncmp(buf, "vga", 3) &&
|
if (!strncmp(buf, "vga", 3) &&
|
||||||
boot_params.screen_info.orig_video_isVGA == 1) {
|
boot_params.screen_info.orig_video_isVGA == 1) {
|
||||||
max_xpos = boot_params.screen_info.orig_video_cols;
|
max_xpos = boot_params.screen_info.orig_video_cols;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче