| 1 | |
| 2 | |
| 3 | * Copyright (C) 2014 Linaro Ltd.
|
| 4 | * Author: Rob Herring <[email protected]>
|
| 5 | *
|
| 6 | * Based on 8250 earlycon:
|
| 7 | * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
|
| 8 | * Bjorn Helgaas <[email protected]>
|
| 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 |
|
| 29 | static struct console early_con = {
|
| 30 | .name = "uart",
|
| 31 | .flags = CON_PRINTBUFFER | CON_BOOT,
|
| 32 | .index = 0,
|
| 33 | };
|
| 34 |
|
| 35 | static struct earlycon_device early_console_dev = {
|
| 36 | .con = &early_con,
|
| 37 | };
|
| 38 |
|
| 39 | static 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 |
|
| 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 |
|
| 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(earlycon->name, name, min(len + 1, sizeof(earlycon->name)));
|
| 71 | earlycon->data = &early_console_dev;
|
| 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;
|
| 78 | char ioinfos[64];
|
| 79 |
|
| 80 | uart_get_ioinfos(port, ioinfos, sizeof(ioinfos));
|
| 81 |
|
| 82 | pr_info("%s%d%s (options '%s')\n", earlycon->name, earlycon->index,
|
| 83 | ioinfos, device->options);
|
| 84 | }
|
| 85 |
|
| 86 | static int __init parse_options(struct earlycon_device *device, char *options)
|
| 87 | {
|
| 88 | struct uart_port *port = &device->port;
|
| 89 | int length;
|
| 90 | resource_size_t addr;
|
| 91 |
|
| 92 | if (uart_parse_earlycon(options, &port->iotype, &addr, &options))
|
| 93 | return -EINVAL;
|
| 94 |
|
| 95 | switch (port->iotype) {
|
| 96 | case UPIO_MEM:
|
| 97 | port->mapbase = addr;
|
| 98 | break;
|
| 99 | case UPIO_MEM16:
|
| 100 | port->regshift = 1;
|
| 101 | port->mapbase = addr;
|
| 102 | break;
|
| 103 | case UPIO_MEM32:
|
| 104 | case UPIO_MEM32BE:
|
| 105 | port->regshift = 2;
|
| 106 | port->mapbase = addr;
|
| 107 | break;
|
| 108 | case UPIO_PORT:
|
| 109 | port->iobase = addr;
|
| 110 | break;
|
| 111 | default:
|
| 112 | return -EINVAL;
|
| 113 | }
|
| 114 |
|
| 115 | if (options) {
|
| 116 | char *uartclk;
|
| 117 |
|
| 118 | device->baud = simple_strtoul(options, NULL, 0);
|
| 119 | uartclk = strchr(options, ',');
|
| 120 | if (uartclk && kstrtouint(uartclk + 1, 0, &port->uartclk) < 0)
|
| 121 | pr_warn("[%s] unsupported earlycon uart clkrate option\n",
|
| 122 | options);
|
| 123 | length = min(strcspn(options, " ") + 1,
|
| 124 | (size_t)(sizeof(device->options)));
|
| 125 | strscpy(device->options, options, length);
|
| 126 | }
|
| 127 |
|
| 128 | return 0;
|
| 129 | }
|
| 130 |
|
| 131 | static int __init register_earlycon(char *buf, const struct earlycon_id *match)
|
| 132 | {
|
| 133 | int err;
|
| 134 | struct uart_port *port = &early_console_dev.port;
|
| 135 |
|
| 136 |
|
| 137 | if (buf && !parse_options(&early_console_dev, buf))
|
| 138 | buf = NULL;
|
| 139 |
|
| 140 | spin_lock_init(&port->lock);
|
| 141 | if (!port->uartclk)
|
| 142 | port->uartclk = BASE_BAUD * 16;
|
| 143 | if (port->mapbase)
|
| 144 | port->membase = earlycon_map(port->mapbase, 64);
|
| 145 |
|
| 146 | earlycon_init(&early_console_dev, match->name);
|
| 147 | err = match->setup(&early_console_dev, buf);
|
| 148 | earlycon_print_info(&early_console_dev);
|
| 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 | }
|
| 157 |
|
| 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
|
| 164 | * <name>,io|mmio|mmio32|mmio32be,<addr>,<options>
|
| 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)
|
| 177 | {
|
| 178 | const struct earlycon_id *match;
|
| 179 | bool empty_compatible = true;
|
| 180 |
|
| 181 | if (!buf || !buf[0])
|
| 182 | return -EINVAL;
|
| 183 |
|
| 184 | if (console_is_registered(&early_con))
|
| 185 | return -EALREADY;
|
| 186 |
|
| 187 | again:
|
| 188 | for (match = __earlycon_table; match < __earlycon_table_end; match++) {
|
| 189 | size_t len = strlen(match->name);
|
| 190 |
|
| 191 | if (strncmp(buf, match->name, len))
|
| 192 | continue;
|
| 193 |
|
| 194 |
|
| 195 | if (empty_compatible && *match->compatible)
|
| 196 | continue;
|
| 197 |
|
| 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 |
|
| 208 | if (empty_compatible) {
|
| 209 | empty_compatible = false;
|
| 210 | goto again;
|
| 211 | }
|
| 212 |
|
| 213 | return -ENOENT;
|
| 214 | }
|
| 215 |
|
| 216 | |
| 217 | * This defers the initialization of the early console until after ACPI has
|
| 218 | * been initialized.
|
| 219 |
|
| 220 | bool earlycon_acpi_spcr_enable __initdata;
|
| 221 |
|
| 222 |
|
| 223 | static int __init param_setup_earlycon(char *buf)
|
| 224 | {
|
| 225 | int err;
|
| 226 |
|
| 227 |
|
| 228 | if (!buf || !buf[0]) {
|
| 229 | if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
|
| 230 | earlycon_acpi_spcr_enable = true;
|
| 231 | return 0;
|
| 232 | } else if (!buf) {
|
| 233 | return early_init_dt_scan_chosen_stdout();
|
| 234 | }
|
| 235 | }
|
| 236 |
|
| 237 | err = setup_earlycon(buf);
|
| 238 | if (err == -ENOENT || err == -EALREADY)
|
| 239 | return 0;
|
| 240 | return err;
|
| 241 | }
|
| 242 | early_param("earlycon", param_setup_earlycon);
|
| 243 |
|
| 244 | |
| 245 | * The `console` parameter is overloaded. It's handled here as an early param
|
| 246 | * and in `printk.c` as a late param. It's possible to specify an early
|
| 247 | * `bootconsole` using `earlycon=uartXXXX` (handled above), or via
|
| 248 | * the `console=uartXXX` alias. See the comment in `8250_early.c`.
|
| 249 |
|
| 250 | static int __init param_setup_earlycon_console_alias(char *buf)
|
| 251 | {
|
| 252 | |
| 253 | * A plain `console` parameter must not enable the SPCR `bootconsole`
|
| 254 | * like a plain `earlycon` does.
|
| 255 | *
|
| 256 | * A `console=` parameter that specifies an empty value is used to
|
| 257 | * disable the `console`, not the `earlycon` `bootconsole`. The
|
| 258 | * disabling of the `console` is handled by `printk.c`.
|
| 259 |
|
| 260 | if (!buf || !buf[0])
|
| 261 | return 0;
|
| 262 |
|
| 263 | return param_setup_earlycon(buf);
|
| 264 | }
|
| 265 | early_param("console", param_setup_earlycon_console_alias);
|
| 266 |
|
| 267 | #ifdef CONFIG_OF_EARLY_FLATTREE
|
| 268 |
|
| 269 | int __init of_setup_earlycon(const struct earlycon_id *match,
|
| 270 | unsigned long node,
|
| 271 | const char *options)
|
| 272 | {
|
| 273 | int err;
|
| 274 | struct uart_port *port = &early_console_dev.port;
|
| 275 | const __be32 *val;
|
| 276 | bool big_endian;
|
| 277 | u64 addr;
|
| 278 |
|
| 279 | if (console_is_registered(&early_con))
|
| 280 | return -EALREADY;
|
| 281 |
|
| 282 | spin_lock_init(&port->lock);
|
| 283 | port->iotype = UPIO_MEM;
|
| 284 | addr = of_flat_dt_translate_address(node);
|
| 285 | if (addr == OF_BAD_ADDR) {
|
| 286 | pr_warn("[%s] bad address\n", match->name);
|
| 287 | return -ENXIO;
|
| 288 | }
|
| 289 | port->mapbase = addr;
|
| 290 |
|
| 291 | val = of_get_flat_dt_prop(node, "reg-offset", NULL);
|
| 292 | if (val)
|
| 293 | port->mapbase += be32_to_cpu(*val);
|
| 294 | port->membase = earlycon_map(port->mapbase, SZ_4K);
|
| 295 |
|
| 296 | val = of_get_flat_dt_prop(node, "reg-shift", NULL);
|
| 297 | if (val)
|
| 298 | port->regshift = be32_to_cpu(*val);
|
| 299 | big_endian = of_get_flat_dt_prop(node, "big-endian", NULL) != NULL ||
|
| 300 | (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&
|
| 301 | of_get_flat_dt_prop(node, "native-endian", NULL) != NULL);
|
| 302 | val = of_get_flat_dt_prop(node, "reg-io-width", NULL);
|
| 303 | if (val) {
|
| 304 | switch (be32_to_cpu(*val)) {
|
| 305 | case 1:
|
| 306 | port->iotype = UPIO_MEM;
|
| 307 | break;
|
| 308 | case 2:
|
| 309 | port->iotype = UPIO_MEM16;
|
| 310 | break;
|
| 311 | case 4:
|
| 312 | port->iotype = (big_endian) ? UPIO_MEM32BE : UPIO_MEM32;
|
| 313 | break;
|
| 314 | default:
|
| 315 | pr_warn("[%s] unsupported reg-io-width\n", match->name);
|
| 316 | return -EINVAL;
|
| 317 | }
|
| 318 | }
|
| 319 |
|
| 320 | val = of_get_flat_dt_prop(node, "current-speed", NULL);
|
| 321 | if (val)
|
| 322 | early_console_dev.baud = be32_to_cpu(*val);
|
| 323 |
|
| 324 | val = of_get_flat_dt_prop(node, "clock-frequency", NULL);
|
| 325 | if (val)
|
| 326 | port->uartclk = be32_to_cpu(*val);
|
| 327 |
|
| 328 | if (options) {
|
| 329 | early_console_dev.baud = simple_strtoul(options, NULL, 0);
|
| 330 | strscpy(early_console_dev.options, options,
|
| 331 | sizeof(early_console_dev.options));
|
| 332 | }
|
| 333 | earlycon_init(&early_console_dev, match->name);
|
| 334 | err = match->setup(&early_console_dev, options);
|
| 335 | earlycon_print_info(&early_console_dev);
|
| 336 | if (err < 0)
|
| 337 | return err;
|
| 338 | if (!early_console_dev.con->write)
|
| 339 | return -ENODEV;
|
| 340 |
|
| 341 |
|
| 342 | register_console(early_console_dev.con);
|
| 343 | return 0;
|
| 344 | }
|
| 345 |
|
| 346 | #endif
|