summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/serial/8250/8250_early.c7
-rw-r--r--drivers/tty/serial/earlycon.c92
-rw-r--r--include/asm-generic/vmlinux.lds.h9
-rw-r--r--include/linux/serial_core.h19
4 files changed, 94 insertions, 33 deletions
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 <linux/console.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -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
+ * <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)
- 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 *)