Commit 18a8bd94 authored by Yinghai Lu's avatar Yinghai Lu Committed by Linus Torvalds
Browse files

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: default avatar <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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b1c931e3
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -462,13 +462,20 @@ and is between 256 and 4096 characters. It is defined in the file
			Documentation/networking/netconsole.txt for an
			alternative.

		uart,io,<addr>[,options]
		uart,mmio,<addr>[,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,
			switching to the matching ttyS device later.  The
			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
			Format:
			<first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>]
+0 −4
Original line number Diff line number Diff line
@@ -390,10 +390,6 @@ early_console_setup (char *cmdline)
	if (!efi_setup_pcdp_console(cmdline))
		earlycons++;
#endif
#ifdef CONFIG_SERIAL_8250_CONSOLE
	if (!early_serial_console_init(cmdline))
		earlycons++;
#endif

	return (earlycons) ? 0 : -1;
}
+4 −2
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/console.h>
#include <linux/efi.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include <asm/vga.h>
#include "pcdp.h"

@@ -27,7 +28,7 @@ setup_serial_console(struct pcdp_uart *uart)
	char parity;

	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);
	if (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
	return -ENODEV;
#endif
+7 −21
Original line number Diff line number Diff line
@@ -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);
}

static int __init serial8250_console_early_setup(void)
{
	return serial8250_find_port_for_earlycon();
}

static struct uart_driver serial8250_reg;
static struct console serial8250_console = {
	.name		= "ttyS",
	.write		= serial8250_console_write,
	.device		= uart_console_device,
	.setup		= serial8250_console_setup,
	.early_setup	= serial8250_console_early_setup,
	.flags		= CON_PRINTBUFFER,
	.index		= -1,
	.data		= &serial8250_reg,
@@ -2533,7 +2539,7 @@ static int __init serial8250_console_init(void)
}
console_initcall(serial8250_console_init);

static int __init find_port(struct uart_port *p)
int serial8250_find_port(struct uart_port *p)
{
	int line;
	struct uart_port *port;
@@ -2546,26 +2552,6 @@ static int __init find_port(struct uart_port *p)
	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
#else
#define SERIAL8250_CONSOLE	NULL
+59 −58
Original line number Diff line number Diff line
@@ -17,13 +17,11 @@
 * we locate the device directly by its MMIO or I/O port address.
 *
 * The user can specify the device directly, e.g.,
 *	console=uart,io,0x3f8,9600n8
 *	console=uart,mmio,0xff5e0000,115200n8
 * or platform code can call early_uart_console_init() to set
 * the early UART device.
 *
 * After the normal serial driver starts, we try to locate the
 * matching ttyS device and start a console there.
 *	earlycon=uart8250,io,0x3f8,9600n8
 *	earlycon=uart8250,mmio,0xff5e0000,115200n8
 * or
 *	console=uart8250,io,0x3f8,9600n8
 *	console=uart8250,mmio,0xff5e0000,115200n8
 */

#include <linux/tty.h>
@@ -32,17 +30,21 @@
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
#include <linux/serial_8250.h>
#include <asm/io.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;
	char options[16];		/* e.g., 115200n8 */
	unsigned int baud;
};

static struct early_uart_device early_device __initdata;
static int early_uart_registered __initdata;
static struct early_serial8250_device early_device;

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);
}

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;
	unsigned int ier;
@@ -111,7 +113,7 @@ static unsigned int __init probe_baud(struct uart_port *port)
	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;
	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);
}

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;
	int mapsize = 64;
	int mmio, length;

	if (!options)
@@ -143,12 +144,18 @@ static int __init parse_options(struct early_uart_device *device, char *options)
	if (!strncmp(options, "mmio,", 5)) {
		port->iotype = UPIO_MEM;
		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) {
			printk(KERN_ERR "%s: Couldn't ioremap 0x%lx\n",
				__FUNCTION__, port->mapbase);
			return -ENOMEM;
		}
#endif
		mmio = 1;
	} else if (!strncmp(options, "io,", 3)) {
		port->iotype = UPIO_PORT;
@@ -175,9 +182,16 @@ static int __init parse_options(struct early_uart_device *device, char *options)
	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;

	if (device->port.membase || device->port.iobase)
@@ -190,61 +204,48 @@ static int __init early_uart_setup(struct console *console, char *options)
	return 0;
}

static struct console early_uart_console __initdata = {
	.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)
int __init setup_early_serial8250_console(char *cmdline)
{
	char *options;
	int err;

	options = strstr(cmdline, "console=uart,");
	options = strstr(cmdline, "uart8250,");
	if (!options) {
		options = strstr(cmdline, "uart,");
		if (!options)
		return -ENODEV;
			return 0;
	}

	options = strchr(cmdline, ',') + 1;
	if ((err = early_uart_setup(NULL, options)) < 0)
	if ((err = early_serial8250_setup(options)) < 0)
		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;
	int mmio, line;
	int line;
	int ret;

	if (!(early_uart_console.flags & CON_ENABLED))
		return 0;
	if (!device->port.membase && !device->port.iobase)
		return -ENODEV;

	/* Try to start the normal driver on a matching line.  */
	mmio = (port->iotype == UPIO_MEM);
	line = serial8250_start_console(port, device->options);
	line = serial8250_find_port(port);
	if (line < 0)
		printk("No ttyS device at %s 0x%lx for console\n",
			mmio ? "MMIO" : "I/O port",
			mmio ? port->mapbase :
			    (unsigned long) port->iobase);
		return -ENODEV;

	unregister_console(&early_uart_console);
	if (mmio)
		iounmap(port->membase);
	ret = update_console_cmdline("uart", 8250,
			     "ttyS", line, device->options);
	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);
Loading