serial: convert early_uart to earlycon for 8250

Beacuse SERIAL_PORT_DFNS is removed from include/asm-i386/serial.h and
include/asm-x86_64/serial.h.  the serial8250_ports need to be probed late in
serial initializing stage.  the console_init=>serial8250_console_init=>
register_console=>serial8250_console_setup will return -ENDEV, and console
ttyS0 can not be enabled at that time.  need to wait till uart_add_one_port in
drivers/serial/serial_core.c to call register_console to get console ttyS0.
that is too late.

Make early_uart to use early_param, so uart console can be used earlier.  Make
it to be bootconsole with CON_BOOT flag, so can use console handover feature.
and it will switch to corresponding normal serial console automatically.

new command line will be:
	console=uart8250,io,0x3f8,9600n8
	console=uart8250,mmio,0xff5e0000,115200n8
or
	earlycon=uart8250,io,0x3f8,9600n8
	earlycon=uart8250,mmio,0xff5e0000,115200n8

it will print in very early stage:
	Early serial console at I/O port 0x3f8 (options '9600n8')
	console [uart0] enabled
later for console it will print:
	console handover: boot [uart0] -> real [ttyS0]

Signed-off-by: <yinghai.lu@sun.com>
Cc: Andi Kleen <ak@suse.de>
Cc: Bjorn Helgaas <bjorn.helgaas@hp.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Gerd Hoffmann <kraxel@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Yinghai Lu 2007-07-15 23:37:59 -07:00 committed by Linus Torvalds
parent b1c931e393
commit 18a8bd949d
15 changed files with 138 additions and 101 deletions

View File

@ -462,13 +462,20 @@ and is between 256 and 4096 characters. It is defined in the file
Documentation/networking/netconsole.txt for an Documentation/networking/netconsole.txt for an
alternative. alternative.
uart,io,<addr>[,options] uart[8250],io,<addr>[,options]
uart,mmio,<addr>[,options] uart[8250],mmio,<addr>[,options]
Start an early, polled-mode console on the 8250/16550 Start an early, polled-mode console on the 8250/16550
UART at the specified I/O port or MMIO address, UART at the specified I/O port or MMIO address,
switching to the matching ttyS device later. The switching to the matching ttyS device later. The
options are the same as for ttyS, above. options are the same as for ttyS, above.
earlycon= [KNL] Output early console device and options.
uart[8250],io,<addr>[,options]
uart[8250],mmio,<addr>[,options]
Start an early, polled-mode console on the 8250/16550
UART at the specified I/O port or MMIO address.
The options are the same as for ttyS, above.
cpcihp_generic= [HW,PCI] Generic port I/O CompactPCI driver cpcihp_generic= [HW,PCI] Generic port I/O CompactPCI driver
Format: Format:
<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>] <first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]

View File

@ -390,10 +390,6 @@ early_console_setup (char *cmdline)
if (!efi_setup_pcdp_console(cmdline)) if (!efi_setup_pcdp_console(cmdline))
earlycons++; earlycons++;
#endif #endif
#ifdef CONFIG_SERIAL_8250_CONSOLE
if (!early_serial_console_init(cmdline))
earlycons++;
#endif
return (earlycons) ? 0 : -1; return (earlycons) ? 0 : -1;
} }

View File

@ -15,6 +15,7 @@
#include <linux/console.h> #include <linux/console.h>
#include <linux/efi.h> #include <linux/efi.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serial_8250.h>
#include <asm/vga.h> #include <asm/vga.h>
#include "pcdp.h" #include "pcdp.h"
@ -27,7 +28,7 @@ setup_serial_console(struct pcdp_uart *uart)
char parity; char parity;
mmio = (uart->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY); mmio = (uart->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY);
p += sprintf(p, "console=uart,%s,0x%lx", p += sprintf(p, "uart8250,%s,0x%lx",
mmio ? "mmio" : "io", uart->addr.address); mmio ? "mmio" : "io", uart->addr.address);
if (uart->baud) { if (uart->baud) {
p += sprintf(p, ",%lu", uart->baud); p += sprintf(p, ",%lu", uart->baud);
@ -41,7 +42,8 @@ setup_serial_console(struct pcdp_uart *uart)
} }
} }
return early_serial_console_init(options); add_preferred_console("uart", 8250, &options[9]);
return setup_early_serial8250_console(options);
#else #else
return -ENODEV; return -ENODEV;
#endif #endif

View File

@ -2514,12 +2514,18 @@ static int __init serial8250_console_setup(struct console *co, char *options)
return uart_set_options(port, co, baud, parity, bits, flow); return uart_set_options(port, co, baud, parity, bits, flow);
} }
static int __init serial8250_console_early_setup(void)
{
return serial8250_find_port_for_earlycon();
}
static struct uart_driver serial8250_reg; static struct uart_driver serial8250_reg;
static struct console serial8250_console = { static struct console serial8250_console = {
.name = "ttyS", .name = "ttyS",
.write = serial8250_console_write, .write = serial8250_console_write,
.device = uart_console_device, .device = uart_console_device,
.setup = serial8250_console_setup, .setup = serial8250_console_setup,
.early_setup = serial8250_console_early_setup,
.flags = CON_PRINTBUFFER, .flags = CON_PRINTBUFFER,
.index = -1, .index = -1,
.data = &serial8250_reg, .data = &serial8250_reg,
@ -2533,7 +2539,7 @@ static int __init serial8250_console_init(void)
} }
console_initcall(serial8250_console_init); console_initcall(serial8250_console_init);
static int __init find_port(struct uart_port *p) int serial8250_find_port(struct uart_port *p)
{ {
int line; int line;
struct uart_port *port; struct uart_port *port;
@ -2546,26 +2552,6 @@ static int __init find_port(struct uart_port *p)
return -ENODEV; return -ENODEV;
} }
int __init serial8250_start_console(struct uart_port *port, char *options)
{
int line;
line = find_port(port);
if (line < 0)
return -ENODEV;
add_preferred_console("ttyS", line, options);
printk("Adding console on ttyS%d at %s 0x%lx (options '%s')\n",
line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
port->iotype == UPIO_MEM ? (unsigned long) port->mapbase :
(unsigned long) port->iobase, options);
if (!(serial8250_console.flags & CON_ENABLED)) {
serial8250_console.flags &= ~CON_PRINTBUFFER;
register_console(&serial8250_console);
}
return line;
}
#define SERIAL8250_CONSOLE &serial8250_console #define SERIAL8250_CONSOLE &serial8250_console
#else #else
#define SERIAL8250_CONSOLE NULL #define SERIAL8250_CONSOLE NULL

View File

@ -17,13 +17,11 @@
* we locate the device directly by its MMIO or I/O port address. * we locate the device directly by its MMIO or I/O port address.
* *
* The user can specify the device directly, e.g., * The user can specify the device directly, e.g.,
* console=uart,io,0x3f8,9600n8 * earlycon=uart8250,io,0x3f8,9600n8
* console=uart,mmio,0xff5e0000,115200n8 * earlycon=uart8250,mmio,0xff5e0000,115200n8
* or platform code can call early_uart_console_init() to set * or
* the early UART device. * console=uart8250,io,0x3f8,9600n8
* * console=uart8250,mmio,0xff5e0000,115200n8
* After the normal serial driver starts, we try to locate the
* matching ttyS device and start a console there.
*/ */
#include <linux/tty.h> #include <linux/tty.h>
@ -32,17 +30,21 @@
#include <linux/serial_core.h> #include <linux/serial_core.h>
#include <linux/serial_reg.h> #include <linux/serial_reg.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serial_8250.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/serial.h> #include <asm/serial.h>
#ifdef CONFIG_FIX_EARLYCON_MEM
#include <asm/pgtable.h>
#include <asm/fixmap.h>
#endif
struct early_uart_device { struct early_serial8250_device {
struct uart_port port; struct uart_port port;
char options[16]; /* e.g., 115200n8 */ char options[16]; /* e.g., 115200n8 */
unsigned int baud; unsigned int baud;
}; };
static struct early_uart_device early_device __initdata; static struct early_serial8250_device early_device;
static int early_uart_registered __initdata;
static unsigned int __init serial_in(struct uart_port *port, int offset) static unsigned int __init serial_in(struct uart_port *port, int offset)
{ {
@ -80,7 +82,7 @@ static void __init putc(struct uart_port *port, int c)
serial_out(port, UART_TX, c); serial_out(port, UART_TX, c);
} }
static void __init early_uart_write(struct console *console, const char *s, unsigned int count) static void __init early_serial8250_write(struct console *console, const char *s, unsigned int count)
{ {
struct uart_port *port = &early_device.port; struct uart_port *port = &early_device.port;
unsigned int ier; unsigned int ier;
@ -111,7 +113,7 @@ static unsigned int __init probe_baud(struct uart_port *port)
return (port->uartclk / 16) / quot; return (port->uartclk / 16) / quot;
} }
static void __init init_port(struct early_uart_device *device) static void __init init_port(struct early_serial8250_device *device)
{ {
struct uart_port *port = &device->port; struct uart_port *port = &device->port;
unsigned int divisor; unsigned int divisor;
@ -130,10 +132,9 @@ static void __init init_port(struct early_uart_device *device)
serial_out(port, UART_LCR, c & ~UART_LCR_DLAB); serial_out(port, UART_LCR, c & ~UART_LCR_DLAB);
} }
static int __init parse_options(struct early_uart_device *device, char *options) static int __init parse_options(struct early_serial8250_device *device, char *options)
{ {
struct uart_port *port = &device->port; struct uart_port *port = &device->port;
int mapsize = 64;
int mmio, length; int mmio, length;
if (!options) if (!options)
@ -143,12 +144,18 @@ static int __init parse_options(struct early_uart_device *device, char *options)
if (!strncmp(options, "mmio,", 5)) { if (!strncmp(options, "mmio,", 5)) {
port->iotype = UPIO_MEM; port->iotype = UPIO_MEM;
port->mapbase = simple_strtoul(options + 5, &options, 0); port->mapbase = simple_strtoul(options + 5, &options, 0);
port->membase = ioremap(port->mapbase, mapsize); #ifdef CONFIG_FIX_EARLYCON_MEM
set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, port->mapbase & PAGE_MASK);
port->membase = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
port->membase += port->mapbase & ~PAGE_MASK;
#else
port->membase = ioremap(port->mapbase, 64);
if (!port->membase) { if (!port->membase) {
printk(KERN_ERR "%s: Couldn't ioremap 0x%lx\n", printk(KERN_ERR "%s: Couldn't ioremap 0x%lx\n",
__FUNCTION__, port->mapbase); __FUNCTION__, port->mapbase);
return -ENOMEM; return -ENOMEM;
} }
#endif
mmio = 1; mmio = 1;
} else if (!strncmp(options, "io,", 3)) { } else if (!strncmp(options, "io,", 3)) {
port->iotype = UPIO_PORT; port->iotype = UPIO_PORT;
@ -175,9 +182,16 @@ static int __init parse_options(struct early_uart_device *device, char *options)
return 0; return 0;
} }
static int __init early_uart_setup(struct console *console, char *options) static struct console early_serial8250_console __initdata = {
.name = "uart",
.write = early_serial8250_write,
.flags = CON_PRINTBUFFER | CON_BOOT,
.index = -1,
};
static int __init early_serial8250_setup(char *options)
{ {
struct early_uart_device *device = &early_device; struct early_serial8250_device *device = &early_device;
int err; int err;
if (device->port.membase || device->port.iobase) if (device->port.membase || device->port.iobase)
@ -190,61 +204,48 @@ static int __init early_uart_setup(struct console *console, char *options)
return 0; return 0;
} }
static struct console early_uart_console __initdata = { int __init setup_early_serial8250_console(char *cmdline)
.name = "uart",
.write = early_uart_write,
.setup = early_uart_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
static int __init early_uart_console_init(void)
{
if (!early_uart_registered) {
register_console(&early_uart_console);
early_uart_registered = 1;
}
return 0;
}
console_initcall(early_uart_console_init);
int __init early_serial_console_init(char *cmdline)
{ {
char *options; char *options;
int err; int err;
options = strstr(cmdline, "console=uart,"); options = strstr(cmdline, "uart8250,");
if (!options) {
options = strstr(cmdline, "uart,");
if (!options) if (!options)
return -ENODEV; return 0;
}
options = strchr(cmdline, ',') + 1; options = strchr(cmdline, ',') + 1;
if ((err = early_uart_setup(NULL, options)) < 0) if ((err = early_serial8250_setup(options)) < 0)
return err; return err;
return early_uart_console_init();
register_console(&early_serial8250_console);
return 0;
} }
static int __init early_uart_console_switch(void) int __init serial8250_find_port_for_earlycon(void)
{ {
struct early_uart_device *device = &early_device; struct early_serial8250_device *device = &early_device;
struct uart_port *port = &device->port; struct uart_port *port = &device->port;
int mmio, line; int line;
int ret;
if (!(early_uart_console.flags & CON_ENABLED)) if (!device->port.membase && !device->port.iobase)
return 0; return -ENODEV;
/* Try to start the normal driver on a matching line. */ line = serial8250_find_port(port);
mmio = (port->iotype == UPIO_MEM);
line = serial8250_start_console(port, device->options);
if (line < 0) if (line < 0)
printk("No ttyS device at %s 0x%lx for console\n", return -ENODEV;
mmio ? "MMIO" : "I/O port",
mmio ? port->mapbase :
(unsigned long) port->iobase);
unregister_console(&early_uart_console); ret = update_console_cmdline("uart", 8250,
if (mmio) "ttyS", line, device->options);
iounmap(port->membase); if (ret < 0)
ret = update_console_cmdline("uart", 0,
"ttyS", line, device->options);
return 0; return ret;
} }
late_initcall(early_uart_console_switch);
early_param("earlycon", setup_early_serial8250_console);

View File

@ -62,8 +62,22 @@ config SERIAL_8250_CONSOLE
kernel will automatically use the first serial line, /dev/ttyS0, as kernel will automatically use the first serial line, /dev/ttyS0, as
system console. system console.
you can set that using a kernel command line option such as
"console=uart8250,io,0x3f8,9600n8"
"console=uart8250,mmio,0xff5e0000,115200n8".
and it will switch to normal serial console when correponding port is
ready.
"earlycon=uart8250,io,0x3f8,9600n8"
"earlycon=uart8250,mmio,0xff5e0000,115200n8".
it will not only setup early console.
If unsure, say N. If unsure, say N.
config FIX_EARLYCON_MEM
bool
depends on X86
default y
config SERIAL_8250_GSC config SERIAL_8250_GSC
tristate tristate
depends on SERIAL_8250 && GSC depends on SERIAL_8250 && GSC

View File

@ -54,6 +54,8 @@ extern unsigned long __FIXADDR_TOP;
enum fixed_addresses { enum fixed_addresses {
FIX_HOLE, FIX_HOLE,
FIX_VDSO, FIX_VDSO,
FIX_DBGP_BASE,
FIX_EARLYCON_MEM_BASE,
#ifdef CONFIG_X86_LOCAL_APIC #ifdef CONFIG_X86_LOCAL_APIC
FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */
#endif #endif

View File

@ -129,6 +129,7 @@ extern void iounmap(volatile void __iomem *addr);
*/ */
extern void *bt_ioremap(unsigned long offset, unsigned long size); extern void *bt_ioremap(unsigned long offset, unsigned long size);
extern void bt_iounmap(void *addr, unsigned long size); extern void bt_iounmap(void *addr, unsigned long size);
extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys);
/* Use early IO mappings for DMI because it's initialized early */ /* Use early IO mappings for DMI because it's initialized early */
#define dmi_ioremap bt_ioremap #define dmi_ioremap bt_ioremap

View File

@ -35,6 +35,8 @@ enum fixed_addresses {
VSYSCALL_LAST_PAGE, VSYSCALL_LAST_PAGE,
VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1, VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1,
VSYSCALL_HPET, VSYSCALL_HPET,
FIX_DBGP_BASE,
FIX_EARLYCON_MEM_BASE,
FIX_HPET_BASE, FIX_HPET_BASE,
FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */
FIX_IO_APIC_BASE_0, FIX_IO_APIC_BASE_0,

View File

@ -144,6 +144,7 @@ extern void early_iounmap(void *addr, unsigned long size);
*/ */
extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size); extern void __iomem * ioremap_nocache (unsigned long offset, unsigned long size);
extern void iounmap(volatile void __iomem *addr); extern void iounmap(volatile void __iomem *addr);
extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys);
/* /*
* ISA I/O bus memory addresses are 1:1 with the physical address. * ISA I/O bus memory addresses are 1:1 with the physical address.

View File

@ -99,6 +99,7 @@ struct console {
struct tty_driver *(*device)(struct console *, int *); struct tty_driver *(*device)(struct console *, int *);
void (*unblank)(void); void (*unblank)(void);
int (*setup)(struct console *, char *); int (*setup)(struct console *, char *);
int (*early_setup)(void);
short flags; short flags;
short index; short index;
int cflag; int cflag;
@ -107,6 +108,7 @@ struct console {
}; };
extern int add_preferred_console(char *name, int idx, char *options); extern int add_preferred_console(char *name, int idx, char *options);
extern int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options);
extern void register_console(struct console *); extern void register_console(struct console *);
extern int unregister_console(struct console *); extern int unregister_console(struct console *);
extern struct console *console_drivers; extern struct console *console_drivers;

View File

@ -177,11 +177,5 @@ struct serial_icounter_struct {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/compiler.h> #include <linux/compiler.h>
/* Allow architectures to override entries in serial8250_ports[] at run time: */
struct uart_port; /* forward declaration */
extern int early_serial_setup(struct uart_port *port);
extern int early_serial_console_init(char *options);
extern int serial8250_start_console(struct uart_port *port, char *options);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_SERIAL_H */ #endif /* _LINUX_SERIAL_H */

View File

@ -60,4 +60,8 @@ void serial8250_unregister_port(int line);
void serial8250_suspend_port(int line); void serial8250_suspend_port(int line);
void serial8250_resume_port(int line); void serial8250_resume_port(int line);
extern int serial8250_find_port(struct uart_port *p);
extern int serial8250_find_port_for_earlycon(void);
extern int setup_early_serial8250_console(char *cmdline);
#endif #endif

View File

@ -453,7 +453,10 @@ static int __init do_early_param(char *param, char *val)
struct obs_kernel_param *p; struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) { for (p = __setup_start; p < __setup_end; p++) {
if (p->early && strcmp(param, p->str) == 0) { if ((p->early && strcmp(param, p->str) == 0) ||
(strcmp(param, "console") == 0 &&
strcmp(p->str, "earlycon") == 0)
) {
if (p->setup_func(val) != 0) if (p->setup_func(val) != 0)
printk(KERN_WARNING printk(KERN_WARNING
"Malformed early option '%s'\n", param); "Malformed early option '%s'\n", param);

View File

@ -726,6 +726,25 @@ int __init add_preferred_console(char *name, int idx, char *options)
return 0; return 0;
} }
int __init update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options)
{
struct console_cmdline *c;
int i;
for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
if (strcmp(console_cmdline[i].name, name) == 0 &&
console_cmdline[i].index == idx) {
c = &console_cmdline[i];
memcpy(c->name, name_new, sizeof(c->name));
c->name[sizeof(c->name) - 1] = 0;
c->options = options;
c->index = idx_new;
return i;
}
/* not found */
return -1;
}
#ifndef CONFIG_DISABLE_CONSOLE_SUSPEND #ifndef CONFIG_DISABLE_CONSOLE_SUSPEND
/** /**
* suspend_console - suspend the console subsystem * suspend_console - suspend the console subsystem
@ -942,6 +961,9 @@ void register_console(struct console *console)
if (preferred_console < 0 || bootconsole || !console_drivers) if (preferred_console < 0 || bootconsole || !console_drivers)
preferred_console = selected_console; preferred_console = selected_console;
if (console->early_setup)
console->early_setup();
/* /*
* See if we want to use this console driver. If we * See if we want to use this console driver. If we
* didn't select a console we take the first one * didn't select a console we take the first one