331 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 ": " fmt12 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_MEM24#include <asm/fixmap.h>25#endif26 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_MEM43 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#else47 base = ioremap(paddr, size);48#endif49 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 strscpy(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 else88 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 char *uartclk;124 125 device->baud = simple_strtoul(options, NULL, 0);126 uartclk = strchr(options, ',');127 if (uartclk && kstrtouint(uartclk + 1, 0, &port->uartclk) < 0)128 pr_warn("[%s] unsupported earlycon uart clkrate option\n",129 options);130 length = min(strcspn(options, " ") + 1,131 (size_t)(sizeof(device->options)));132 strscpy(device->options, options, length);133 }134 135 return 0;136}137 138static int __init register_earlycon(char *buf, const struct earlycon_id *match)139{140 int err;141 struct uart_port *port = &early_console_dev.port;142 143 /* On parsing error, pass the options buf to the setup function */144 if (buf && !parse_options(&early_console_dev, buf))145 buf = NULL;146 147 spin_lock_init(&port->lock);148 if (!port->uartclk)149 port->uartclk = BASE_BAUD * 16;150 if (port->mapbase)151 port->membase = earlycon_map(port->mapbase, 64);152 153 earlycon_init(&early_console_dev, match->name);154 err = match->setup(&early_console_dev, buf);155 earlycon_print_info(&early_console_dev);156 if (err < 0)157 return err;158 if (!early_console_dev.con->write)159 return -ENODEV;160 161 register_console(early_console_dev.con);162 return 0;163}164 165/**166 * setup_earlycon - match and register earlycon console167 * @buf: earlycon param string168 *169 * Registers the earlycon console matching the earlycon specified170 * in the param string @buf. Acceptable param strings are of the form171 * <name>,io|mmio|mmio32|mmio32be,<addr>,<options>172 * <name>,0x<addr>,<options>173 * <name>,<options>174 * <name>175 *176 * Only for the third form does the earlycon setup() method receive the177 * <options> string in the 'options' parameter; all other forms set178 * the parameter to NULL.179 *180 * Returns 0 if an attempt to register the earlycon was made,181 * otherwise negative error code182 */183int __init setup_earlycon(char *buf)184{185 const struct earlycon_id *match;186 bool empty_compatible = true;187 188 if (!buf || !buf[0])189 return -EINVAL;190 191 if (console_is_registered(&early_con))192 return -EALREADY;193 194again:195 for (match = __earlycon_table; match < __earlycon_table_end; match++) {196 size_t len = strlen(match->name);197 198 if (strncmp(buf, match->name, len))199 continue;200 201 /* prefer entries with empty compatible */202 if (empty_compatible && *match->compatible)203 continue;204 205 if (buf[len]) {206 if (buf[len] != ',')207 continue;208 buf += len + 1;209 } else210 buf = NULL;211 212 return register_earlycon(buf, match);213 }214 215 if (empty_compatible) {216 empty_compatible = false;217 goto again;218 }219 220 return -ENOENT;221}222 223/*224 * This defers the initialization of the early console until after ACPI has225 * been initialized.226 */227bool earlycon_acpi_spcr_enable __initdata;228 229/* early_param wrapper for setup_earlycon() */230static int __init param_setup_earlycon(char *buf)231{232 int err;233 234 /* Just 'earlycon' is a valid param for devicetree and ACPI SPCR. */235 if (!buf || !buf[0]) {236 if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {237 earlycon_acpi_spcr_enable = true;238 return 0;239 } else if (!buf) {240 return early_init_dt_scan_chosen_stdout();241 }242 }243 244 err = setup_earlycon(buf);245 if (err == -ENOENT || err == -EALREADY)246 return 0;247 return err;248}249early_param("earlycon", param_setup_earlycon);250 251#ifdef CONFIG_OF_EARLY_FLATTREE252 253int __init of_setup_earlycon(const struct earlycon_id *match,254 unsigned long node,255 const char *options)256{257 int err;258 struct uart_port *port = &early_console_dev.port;259 const __be32 *val;260 bool big_endian;261 u64 addr;262 263 if (console_is_registered(&early_con))264 return -EALREADY;265 266 spin_lock_init(&port->lock);267 port->iotype = UPIO_MEM;268 addr = of_flat_dt_translate_address(node);269 if (addr == OF_BAD_ADDR) {270 pr_warn("[%s] bad address\n", match->name);271 return -ENXIO;272 }273 port->mapbase = addr;274 275 val = of_get_flat_dt_prop(node, "reg-offset", NULL);276 if (val)277 port->mapbase += be32_to_cpu(*val);278 port->membase = earlycon_map(port->mapbase, SZ_4K);279 280 val = of_get_flat_dt_prop(node, "reg-shift", NULL);281 if (val)282 port->regshift = be32_to_cpu(*val);283 big_endian = of_get_flat_dt_prop(node, "big-endian", NULL) != NULL ||284 (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&285 of_get_flat_dt_prop(node, "native-endian", NULL) != NULL);286 val = of_get_flat_dt_prop(node, "reg-io-width", NULL);287 if (val) {288 switch (be32_to_cpu(*val)) {289 case 1:290 port->iotype = UPIO_MEM;291 break;292 case 2:293 port->iotype = UPIO_MEM16;294 break;295 case 4:296 port->iotype = (big_endian) ? UPIO_MEM32BE : UPIO_MEM32;297 break;298 default:299 pr_warn("[%s] unsupported reg-io-width\n", match->name);300 return -EINVAL;301 }302 }303 304 val = of_get_flat_dt_prop(node, "current-speed", NULL);305 if (val)306 early_console_dev.baud = be32_to_cpu(*val);307 308 val = of_get_flat_dt_prop(node, "clock-frequency", NULL);309 if (val)310 port->uartclk = be32_to_cpu(*val);311 312 if (options) {313 early_console_dev.baud = simple_strtoul(options, NULL, 0);314 strscpy(early_console_dev.options, options,315 sizeof(early_console_dev.options));316 }317 earlycon_init(&early_console_dev, match->name);318 err = match->setup(&early_console_dev, options);319 earlycon_print_info(&early_console_dev);320 if (err < 0)321 return err;322 if (!early_console_dev.con->write)323 return -ENODEV;324 325 326 register_console(early_console_dev.con);327 return 0;328}329 330#endif /* CONFIG_OF_EARLY_FLATTREE */331