Commit 470ca0de authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman

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: default avatarRob Herring <robh@kernel.org>
Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7c53cb3d
...@@ -170,10 +170,5 @@ EARLYCON_DECLARE(uart, early_serial8250_setup); ...@@ -170,10 +170,5 @@ EARLYCON_DECLARE(uart, early_serial8250_setup);
int __init setup_early_serial8250_console(char *cmdline) int __init setup_early_serial8250_console(char *cmdline)
{ {
char match[] = "uart8250"; return setup_earlycon(cmdline);
if (cmdline && cmdline[4] == ',')
match[4] = '\0';
return setup_earlycon(cmdline, match, early_serial8250_setup);
} }
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/console.h> #include <linux/console.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -34,6 +37,10 @@ static struct earlycon_device early_console_dev = { ...@@ -34,6 +37,10 @@ static struct earlycon_device early_console_dev = {
.con = &early_con, .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 static const struct of_device_id __earlycon_of_table_sentinel
__used __section(__earlycon_of_table_end); __used __section(__earlycon_of_table_end);
...@@ -96,9 +103,7 @@ static int __init parse_options(struct earlycon_device *device, char *options) ...@@ -96,9 +103,7 @@ static int __init parse_options(struct earlycon_device *device, char *options)
return 0; return 0;
} }
static int __init register_earlycon(char *buf, const struct earlycon_id *match)
static int __init
register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *))
{ {
int err; int err;
struct uart_port *port = &early_console_dev.port; struct uart_port *port = &early_console_dev.port;
...@@ -112,7 +117,7 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char * ...@@ -112,7 +117,7 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *
port->membase = earlycon_map(port->mapbase, 64); port->membase = earlycon_map(port->mapbase, 64);
early_console_dev.con->data = &early_console_dev; early_console_dev.con->data = &early_console_dev;
err = setup(&early_console_dev, buf); err = match->setup(&early_console_dev, buf);
if (err < 0) if (err < 0)
return err; return err;
if (!early_console_dev.con->write) if (!early_console_dev.con->write)
...@@ -122,27 +127,76 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char * ...@@ -122,27 +127,76 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *
return 0; 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
* <name>,io|mmio|mmio32,<addr>,<options>
* <name>,0x<addr>,<options>
* <name>,<options>
* <name>
*
* Only for the third form does the earlycon setup() method receive the
* <options> 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) if (!buf || !buf[0])
return 0; return -EINVAL;
len = strlen(match); if (early_con.flags & CON_ENABLED)
if (strncmp(buf, match, len)) return -EALREADY;
return 0;
if (buf[len]) { for (match = __earlycon_table; match->name[0]; match++) {
if (buf[len] != ',') size_t len = strlen(match->name);
return 0;
buf += len + 1;
} else
buf = NULL;
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 __init of_setup_earlycon(unsigned long addr,
int (*setup)(struct earlycon_device *, const char *)) int (*setup)(struct earlycon_device *, const char *))
......
...@@ -150,6 +150,14 @@ ...@@ -150,6 +150,14 @@
#define TRACE_SYSCALLS() #define TRACE_SYSCALLS()
#endif #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)
#define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name) #define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name)
...@@ -503,6 +511,7 @@ ...@@ -503,6 +511,7 @@
CPU_METHOD_OF_TABLES() \ CPU_METHOD_OF_TABLES() \
KERNEL_DTB() \ KERNEL_DTB() \
IRQCHIP_OF_MATCH_TABLE() \ IRQCHIP_OF_MATCH_TABLE() \
EARLYCON_TABLE() \
EARLYCON_OF_TABLES() EARLYCON_OF_TABLES()
#define INIT_TEXT \ #define INIT_TEXT \
......
...@@ -337,18 +337,21 @@ struct earlycon_device { ...@@ -337,18 +337,21 @@ struct earlycon_device {
char options[16]; /* e.g., 115200n8 */ char options[16]; /* e.g., 115200n8 */
unsigned int baud; 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, extern int of_setup_earlycon(unsigned long addr,
int (*setup)(struct earlycon_device *, const char *)); int (*setup)(struct earlycon_device *, const char *));
#define EARLYCON_DECLARE(name, func) \ #define EARLYCON_DECLARE(_name, func) \
static int __init name ## _setup_earlycon(char *buf) \ static const struct earlycon_id __earlycon_##_name \
{ \ __used __section(__earlycon_table) \
return setup_earlycon(buf, __stringify(name), func); \ = { .name = __stringify(_name), \
} \ .setup = func }
early_param("earlycon", name ## _setup_earlycon);
#define OF_EARLYCON_DECLARE(name, compat, fn) \ #define OF_EARLYCON_DECLARE(name, compat, fn) \
_OF_DECLARE(earlycon, name, compat, fn, void *) _OF_DECLARE(earlycon, name, compat, fn, void *)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment