summaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/earlycon.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/earlycon.c')
-rw-r--r--drivers/tty/serial/earlycon.c92
1 files changed, 73 insertions, 19 deletions
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 *))