LCOV - code coverage report
Current view: top level - drivers/tty/serial - earlycon.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 19 97 19.6 %
Date: 2021-04-22 12:43:58 Functions: 2 7 28.6 %

          Line data    Source code
       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             : 
      29             : static struct console early_con = {
      30             :         .name =         "uart",               /* fixed up at earlycon registration */
      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           0 : static void __iomem * __init earlycon_map(resource_size_t paddr, size_t size)
      40             : {
      41           0 :         void __iomem *base;
      42             : #ifdef CONFIG_FIX_EARLYCON_MEM
      43           0 :         set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);
      44           0 :         base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
      45           0 :         base += paddr & ~PAGE_MASK;
      46             : #else
      47             :         base = ioremap(paddr, size);
      48             : #endif
      49           0 :         if (!base)
      50             :                 pr_err("%s: Couldn't map %pa\n", __func__, &paddr);
      51             : 
      52           0 :         return base;
      53             : }
      54             : 
      55           0 : static void __init earlycon_init(struct earlycon_device *device,
      56             :                                  const char *name)
      57             : {
      58           0 :         struct console *earlycon = device->con;
      59           0 :         const char *s;
      60           0 :         size_t len;
      61             : 
      62             :         /* scan backwards from end of string for first non-numeral */
      63           0 :         for (s = name + strlen(name);
      64           0 :              s > name && s[-1] >= '0' && s[-1] <= '9';
      65           0 :              s--)
      66           0 :                 ;
      67           0 :         if (*s)
      68           0 :                 earlycon->index = simple_strtoul(s, NULL, 10);
      69           0 :         len = s - name;
      70           0 :         strlcpy(earlycon->name, name, min(len + 1, sizeof(earlycon->name)));
      71           0 :         earlycon->data = &early_console_dev;
      72           0 : }
      73             : 
      74           0 : static void __init earlycon_print_info(struct earlycon_device *device)
      75             : {
      76           0 :         struct console *earlycon = device->con;
      77           0 :         struct uart_port *port = &device->port;
      78             : 
      79           0 :         if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM16 ||
      80           0 :             port->iotype == UPIO_MEM32 || port->iotype == UPIO_MEM32BE)
      81           0 :                 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           0 :                 pr_info("%s%d at I/O port 0x%lx (options '%s')\n",
      89             :                         earlycon->name, earlycon->index,
      90             :                         port->iobase, device->options);
      91           0 : }
      92             : 
      93           0 : static int __init parse_options(struct earlycon_device *device, char *options)
      94             : {
      95           0 :         struct uart_port *port = &device->port;
      96           0 :         int length;
      97           0 :         resource_size_t addr;
      98             : 
      99           0 :         if (uart_parse_earlycon(options, &port->iotype, &addr, &options))
     100             :                 return -EINVAL;
     101             : 
     102           0 :         switch (port->iotype) {
     103           0 :         case UPIO_MEM:
     104           0 :                 port->mapbase = addr;
     105           0 :                 break;
     106           0 :         case UPIO_MEM16:
     107           0 :                 port->regshift = 1;
     108           0 :                 port->mapbase = addr;
     109           0 :                 break;
     110           0 :         case UPIO_MEM32:
     111             :         case UPIO_MEM32BE:
     112           0 :                 port->regshift = 2;
     113           0 :                 port->mapbase = addr;
     114           0 :                 break;
     115           0 :         case UPIO_PORT:
     116           0 :                 port->iobase = addr;
     117           0 :                 break;
     118             :         default:
     119             :                 return -EINVAL;
     120             :         }
     121             : 
     122           0 :         if (options) {
     123           0 :                 device->baud = simple_strtoul(options, NULL, 0);
     124           0 :                 length = min(strcspn(options, " ") + 1,
     125             :                              (size_t)(sizeof(device->options)));
     126           0 :                 strlcpy(device->options, options, length);
     127             :         }
     128             : 
     129             :         return 0;
     130             : }
     131             : 
     132           0 : static int __init register_earlycon(char *buf, const struct earlycon_id *match)
     133             : {
     134           0 :         int err;
     135           0 :         struct uart_port *port = &early_console_dev.port;
     136             : 
     137             :         /* On parsing error, pass the options buf to the setup function */
     138           0 :         if (buf && !parse_options(&early_console_dev, buf))
     139           0 :                 buf = NULL;
     140             : 
     141           0 :         spin_lock_init(&port->lock);
     142           0 :         port->uartclk = BASE_BAUD * 16;
     143           0 :         if (port->mapbase)
     144           0 :                 port->membase = earlycon_map(port->mapbase, 64);
     145             : 
     146           0 :         earlycon_init(&early_console_dev, match->name);
     147           0 :         err = match->setup(&early_console_dev, buf);
     148           0 :         earlycon_print_info(&early_console_dev);
     149           0 :         if (err < 0)
     150             :                 return err;
     151           0 :         if (!early_console_dev.con->write)
     152             :                 return -ENODEV;
     153             : 
     154           0 :         register_console(early_console_dev.con);
     155           0 :         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           1 : int __init setup_earlycon(char *buf)
     177             : {
     178           1 :         const struct earlycon_id *match;
     179           1 :         bool empty_compatible = true;
     180             : 
     181           1 :         if (!buf || !buf[0])
     182             :                 return -EINVAL;
     183             : 
     184           1 :         if (early_con.flags & CON_ENABLED)
     185             :                 return -EALREADY;
     186             : 
     187           1 : again:
     188          14 :         for (match = __earlycon_table; match < __earlycon_table_end; match++) {
     189          12 :                 size_t len = strlen(match->name);
     190             : 
     191          12 :                 if (strncmp(buf, match->name, len))
     192          12 :                         continue;
     193             : 
     194             :                 /* prefer entries with empty compatible */
     195           0 :                 if (empty_compatible && *match->compatible)
     196           0 :                         continue;
     197             : 
     198           0 :                 if (buf[len]) {
     199           0 :                         if (buf[len] != ',')
     200           0 :                                 continue;
     201           0 :                         buf += len + 1;
     202             :                 } else
     203             :                         buf = NULL;
     204             : 
     205           0 :                 return register_earlycon(buf, match);
     206             :         }
     207             : 
     208           2 :         if (empty_compatible) {
     209           1 :                 empty_compatible = false;
     210           1 :                 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             : /* early_param wrapper for setup_earlycon() */
     223           1 : static int __init param_setup_earlycon(char *buf)
     224             : {
     225           1 :         int err;
     226             : 
     227             :         /* Just 'earlycon' is a valid param for devicetree and ACPI SPCR. */
     228           1 :         if (!buf || !buf[0]) {
     229           0 :                 if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
     230             :                         earlycon_acpi_spcr_enable = true;
     231             :                         return 0;
     232           0 :                 } else if (!buf) {
     233             :                         return early_init_dt_scan_chosen_stdout();
     234             :                 }
     235             :         }
     236             : 
     237           1 :         err = setup_earlycon(buf);
     238           1 :         if (err == -ENOENT || err == -EALREADY)
     239           1 :                 return 0;
     240             :         return err;
     241             : }
     242             : early_param("earlycon", param_setup_earlycon);
     243             : 
     244             : #ifdef CONFIG_OF_EARLY_FLATTREE
     245             : 
     246             : int __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 */

Generated by: LCOV version 1.14