Commit | Line | Data |
---|---|---|
e3b3d0f5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
9aac5887 RH |
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> | |
9aac5887 | 9 | */ |
470ca0de PH |
10 | |
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
12 | ||
9aac5887 RH |
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> | |
b0b6abd3 | 18 | #include <linux/sizes.h> |
c90fe9c0 | 19 | #include <linux/of.h> |
088da2a1 | 20 | #include <linux/of_fdt.h> |
ad1696f6 | 21 | #include <linux/acpi.h> |
9aac5887 RH |
22 | |
23 | #ifdef CONFIG_FIX_EARLYCON_MEM | |
24 | #include <asm/fixmap.h> | |
25 | #endif | |
26 | ||
27 | #include <asm/serial.h> | |
28 | ||
29 | static struct console early_con = { | |
cda64e68 | 30 | .name = "uart", /* fixed up at earlycon registration */ |
9aac5887 | 31 | .flags = CON_PRINTBUFFER | CON_BOOT, |
cda64e68 | 32 | .index = 0, |
9aac5887 RH |
33 | }; |
34 | ||
35 | static struct earlycon_device early_console_dev = { | |
36 | .con = &early_con, | |
37 | }; | |
38 | ||
46e36683 | 39 | static void __iomem * __init earlycon_map(resource_size_t paddr, size_t size) |
9aac5887 RH |
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) | |
46e36683 | 50 | pr_err("%s: Couldn't map %pa\n", __func__, &paddr); |
9aac5887 RH |
51 | |
52 | return base; | |
53 | } | |
54 | ||
cda64e68 PH |
55 | static 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 | strlcpy(earlycon->name, name, min(len + 1, sizeof(earlycon->name))); | |
71 | earlycon->data = &early_console_dev; | |
f28295cc HYW |
72 | } |
73 | ||
74 | static void __init earlycon_print_info(struct earlycon_device *device) | |
75 | { | |
76 | struct console *earlycon = device->con; | |
77 | struct uart_port *port = &device->port; | |
8e773739 PH |
78 | |
79 | if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 || | |
80 | port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE) | |
b9693984 PH |
81 | pr_info("%s%d at MMIO%s %pa (options '%s')\n", |
82 | earlycon->name, earlycon->index, | |
8e773739 PH |
83 | (port->iotype == UPIO_MEM) ? "" : |
84 | (port->iotype == UPIO_MEM16) ? "16" : | |
85 | (port->iotype == UPIO_MEM32) ? "32" : "32be", | |
b9693984 | 86 | &port->mapbase, device->options); |
8e773739 | 87 | else |
b9693984 PH |
88 | pr_info("%s%d at I/O port 0x%lx (options '%s')\n", |
89 | earlycon->name, earlycon->index, | |
90 | port->iobase, device->options); | |
cda64e68 PH |
91 | } |
92 | ||
73abaf87 | 93 | static int __init parse_options(struct earlycon_device *device, char *options) |
9aac5887 RH |
94 | { |
95 | struct uart_port *port = &device->port; | |
73abaf87 | 96 | int length; |
46e36683 | 97 | resource_size_t addr; |
9aac5887 | 98 | |
73abaf87 PH |
99 | if (uart_parse_earlycon(options, &port->iotype, &addr, &options)) |
100 | return -EINVAL; | |
9aac5887 | 101 | |
73abaf87 | 102 | switch (port->iotype) { |
bd94c407 MY |
103 | case UPIO_MEM: |
104 | port->mapbase = addr; | |
105 | break; | |
106 | case UPIO_MEM16: | |
107 | port->regshift = 1; | |
108 | port->mapbase = addr; | |
109 | break; | |
73abaf87 | 110 | case UPIO_MEM32: |
6e63be3f | 111 | case UPIO_MEM32BE: |
bd94c407 | 112 | port->regshift = 2; |
9aac5887 | 113 | port->mapbase = addr; |
73abaf87 PH |
114 | break; |
115 | case UPIO_PORT: | |
9aac5887 | 116 | port->iobase = addr; |
73abaf87 PH |
117 | break; |
118 | default: | |
9aac5887 RH |
119 | return -EINVAL; |
120 | } | |
121 | ||
9aac5887 | 122 | if (options) { |
e26f1db9 | 123 | device->baud = simple_strtoul(options, NULL, 0); |
9aac5887 RH |
124 | length = min(strcspn(options, " ") + 1, |
125 | (size_t)(sizeof(device->options))); | |
126 | strlcpy(device->options, options, length); | |
127 | } | |
128 | ||
9aac5887 RH |
129 | return 0; |
130 | } | |
131 | ||
470ca0de | 132 | static int __init register_earlycon(char *buf, const struct earlycon_id *match) |
9aac5887 RH |
133 | { |
134 | int err; | |
9aac5887 RH |
135 | struct uart_port *port = &early_console_dev.port; |
136 | ||
9aac5887 | 137 | /* On parsing error, pass the options buf to the setup function */ |
60846880 | 138 | if (buf && !parse_options(&early_console_dev, buf)) |
9aac5887 RH |
139 | buf = NULL; |
140 | ||
e1dd3bef | 141 | spin_lock_init(&port->lock); |
feed5bab | 142 | port->uartclk = BASE_BAUD * 16; |
9aac5887 RH |
143 | if (port->mapbase) |
144 | port->membase = earlycon_map(port->mapbase, 64); | |
145 | ||
cda64e68 | 146 | earlycon_init(&early_console_dev, match->name); |
470ca0de | 147 | err = match->setup(&early_console_dev, buf); |
f28295cc | 148 | earlycon_print_info(&early_console_dev); |
9aac5887 RH |
149 | if (err < 0) |
150 | return err; | |
151 | if (!early_console_dev.con->write) | |
152 | return -ENODEV; | |
153 | ||
154 | register_console(early_console_dev.con); | |
155 | return 0; | |
156 | } | |
b0b6abd3 | 157 | |
470ca0de PH |
158 | /** |
159 | * setup_earlycon - match and register earlycon console | |
160 | * @buf: earlycon param string | |
161 | * | |
162 | * Registers the earlycon console matching the earlycon specified | |
163 | * in the param string @buf. Acceptable param strings are of the form | |
6e63be3f | 164 | * <name>,io|mmio|mmio32|mmio32be,<addr>,<options> |
470ca0de PH |
165 | * <name>,0x<addr>,<options> |
166 | * <name>,<options> | |
167 | * <name> | |
168 | * | |
169 | * Only for the third form does the earlycon setup() method receive the | |
170 | * <options> string in the 'options' parameter; all other forms set | |
171 | * the parameter to NULL. | |
172 | * | |
173 | * Returns 0 if an attempt to register the earlycon was made, | |
174 | * otherwise negative error code | |
175 | */ | |
176 | int __init setup_earlycon(char *buf) | |
7c53cb3d | 177 | { |
62dcd9c5 | 178 | const struct earlycon_id *match; |
f8c3686c | 179 | bool empty_compatible = true; |
7c53cb3d | 180 | |
470ca0de PH |
181 | if (!buf || !buf[0]) |
182 | return -EINVAL; | |
7c53cb3d | 183 | |
470ca0de PH |
184 | if (early_con.flags & CON_ENABLED) |
185 | return -EALREADY; | |
7c53cb3d | 186 | |
f8c3686c | 187 | again: |
62dcd9c5 | 188 | for (match = __earlycon_table; match < __earlycon_table_end; match++) { |
470ca0de | 189 | size_t len = strlen(match->name); |
7c53cb3d | 190 | |
470ca0de PH |
191 | if (strncmp(buf, match->name, len)) |
192 | continue; | |
193 | ||
f8c3686c MW |
194 | /* prefer entries with empty compatible */ |
195 | if (empty_compatible && *match->compatible) | |
196 | continue; | |
197 | ||
470ca0de PH |
198 | if (buf[len]) { |
199 | if (buf[len] != ',') | |
200 | continue; | |
201 | buf += len + 1; | |
202 | } else | |
203 | buf = NULL; | |
204 | ||
205 | return register_earlycon(buf, match); | |
206 | } | |
207 | ||
f8c3686c MW |
208 | if (empty_compatible) { |
209 | empty_compatible = false; | |
210 | goto again; | |
211 | } | |
212 | ||
470ca0de PH |
213 | return -ENOENT; |
214 | } | |
215 | ||
ad1696f6 | 216 | /* |
0231d000 PB |
217 | * This defers the initialization of the early console until after ACPI has |
218 | * been initialized. | |
ad1696f6 | 219 | */ |
0231d000 | 220 | bool earlycon_acpi_spcr_enable __initdata; |
ad1696f6 | 221 | |
470ca0de PH |
222 | /* early_param wrapper for setup_earlycon() */ |
223 | static int __init param_setup_earlycon(char *buf) | |
224 | { | |
225 | int err; | |
226 | ||
0231d000 | 227 | /* Just 'earlycon' is a valid param for devicetree and ACPI SPCR. */ |
ad1696f6 AM |
228 | if (!buf || !buf[0]) { |
229 | if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) { | |
0231d000 | 230 | earlycon_acpi_spcr_enable = true; |
ad1696f6 | 231 | return 0; |
447ef990 | 232 | } else if (!buf) { |
ad1696f6 AM |
233 | return early_init_dt_scan_chosen_stdout(); |
234 | } | |
235 | } | |
470ca0de PH |
236 | |
237 | err = setup_earlycon(buf); | |
66c53aaa PH |
238 | if (err == -ENOENT || err == -EALREADY) |
239 | return 0; | |
470ca0de | 240 | return err; |
7c53cb3d | 241 | } |
470ca0de | 242 | early_param("earlycon", param_setup_earlycon); |
7c53cb3d | 243 | |
8477614d PH |
244 | #ifdef CONFIG_OF_EARLY_FLATTREE |
245 | ||
c90fe9c0 | 246 | int __init of_setup_earlycon(const struct earlycon_id *match, |
088da2a1 | 247 | unsigned long node, |
4d118c9a | 248 | const char *options) |
b0b6abd3 RH |
249 | { |
250 | int err; | |
251 | struct uart_port *port = &early_console_dev.port; | |
088da2a1 PH |
252 | const __be32 *val; |
253 | bool big_endian; | |
c90fe9c0 | 254 | u64 addr; |
b0b6abd3 | 255 | |
e1dd3bef | 256 | spin_lock_init(&port->lock); |
b0b6abd3 | 257 | port->iotype = UPIO_MEM; |
c90fe9c0 PH |
258 | addr = of_flat_dt_translate_address(node); |
259 | if (addr == OF_BAD_ADDR) { | |
260 | pr_warn("[%s] bad address\n", match->name); | |
261 | return -ENXIO; | |
262 | } | |
b0b6abd3 | 263 | port->mapbase = addr; |
b0b6abd3 | 264 | |
088da2a1 PH |
265 | val = of_get_flat_dt_prop(node, "reg-offset", NULL); |
266 | if (val) | |
267 | port->mapbase += be32_to_cpu(*val); | |
1f66dd36 GH |
268 | port->membase = earlycon_map(port->mapbase, SZ_4K); |
269 | ||
088da2a1 PH |
270 | val = of_get_flat_dt_prop(node, "reg-shift", NULL); |
271 | if (val) | |
272 | port->regshift = be32_to_cpu(*val); | |
273 | big_endian = of_get_flat_dt_prop(node, "big-endian", NULL) != NULL || | |
274 | (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) && | |
275 | of_get_flat_dt_prop(node, "native-endian", NULL) != NULL); | |
276 | val = of_get_flat_dt_prop(node, "reg-io-width", NULL); | |
277 | if (val) { | |
278 | switch (be32_to_cpu(*val)) { | |
279 | case 1: | |
280 | port->iotype = UPIO_MEM; | |
281 | break; | |
282 | case 2: | |
283 | port->iotype = UPIO_MEM16; | |
284 | break; | |
285 | case 4: | |
286 | port->iotype = (big_endian) ? UPIO_MEM32BE : UPIO_MEM32; | |
287 | break; | |
288 | default: | |
289 | pr_warn("[%s] unsupported reg-io-width\n", match->name); | |
290 | return -EINVAL; | |
291 | } | |
292 | } | |
293 | ||
31cb9a85 EP |
294 | val = of_get_flat_dt_prop(node, "current-speed", NULL); |
295 | if (val) | |
296 | early_console_dev.baud = be32_to_cpu(*val); | |
297 | ||
814453ad MS |
298 | val = of_get_flat_dt_prop(node, "clock-frequency", NULL); |
299 | if (val) | |
300 | port->uartclk = be32_to_cpu(*val); | |
301 | ||
4d118c9a | 302 | if (options) { |
31cb9a85 | 303 | early_console_dev.baud = simple_strtoul(options, NULL, 0); |
4d118c9a PH |
304 | strlcpy(early_console_dev.options, options, |
305 | sizeof(early_console_dev.options)); | |
306 | } | |
05d96132 | 307 | earlycon_init(&early_console_dev, match->name); |
4d118c9a | 308 | err = match->setup(&early_console_dev, options); |
f28295cc | 309 | earlycon_print_info(&early_console_dev); |
b0b6abd3 RH |
310 | if (err < 0) |
311 | return err; | |
312 | if (!early_console_dev.con->write) | |
313 | return -ENODEV; | |
314 | ||
315 | ||
316 | register_console(early_console_dev.con); | |
317 | return 0; | |
318 | } | |
8477614d PH |
319 | |
320 | #endif /* CONFIG_OF_EARLY_FLATTREE */ |