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