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