From 9aac5887595b765b6f64b2af08b785e82e095b57 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 18 Apr 2014 17:19:55 -0500 Subject: tty/serial: add generic serial earlycon This introduces generic earlycon infrastructure for serial devices based on the 8250 earlycon. This allows for supporting earlycon option with other serial devices. The earlycon output is enabled at the time early_params are processed. Only architectures that have fixmap support or have functional ioremap when early_params are processed are supported. This is the same restriction that the 8250 driver had. Signed-off-by: Rob Herring Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 152 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 drivers/tty/serial/earlycon.c (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c new file mode 100644 index 000000000000..73bf1e21aae0 --- /dev/null +++ b/drivers/tty/serial/earlycon.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2014 Linaro Ltd. + * Author: Rob Herring + * + * Based on 8250 earlycon: + * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. + * Bjorn Helgaas + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#ifdef CONFIG_FIX_EARLYCON_MEM +#include +#endif + +#include + +static struct console early_con = { + .name = "earlycon", + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +static struct earlycon_device early_console_dev = { + .con = &early_con, +}; + +static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) +{ + void __iomem *base; +#ifdef CONFIG_FIX_EARLYCON_MEM + set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK); + base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE); + base += paddr & ~PAGE_MASK; +#else + base = ioremap(paddr, size); +#endif + if (!base) + pr_err("%s: Couldn't map 0x%llx\n", __func__, + (unsigned long long)paddr); + + return base; +} + +static int __init parse_options(struct earlycon_device *device, + char *options) +{ + struct uart_port *port = &device->port; + int mmio, mmio32, length, ret; + unsigned long addr; + + if (!options) + return -ENODEV; + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + options += mmio ? 5 : 7; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->mapbase = addr; + if (mmio32) + port->regshift = 2; + } else if (!strncmp(options, "io,", 3)) { + port->iotype = UPIO_PORT; + options += 3; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->iobase = addr; + mmio = 0; + } else if (!strncmp(options, "0x", 2)) { + port->iotype = UPIO_MEM; + ret = kstrtoul(options, 0, &addr); + if (ret) + return ret; + port->mapbase = addr; + } else { + return -EINVAL; + } + + port->uartclk = BASE_BAUD * 16; + + options = strchr(options, ','); + if (options) { + options++; + ret = kstrtouint(options, 0, &device->baud); + if (ret) + return ret; + length = min(strcspn(options, " ") + 1, + (size_t)(sizeof(device->options))); + strlcpy(device->options, options, length); + } + + if (mmio || mmio32) + pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (unsigned long long)port->mapbase, + device->options); + else + pr_info("Early serial console at I/O port 0x%lx (options '%s')\n", + port->iobase, + device->options); + + return 0; +} + +int __init setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + size_t len; + struct uart_port *port = &early_console_dev.port; + + if (!buf || !match || !setup) + return 0; + + len = strlen(match); + if (strncmp(buf, match, len)) + return 0; + if (buf[len] && (buf[len] != ',')) + return 0; + + buf += len + 1; + + err = parse_options(&early_console_dev, buf); + /* On parsing error, pass the options buf to the setup function */ + if (!err) + buf = NULL; + + if (port->mapbase) + port->membase = earlycon_map(port->mapbase, 64); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, buf); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + register_console(early_console_dev.con); + return 0; +} -- cgit v1.2.3-55-g7522 From e26f1db9b8d74617519e50b41749900d0a257406 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 30 Apr 2014 19:48:29 -0500 Subject: tty/serial: fix generic earlycon option parsing Commit 9aac5887595 (tty/serial: add generic serial earlycon) moved console option parsing from 8250_early.c and converted to kstrto* functions from simple_strtoul along the way. However, kstrto* functions are not equivalent in that they do not allow non-convertible characters at the end such as "115200n8". Fix this by changing back to simple_strtoul and ignore what checkpatch.pl says. Reported-by: Yinghai Lu Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 73bf1e21aae0..c92e83088adb 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -53,7 +53,7 @@ static int __init parse_options(struct earlycon_device *device, char *options) { struct uart_port *port = &device->port; - int mmio, mmio32, length, ret; + int mmio, mmio32, length; unsigned long addr; if (!options) @@ -64,25 +64,19 @@ static int __init parse_options(struct earlycon_device *device, if (mmio || mmio32) { port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); options += mmio ? 5 : 7; - ret = kstrtoul(options, 0, &addr); - if (ret) - return ret; + addr = simple_strtoul(options, NULL, 0); port->mapbase = addr; if (mmio32) port->regshift = 2; } else if (!strncmp(options, "io,", 3)) { port->iotype = UPIO_PORT; options += 3; - ret = kstrtoul(options, 0, &addr); - if (ret) - return ret; + addr = simple_strtoul(options, NULL, 0); port->iobase = addr; mmio = 0; } else if (!strncmp(options, "0x", 2)) { port->iotype = UPIO_MEM; - ret = kstrtoul(options, 0, &addr); - if (ret) - return ret; + addr = simple_strtoul(options, NULL, 0); port->mapbase = addr; } else { return -EINVAL; @@ -93,9 +87,7 @@ static int __init parse_options(struct earlycon_device *device, options = strchr(options, ','); if (options) { options++; - ret = kstrtouint(options, 0, &device->baud); - if (ret) - return ret; + device->baud = simple_strtoul(options, NULL, 0); length = min(strcspn(options, " ") + 1, (size_t)(sizeof(device->options))); strlcpy(device->options, options, length); -- cgit v1.2.3-55-g7522 From b0b6abd34c1b508d4ac95dbc614f36c49d29e65a Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 27 Mar 2014 08:06:16 -0500 Subject: serial: earlycon: add DT support This adds the infrastructure to generic earlycon for earlycon setup using DT. The actual setup is not enabled until a following commit to add the FDT parsing. Signed-off-by: Rob Herring Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Arnd Bergmann Acked-by: Grant Likely --- drivers/tty/serial/earlycon.c | 28 ++++++++++++++++++++++++++++ include/asm-generic/vmlinux.lds.h | 4 +++- include/linux/serial_core.h | 6 ++++++ 3 files changed, 37 insertions(+), 1 deletion(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index c92e83088adb..5131b5ee6164 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #ifdef CONFIG_FIX_EARLYCON_MEM #include @@ -32,6 +34,9 @@ static struct earlycon_device early_console_dev = { .con = &early_con, }; +static const struct of_device_id __earlycon_of_table_sentinel + __used __section(__earlycon_of_table_end); + static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) { void __iomem *base; @@ -142,3 +147,26 @@ int __init setup_earlycon(char *buf, const char *match, register_console(early_console_dev.con); return 0; } + +int __init of_setup_earlycon(unsigned long addr, + int (*setup)(struct earlycon_device *, const char *)) +{ + int err; + struct uart_port *port = &early_console_dev.port; + + port->iotype = UPIO_MEM; + port->mapbase = addr; + port->uartclk = BASE_BAUD * 16; + port->membase = earlycon_map(addr, SZ_4K); + + early_console_dev.con->data = &early_console_dev; + err = setup(&early_console_dev, NULL); + if (err < 0) + return err; + if (!early_console_dev.con->write) + return -ENODEV; + + + register_console(early_console_dev.con); + return 0; +} diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index b9404f6590f1..d647637cd699 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -155,6 +155,7 @@ #define CLK_OF_TABLES() OF_TABLE(CONFIG_COMMON_CLK, clk) #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) +#define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) #define KERNEL_DTB() \ STRUCT_ALIGN(); \ @@ -483,7 +484,8 @@ CLKSRC_OF_TABLES() \ CPU_METHOD_OF_TABLES() \ KERNEL_DTB() \ - IRQCHIP_OF_MATCH_TABLE() + IRQCHIP_OF_MATCH_TABLE() \ + EARLYCON_OF_TABLES() #define INIT_TEXT \ *(.init.text) \ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 7a15b5b24c0b..5bbb809ee197 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -294,6 +294,9 @@ struct earlycon_device { int setup_earlycon(char *buf, const char *match, int (*setup)(struct earlycon_device *, const char *)); +extern int of_setup_earlycon(unsigned long addr, + int (*setup)(struct earlycon_device *, const char *)); + #define EARLYCON_DECLARE(name, func) \ static int __init name ## _setup_earlycon(char *buf) \ { \ @@ -301,6 +304,9 @@ static int __init name ## _setup_earlycon(char *buf) \ } \ early_param("earlycon", name ## _setup_earlycon); +#define OF_EARLYCON_DECLARE(name, compat, fn) \ + _OF_DECLARE(earlycon, name, compat, fn, void *) + struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct console *c); void uart_parse_options(char *options, int *baud, int *parity, int *bits, -- cgit v1.2.3-55-g7522