1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2014 Linaro Ltd.
4 * Author: Rob Herring <robh@kernel.org>
5 *
6 * Based on 8250 earlycon:
7 * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
8 * Bjorn Helgaas <bjorn.helgaas@hp.com>
9 */
10
11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
13#include <linux/console.h>
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/serial_core.h>
18#include <linux/sizes.h>
19#include <linux/of.h>
20#include <linux/of_fdt.h>
21#include <linux/acpi.h>
22
23#ifdef CONFIG_FIX_EARLYCON_MEM
24#include <asm/fixmap.h>
25#endif
26
27#include <asm/serial.h>
28
29static struct console early_con = {
30 .name = "uart", /* fixed up at earlycon registration */
31 .flags = CON_PRINTBUFFER | CON_BOOT,
32 .index = 0,
33};
34
35static struct earlycon_device early_console_dev = {
36 .con = &early_con,
37};
38
39static void __iomem * __init earlycon_map(resource_size_t paddr, size_t size)
40{
41 void __iomem *base;
42#ifdef CONFIG_FIX_EARLYCON_MEM
43 set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);
44 base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
45 base += paddr & ~PAGE_MASK;
46#else
47 base = ioremap(paddr, size);
48#endif
49 if (!base)
50 pr_err("%s: Couldn't map %pa\n", __func__, &paddr);
51
52 return base;
53}
54
55static void __init earlycon_init(struct earlycon_device *device,
56 const char *name)
57{
58 struct console *earlycon = device->con;
59 const char *s;
60 size_t len;
61
62 /* scan backwards from end of string for first non-numeral */
63 for (s = name + strlen(name);
64 s > name && s[-1] >= '0' && s[-1] <= '9';
65 s--)
66 ;
67 if (*s)
68 earlycon->index = simple_strtoul(s, NULL, 10);
69 len = s - name;
70 strscpy(p: earlycon->name, q: name, min(len + 1, sizeof(earlycon->name)));
71 earlycon->data = &early_console_dev;
72}
73
74static void __init earlycon_print_info(struct earlycon_device *device)
75{
76 struct console *earlycon = device->con;
77 struct uart_port *port = &device->port;
78
79 if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 ||
80 port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE)
81 pr_info("%s%d at MMIO%s %pa (options '%s')\n",
82 earlycon->name, earlycon->index,
83 (port->iotype == UPIO_MEM) ? "" :
84 (port->iotype == UPIO_MEM16) ? "16" :
85 (port->iotype == UPIO_MEM32) ? "32" : "32be",
86 &port->mapbase, device->options);
87 else
88 pr_info("%s%d at I/O port 0x%lx (options '%s')\n",
89 earlycon->name, earlycon->index,
90 port->iobase, device->options);
91}
92
93static int __init parse_options(struct earlycon_device *device, char *options)
94{
95 struct uart_port *port = &device->port;
96 int length;
97 resource_size_t addr;
98
99 if (uart_parse_earlycon(p: options, iotype: &port->iotype, addr: &addr, options: &options))
100 return -EINVAL;
101
102 switch (port->iotype) {
103 case UPIO_MEM:
104 port->mapbase = addr;
105 break;
106 case UPIO_MEM16:
107 port->regshift = 1;
108 port->mapbase = addr;
109 break;
110 case UPIO_MEM32:
111 case UPIO_MEM32BE:
112 port->regshift = 2;
113 port->mapbase = addr;
114 break;
115 case UPIO_PORT:
116 port->iobase = addr;
117 break;
118 default:
119 return -EINVAL;
120 }
121
122 if (options) {
123 char *uartclk;
124
125 device->baud = simple_strtoul(options, NULL, 0);
126 uartclk = strchr(options, ',');
127 if (uartclk && kstrtouint(s: uartclk + 1, base: 0, res: &port->uartclk) < 0)
128 pr_warn("[%s] unsupported earlycon uart clkrate option\n",
129 options);
130 length = min(strcspn(options, " ") + 1,
131 (size_t)(sizeof(device->options)));
132 strscpy(p: device->options, q: options, size: length);
133 }
134
135 return 0;
136}
137
138static int __init register_earlycon(char *buf, const struct earlycon_id *match)
139{
140 int err;
141 struct uart_port *port = &early_console_dev.port;
142
143 /* On parsing error, pass the options buf to the setup function */
144 if (buf && !parse_options(device: &early_console_dev, options: buf))
145 buf = NULL;
146
147 spin_lock_init(&port->lock);
148 if (!port->uartclk)
149 port->uartclk = BASE_BAUD * 16;
150 if (port->mapbase)
151 port->membase = earlycon_map(paddr: port->mapbase, size: 64);
152
153 earlycon_init(device: &early_console_dev, name: match->name);
154 err = match->setup(&early_console_dev, buf);
155 earlycon_print_info(device: &early_console_dev);
156 if (err < 0)
157 return err;
158 if (!early_console_dev.con->write)
159 return -ENODEV;
160
161 register_console(early_console_dev.con);
162 return 0;
163}
164
165/**
166 * setup_earlycon - match and register earlycon console
167 * @buf: earlycon param string
168 *
169 * Registers the earlycon console matching the earlycon specified
170 * in the param string @buf. Acceptable param strings are of the form
171 * <name>,io|mmio|mmio32|mmio32be,<addr>,<options>
172 * <name>,0x<addr>,<options>
173 * <name>,<options>
174 * <name>
175 *
176 * Only for the third form does the earlycon setup() method receive the
177 * <options> string in the 'options' parameter; all other forms set
178 * the parameter to NULL.
179 *
180 * Returns 0 if an attempt to register the earlycon was made,
181 * otherwise negative error code
182 */
183int __init setup_earlycon(char *buf)
184{
185 const struct earlycon_id *match;
186 bool empty_compatible = true;
187
188 if (!buf || !buf[0])
189 return -EINVAL;
190
191 if (console_is_registered(con: &early_con))
192 return -EALREADY;
193
194again:
195 for (match = __earlycon_table; match < __earlycon_table_end; match++) {
196 size_t len = strlen(match->name);
197
198 if (strncmp(buf, match->name, len))
199 continue;
200
201 /* prefer entries with empty compatible */
202 if (empty_compatible && *match->compatible)
203 continue;
204
205 if (buf[len]) {
206 if (buf[len] != ',')
207 continue;
208 buf += len + 1;
209 } else
210 buf = NULL;
211
212 return register_earlycon(buf, match);
213 }
214
215 if (empty_compatible) {
216 empty_compatible = false;
217 goto again;
218 }
219
220 return -ENOENT;
221}
222
223/*
224 * This defers the initialization of the early console until after ACPI has
225 * been initialized.
226 */
227bool earlycon_acpi_spcr_enable __initdata;
228
229/* early_param wrapper for setup_earlycon() */
230static int __init param_setup_earlycon(char *buf)
231{
232 int err;
233
234 /* Just 'earlycon' is a valid param for devicetree and ACPI SPCR. */
235 if (!buf || !buf[0]) {
236 if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
237 earlycon_acpi_spcr_enable = true;
238 return 0;
239 } else if (!buf) {
240 return early_init_dt_scan_chosen_stdout();
241 }
242 }
243
244 err = setup_earlycon(buf);
245 if (err == -ENOENT || err == -EALREADY)
246 return 0;
247 return err;
248}
249early_param("earlycon", param_setup_earlycon);
250
251#ifdef CONFIG_OF_EARLY_FLATTREE
252
253int __init of_setup_earlycon(const struct earlycon_id *match,
254 unsigned long node,
255 const char *options)
256{
257 int err;
258 struct uart_port *port = &early_console_dev.port;
259 const __be32 *val;
260 bool big_endian;
261 u64 addr;
262
263 if (console_is_registered(con: &early_con))
264 return -EALREADY;
265
266 spin_lock_init(&port->lock);
267 port->iotype = UPIO_MEM;
268 addr = of_flat_dt_translate_address(node);
269 if (addr == OF_BAD_ADDR) {
270 pr_warn("[%s] bad address\n", match->name);
271 return -ENXIO;
272 }
273 port->mapbase = addr;
274
275 val = of_get_flat_dt_prop(node, name: "reg-offset", NULL);
276 if (val)
277 port->mapbase += be32_to_cpu(*val);
278 port->membase = earlycon_map(paddr: port->mapbase, SZ_4K);
279
280 val = of_get_flat_dt_prop(node, name: "reg-shift", NULL);
281 if (val)
282 port->regshift = be32_to_cpu(*val);
283 big_endian = of_get_flat_dt_prop(node, name: "big-endian", NULL) != NULL ||
284 (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&
285 of_get_flat_dt_prop(node, name: "native-endian", NULL) != NULL);
286 val = of_get_flat_dt_prop(node, name: "reg-io-width", NULL);
287 if (val) {
288 switch (be32_to_cpu(*val)) {
289 case 1:
290 port->iotype = UPIO_MEM;
291 break;
292 case 2:
293 port->iotype = UPIO_MEM16;
294 break;
295 case 4:
296 port->iotype = (big_endian) ? UPIO_MEM32BE : UPIO_MEM32;
297 break;
298 default:
299 pr_warn("[%s] unsupported reg-io-width\n", match->name);
300 return -EINVAL;
301 }
302 }
303
304 val = of_get_flat_dt_prop(node, name: "current-speed", NULL);
305 if (val)
306 early_console_dev.baud = be32_to_cpu(*val);
307
308 val = of_get_flat_dt_prop(node, name: "clock-frequency", NULL);
309 if (val)
310 port->uartclk = be32_to_cpu(*val);
311
312 if (options) {
313 early_console_dev.baud = simple_strtoul(options, NULL, 0);
314 strscpy(p: early_console_dev.options, q: options,
315 size: sizeof(early_console_dev.options));
316 }
317 earlycon_init(device: &early_console_dev, name: match->name);
318 err = match->setup(&early_console_dev, options);
319 earlycon_print_info(device: &early_console_dev);
320 if (err < 0)
321 return err;
322 if (!early_console_dev.con->write)
323 return -ENODEV;
324
325
326 register_console(early_console_dev.con);
327 return 0;
328}
329
330#endif /* CONFIG_OF_EARLY_FLATTREE */
331

source code of linux/drivers/tty/serial/earlycon.c