From 73abaf87f01be6fa6da3c0aa9c138a1b6b281068 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sun, 1 Mar 2015 11:05:46 -0500 Subject: serial: earlycon: Refactor parse_options into serial core Prepare to support console-defined matching; refactor the command line parameter string processing from parse_options() into a new core function, uart_parse_earlycon(), which decodes command line parameters of the form: earlycon=,io|mmio|mmio32,, console=,io|mmio|mmio32,, earlycon=,0x, console=,0x, Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 64fe25a4285c..58d6bcdaf31c 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -54,44 +54,31 @@ static void __iomem * __init earlycon_map(unsigned long paddr, size_t size) return base; } -static int __init parse_options(struct earlycon_device *device, - char *options) +static int __init parse_options(struct earlycon_device *device, char *options) { struct uart_port *port = &device->port; - int mmio, mmio32, length; + int length; unsigned long addr; - if (!options) - return -ENODEV; + if (uart_parse_earlycon(options, &port->iotype, &addr, &options)) + return -EINVAL; - mmio = !strncmp(options, "mmio,", 5); - mmio32 = !strncmp(options, "mmio32,", 7); - if (mmio || mmio32) { - port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); - options += mmio ? 5 : 7; - addr = simple_strtoul(options, NULL, 0); + switch (port->iotype) { + case UPIO_MEM32: + port->regshift = 2; /* fall-through */ + case UPIO_MEM: port->mapbase = addr; - if (mmio32) - port->regshift = 2; - } else if (!strncmp(options, "io,", 3)) { - port->iotype = UPIO_PORT; - options += 3; - addr = simple_strtoul(options, NULL, 0); + break; + case UPIO_PORT: port->iobase = addr; - mmio = 0; - } else if (!strncmp(options, "0x", 2)) { - port->iotype = UPIO_MEM; - addr = simple_strtoul(options, NULL, 0); - port->mapbase = addr; - } else { + break; + default: return -EINVAL; } port->uartclk = BASE_BAUD * 16; - options = strchr(options, ','); if (options) { - options++; device->baud = simple_strtoul(options, NULL, 0); length = min(strcspn(options, " ") + 1, (size_t)(sizeof(device->options))); @@ -100,7 +87,7 @@ static int __init parse_options(struct earlycon_device *device, if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32) pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n", - mmio32 ? "32" : "", + (port->iotype == UPIO_MEM32) ? "32" : "", (unsigned long long)port->mapbase, device->options); else -- cgit v1.2.3 From feed5bab0d33feb5dbe5365b34d044c79f93dc6b Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:15 -0400 Subject: serial: earlycon: Move ->uartclk initialize Initializing the ->uartclk field is not related to option parsing; relocate from parse_options() to setup_earlycon() (which mirrors the behavior of of_setup_earlycon()). Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 58d6bcdaf31c..0480c8f24cbb 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -76,8 +76,6 @@ static int __init parse_options(struct earlycon_device *device, char *options) return -EINVAL; } - port->uartclk = BASE_BAUD * 16; - if (options) { device->baud = simple_strtoul(options, NULL, 0); length = min(strcspn(options, " ") + 1, @@ -121,6 +119,7 @@ int __init setup_earlycon(char *buf, const char *match, if (!err) buf = NULL; + port->uartclk = BASE_BAUD * 16; if (port->mapbase) port->membase = earlycon_map(port->mapbase, 64); -- cgit v1.2.3 From 526ebc3f56491fe971cd2b51cfc859a879e11e93 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:18 -0400 Subject: serial: earlycon: Ignore parse_options() error code Because setup_earlycon() continues to attempt console registration if an error occurred parsing the option string, the actual value of the error code from parse_options() is ignored. Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 0480c8f24cbb..da5e8c8c4ad4 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -114,9 +114,8 @@ int __init setup_earlycon(char *buf, const char *match, buf += len + 1; - err = parse_options(&early_console_dev, buf); /* On parsing error, pass the options buf to the setup function */ - if (!err) + if (!parse_options(&early_console_dev, buf)) buf = NULL; port->uartclk = BASE_BAUD * 16; -- cgit v1.2.3 From 60846880384f00713c7f54243f749c1923e6c0e3 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:19 -0400 Subject: serial: earlycon: Skip parse_options() if empty string Earlycon param strings of the form earlycon= are rejected from parse_options() with an error (which, in turn, results in a NULL argument for the setup() method options parameter). Only pass non-empty string to parse_options(); this will enable handling actual parse errors differently than expected and allow formats. Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index da5e8c8c4ad4..025ea0140896 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -109,13 +109,16 @@ int __init setup_earlycon(char *buf, const char *match, len = strlen(match); if (strncmp(buf, match, len)) return 0; - if (buf[len] && (buf[len] != ',')) - return 0; - buf += len + 1; + if (buf[len]) { + if (buf[len] != ',') + return 0; + buf += len + 1; + } else + buf = NULL; /* On parsing error, pass the options buf to the setup function */ - if (!parse_options(&early_console_dev, buf)) + if (buf && !parse_options(&early_console_dev, buf)) buf = NULL; port->uartclk = BASE_BAUD * 16; -- cgit v1.2.3 From 7c53cb3de493573dc3b7f2468a542a9f11cc5079 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:20 -0400 Subject: serial: earlycon: Refactor earlycon registration Separate earlycon matching from registration; add register_earlycon which initializes and registers the matched earlycon. Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 025ea0140896..9fb76b66c545 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -96,27 +96,13 @@ static int __init parse_options(struct earlycon_device *device, char *options) return 0; } -int __init setup_earlycon(char *buf, const char *match, - int (*setup)(struct earlycon_device *, const char *)) + +static int __init +register_earlycon(char *buf, 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]) { - if (buf[len] != ',') - return 0; - buf += len + 1; - } else - buf = NULL; - /* On parsing error, pass the options buf to the setup function */ if (buf && !parse_options(&early_console_dev, buf)) buf = NULL; @@ -136,6 +122,28 @@ int __init setup_earlycon(char *buf, const char *match, return 0; } +int __init setup_earlycon(char *buf, const char *match, + int (*setup)(struct earlycon_device *, const char *)) +{ + size_t len; + + if (!buf || !match || !setup) + return 0; + + len = strlen(match); + if (strncmp(buf, match, len)) + return 0; + + if (buf[len]) { + if (buf[len] != ',') + return 0; + buf += len + 1; + } else + buf = NULL; + + return register_earlycon(buf, setup); +} + int __init of_setup_earlycon(unsigned long addr, int (*setup)(struct earlycon_device *, const char *)) { -- cgit v1.2.3 From 470ca0de69feaba5df215ad804cec1859883a5ed Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Mon, 9 Mar 2015 16:27:21 -0400 Subject: serial: earlycon: Enable earlycon without command line param Earlycon matching can only be triggered if 'earlycon=...' has been specified on the kernel command line. To workaround this limitation requires tight coupling between arches and specific serial drivers in order to start an earlycon. Devicetree avoids this limitation with a link table that contains the required data to match earlycons. Mirror this approach for earlycon match by name. Re-purpose EARLYCON_DECLARE to generate a table entry which associates name with setup() function. Re-purpose setup_earlycon() to scan this table for an earlycon match, which is registered if found. Declare one "earlycon" early_param, which calls setup_earlycon(). This design allows setup_earlycon() to be called directly with a param string (as if 'earlycon=...' had been set on the command line). Re-registration (either directly or by early_param) is prevented. Acked-by: Rob Herring Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 7 +-- drivers/tty/serial/earlycon.c | 92 ++++++++++++++++++++++++++++-------- include/asm-generic/vmlinux.lds.h | 9 ++++ include/linux/serial_core.h | 19 ++++---- 4 files changed, 94 insertions(+), 33 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index b199c10689f4..d272139a5729 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -170,10 +170,5 @@ EARLYCON_DECLARE(uart, early_serial8250_setup); int __init setup_early_serial8250_console(char *cmdline) { - char match[] = "uart8250"; - - if (cmdline && cmdline[4] == ',') - match[4] = '\0'; - - return setup_earlycon(cmdline, match, early_serial8250_setup); + return setup_earlycon(cmdline); } diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 9fb76b66c545..5fdc9f3ecd64 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -10,6 +10,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -34,6 +37,10 @@ static struct earlycon_device early_console_dev = { .con = &early_con, }; +extern struct earlycon_id __earlycon_table[]; +static const struct earlycon_id __earlycon_table_sentinel + __used __section(__earlycon_table_end); + static const struct of_device_id __earlycon_of_table_sentinel __used __section(__earlycon_of_table_end); @@ -96,9 +103,7 @@ static int __init parse_options(struct earlycon_device *device, char *options) return 0; } - -static int __init -register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *)) +static int __init register_earlycon(char *buf, const struct earlycon_id *match) { int err; struct uart_port *port = &early_console_dev.port; @@ -112,7 +117,7 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char * port->membase = earlycon_map(port->mapbase, 64); early_console_dev.con->data = &early_console_dev; - err = setup(&early_console_dev, buf); + err = match->setup(&early_console_dev, buf); if (err < 0) return err; if (!early_console_dev.con->write) @@ -122,27 +127,76 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char * return 0; } -int __init setup_earlycon(char *buf, const char *match, - int (*setup)(struct earlycon_device *, const char *)) +/** + * setup_earlycon - match and register earlycon console + * @buf: earlycon param string + * + * Registers the earlycon console matching the earlycon specified + * in the param string @buf. Acceptable param strings are of the form + * ,io|mmio|mmio32,, + * ,0x, + * , + * + * + * Only for the third form does the earlycon setup() method receive the + * string in the 'options' parameter; all other forms set + * the parameter to NULL. + * + * Returns 0 if an attempt to register the earlycon was made, + * otherwise negative error code + */ +int __init setup_earlycon(char *buf) { - size_t len; + const struct earlycon_id *match; - if (!buf || !match || !setup) - return 0; + if (!buf || !buf[0]) + return -EINVAL; - len = strlen(match); - if (strncmp(buf, match, len)) - return 0; + if (early_con.flags & CON_ENABLED) + return -EALREADY; - if (buf[len]) { - if (buf[len] != ',') - return 0; - buf += len + 1; - } else - buf = NULL; + for (match = __earlycon_table; match->name[0]; match++) { + size_t len = strlen(match->name); - return register_earlycon(buf, setup); + if (strncmp(buf, match->name, len)) + continue; + + if (buf[len]) { + if (buf[len] != ',') + continue; + buf += len + 1; + } else + buf = NULL; + + return register_earlycon(buf, match); + } + + return -ENOENT; +} + +/* early_param wrapper for setup_earlycon() */ +static int __init param_setup_earlycon(char *buf) +{ + int err; + + /* + * Just 'earlycon' is a valid param for devicetree earlycons; + * don't generate a warning from parse_early_params() in that case + */ + if (!buf || !buf[0]) + return 0; + + err = setup_earlycon(buf); + if (err == -ENOENT) { + pr_warn("no match for %s\n", buf); + err = 0; + } else if (err == -EALREADY) { + pr_warn("already registered\n"); + err = 0; + } + return err; } +early_param("earlycon", param_setup_earlycon); int __init of_setup_earlycon(unsigned long addr, int (*setup)(struct earlycon_device *, const char *)) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..87e5b6f8f4fc 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -150,6 +150,14 @@ #define TRACE_SYSCALLS() #endif +#ifdef CONFIG_SERIAL_EARLYCON +#define EARLYCON_TABLE() . = ALIGN(8); \ + VMLINUX_SYMBOL(__earlycon_table) = .; \ + *(__earlycon_table) \ + *(__earlycon_table_end) +#else +#define EARLYCON_TABLE() +#endif #define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name) #define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name) @@ -503,6 +511,7 @@ CPU_METHOD_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() \ + EARLYCON_TABLE() \ EARLYCON_OF_TABLES() #define INIT_TEXT \ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 980170e5a982..8aeec4913a9c 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -337,18 +337,21 @@ struct earlycon_device { char options[16]; /* e.g., 115200n8 */ unsigned int baud; }; -int setup_earlycon(char *buf, const char *match, - int (*setup)(struct earlycon_device *, const char *)); +struct earlycon_id { + char name[16]; + int (*setup)(struct earlycon_device *, const char *options); +}; + +extern int setup_earlycon(char *buf); 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) \ -{ \ - return setup_earlycon(buf, __stringify(name), func); \ -} \ -early_param("earlycon", name ## _setup_earlycon); +#define EARLYCON_DECLARE(_name, func) \ + static const struct earlycon_id __earlycon_##_name \ + __used __section(__earlycon_table) \ + = { .name = __stringify(_name), \ + .setup = func } #define OF_EARLYCON_DECLARE(name, compat, fn) \ _OF_DECLARE(earlycon, name, compat, fn, void *) -- cgit v1.2.3 From 66c53aaa9c82766d4e9253748c6988441d8c2dc1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 7 May 2015 14:19:21 -0400 Subject: earlycon: Revert log warnings Log warnings meant to help diagnose problems setting up earlycon are reporting false positives for 'console='. Revert to the previous behavior which reported nothing. Cc: Maciej W. Rozycki Cc: Robert Schwebel Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/earlycon.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/tty/serial/earlycon.c') diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 5fdc9f3ecd64..6dc471e30e79 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -187,13 +187,8 @@ static int __init param_setup_earlycon(char *buf) return 0; err = setup_earlycon(buf); - if (err == -ENOENT) { - pr_warn("no match for %s\n", buf); - err = 0; - } else if (err == -EALREADY) { - pr_warn("already registered\n"); - err = 0; - } + if (err == -ENOENT || err == -EALREADY) + return 0; return err; } early_param("earlycon", param_setup_earlycon); -- cgit v1.2.3