aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-03-09 16:27:21 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-26 12:25:27 -0400
commit470ca0de69feaba5df215ad804cec1859883a5ed (patch)
treef6881a136d161bb131d56b9b68860568756b53eb /drivers/tty
parent7c53cb3de493573dc3b7f2468a542a9f11cc5079 (diff)
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 <robh@kernel.org> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/8250/8250_early.c7
-rw-r--r--drivers/tty/serial/earlycon.c92
2 files changed, 74 insertions, 25 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);
170 170
171int __init setup_early_serial8250_console(char *cmdline) 171int __init setup_early_serial8250_console(char *cmdline)
172{ 172{
173 char match[] = "uart8250"; 173 return setup_earlycon(cmdline);
174
175 if (cmdline && cmdline[4] == ',')
176 match[4] = '\0';
177
178 return setup_earlycon(cmdline, match, early_serial8250_setup);
179} 174}
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 @@
10 * it under the terms of the GNU General Public License version 2 as 10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation. 11 * published by the Free Software Foundation.
12 */ 12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
13#include <linux/console.h> 16#include <linux/console.h>
14#include <linux/kernel.h> 17#include <linux/kernel.h>
15#include <linux/init.h> 18#include <linux/init.h>
@@ -34,6 +37,10 @@ static struct earlycon_device early_console_dev = {
34 .con = &early_con, 37 .con = &early_con,
35}; 38};
36 39
40extern struct earlycon_id __earlycon_table[];
41static const struct earlycon_id __earlycon_table_sentinel
42 __used __section(__earlycon_table_end);
43
37static const struct of_device_id __earlycon_of_table_sentinel 44static const struct of_device_id __earlycon_of_table_sentinel
38 __used __section(__earlycon_of_table_end); 45 __used __section(__earlycon_of_table_end);
39 46
@@ -96,9 +103,7 @@ static int __init parse_options(struct earlycon_device *device, char *options)
96 return 0; 103 return 0;
97} 104}
98 105
99 106static int __init register_earlycon(char *buf, const struct earlycon_id *match)
100static int __init
101register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *))
102{ 107{
103 int err; 108 int err;
104 struct uart_port *port = &early_console_dev.port; 109 struct uart_port *port = &early_console_dev.port;
@@ -112,7 +117,7 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *
112 port->membase = earlycon_map(port->mapbase, 64); 117 port->membase = earlycon_map(port->mapbase, 64);
113 118
114 early_console_dev.con->data = &early_console_dev; 119 early_console_dev.con->data = &early_console_dev;
115 err = setup(&early_console_dev, buf); 120 err = match->setup(&early_console_dev, buf);
116 if (err < 0) 121 if (err < 0)
117 return err; 122 return err;
118 if (!early_console_dev.con->write) 123 if (!early_console_dev.con->write)
@@ -122,27 +127,76 @@ register_earlycon(char *buf, int (*setup)(struct earlycon_device *, const char *
122 return 0; 127 return 0;
123} 128}
124 129
125int __init setup_earlycon(char *buf, const char *match, 130/**
126 int (*setup)(struct earlycon_device *, const char *)) 131 * setup_earlycon - match and register earlycon console
132 * @buf: earlycon param string
133 *
134 * Registers the earlycon console matching the earlycon specified
135 * in the param string @buf. Acceptable param strings are of the form
136 * <name>,io|mmio|mmio32,<addr>,<options>
137 * <name>,0x<addr>,<options>
138 * <name>,<options>
139 * <name>
140 *
141 * Only for the third form does the earlycon setup() method receive the
142 * <options> string in the 'options' parameter; all other forms set
143 * the parameter to NULL.
144 *
145 * Returns 0 if an attempt to register the earlycon was made,
146 * otherwise negative error code
147 */
148int __init setup_earlycon(char *buf)
127{ 149{
128 size_t len; 150 const struct earlycon_id *match;
129 151
130 if (!buf || !match || !setup) 152 if (!buf || !buf[0])
131 return 0; 153 return -EINVAL;
132 154
133 len = strlen(match); 155 if (early_con.flags & CON_ENABLED)
134 if (strncmp(buf, match, len)) 156 return -EALREADY;
135 return 0;
136 157
137 if (buf[len]) { 158 for (match = __earlycon_table; match->name[0]; match++) {
138 if (buf[len] != ',') 159 size_t len = strlen(match->name);
139 return 0;
140 buf += len + 1;
141 } else
142 buf = NULL;
143 160
144 return register_earlycon(buf, setup); 161 if (strncmp(buf, match->name, len))
162 continue;
163
164 if (buf[len]) {
165 if (buf[len] != ',')
166 continue;
167 buf += len + 1;
168 } else
169 buf = NULL;
170
171 return register_earlycon(buf, match);
172 }
173
174 return -ENOENT;
175}
176
177/* early_param wrapper for setup_earlycon() */
178static int __init param_setup_earlycon(char *buf)
179{
180 int err;
181
182 /*
183 * Just 'earlycon' is a valid param for devicetree earlycons;
184 * don't generate a warning from parse_early_params() in that case
185 */
186 if (!buf || !buf[0])
187 return 0;
188
189 err = setup_earlycon(buf);
190 if (err == -ENOENT) {
191 pr_warn("no match for %s\n", buf);
192 err = 0;
193 } else if (err == -EALREADY) {
194 pr_warn("already registered\n");
195 err = 0;
196 }
197 return err;
145} 198}
199early_param("earlycon", param_setup_earlycon);
146 200
147int __init of_setup_earlycon(unsigned long addr, 201int __init of_setup_earlycon(unsigned long addr,
148 int (*setup)(struct earlycon_device *, const char *)) 202 int (*setup)(struct earlycon_device *, const char *))