linux/drivers/tty/serial/earlycon.c
<<
>>
Prefs
   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        strlcpy(earlycon->name, 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(options, &port->iotype, &addr, &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                device->baud = simple_strtoul(options, NULL, 0);
 124                length = min(strcspn(options, " ") + 1,
 125                             (size_t)(sizeof(device->options)));
 126                strlcpy(device->options, options, length);
 127        }
 128
 129        return 0;
 130}
 131
 132static int __init register_earlycon(char *buf, const struct earlycon_id *match)
 133{
 134        int err;
 135        struct uart_port *port = &early_console_dev.port;
 136
 137        /* On parsing error, pass the options buf to the setup function */
 138        if (buf && !parse_options(&early_console_dev, buf))
 139                buf = NULL;
 140
 141        spin_lock_init(&port->lock);
 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 */
 176int __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 (early_con.flags & CON_ENABLED)
 185                return -EALREADY;
 186
 187again:
 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                /* prefer entries with empty compatible */
 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 */
 220bool earlycon_acpi_spcr_enable __initdata;
 221
 222/* early_param wrapper for setup_earlycon() */
 223static int __init param_setup_earlycon(char *buf)
 224{
 225        int err;
 226
 227        /* Just 'earlycon' is a valid param for devicetree and ACPI SPCR. */
 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}
 242early_param("earlycon", param_setup_earlycon);
 243
 244#ifdef CONFIG_OF_EARLY_FLATTREE
 245
 246int __init of_setup_earlycon(const struct earlycon_id *match,
 247                             unsigned long node,
 248                             const char *options)
 249{
 250        int err;
 251        struct uart_port *port = &early_console_dev.port;
 252        const __be32 *val;
 253        bool big_endian;
 254        u64 addr;
 255
 256        spin_lock_init(&port->lock);
 257        port->iotype = UPIO_MEM;
 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        }
 263        port->mapbase = addr;
 264
 265        val = of_get_flat_dt_prop(node, "reg-offset", NULL);
 266        if (val)
 267                port->mapbase += be32_to_cpu(*val);
 268        port->membase = earlycon_map(port->mapbase, SZ_4K);
 269
 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
 294        val = of_get_flat_dt_prop(node, "current-speed", NULL);
 295        if (val)
 296                early_console_dev.baud = be32_to_cpu(*val);
 297
 298        val = of_get_flat_dt_prop(node, "clock-frequency", NULL);
 299        if (val)
 300                port->uartclk = be32_to_cpu(*val);
 301
 302        if (options) {
 303                early_console_dev.baud = simple_strtoul(options, NULL, 0);
 304                strlcpy(early_console_dev.options, options,
 305                        sizeof(early_console_dev.options));
 306        }
 307        earlycon_init(&early_console_dev, match->name);
 308        err = match->setup(&early_console_dev, options);
 309        earlycon_print_info(&early_console_dev);
 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}
 319
 320#endif /* CONFIG_OF_EARLY_FLATTREE */
 321