LCOV - code coverage report
Current view: top level - tty/serial - earlycon.c (source / functions) Coverage Total Hit
Test: TTY Combined Coverage Lines: 0.0 % 120 0
Test Date: 2025-08-26 15:45:50 Functions: 0.0 % 8 0

            Line data    Source code
       1              : // SPDX-License-Identifier: GPL-2.0
       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",               /* 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            0 :                 pr_err("%s: Couldn't map %pa\n", __func__, &paddr);
      51              : 
      52            0 :         return base;
      53            0 : }
      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              :                 ;
      67            0 :         if (*s)
      68            0 :                 earlycon->index = simple_strtoul(s, NULL, 10);
      69            0 :         len = s - name;
      70            0 :         strscpy(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            0 :                 return -EINVAL;
     101              : 
     102            0 :         switch (port->iotype) {
     103              :         case UPIO_MEM:
     104            0 :                 port->mapbase = addr;
     105            0 :                 break;
     106              :         case UPIO_MEM16:
     107            0 :                 port->regshift = 1;
     108            0 :                 port->mapbase = addr;
     109            0 :                 break;
     110              :         case UPIO_MEM32:
     111              :         case UPIO_MEM32BE:
     112            0 :                 port->regshift = 2;
     113            0 :                 port->mapbase = addr;
     114            0 :                 break;
     115              :         case UPIO_PORT:
     116            0 :                 port->iobase = addr;
     117            0 :                 break;
     118              :         default:
     119            0 :                 return -EINVAL;
     120              :         }
     121              : 
     122            0 :         if (options) {
     123            0 :                 char *uartclk;
     124              : 
     125            0 :                 device->baud = simple_strtoul(options, NULL, 0);
     126            0 :                 uartclk = strchr(options, ',');
     127            0 :                 if (uartclk && kstrtouint(uartclk + 1, 0, &port->uartclk) < 0)
     128            0 :                         pr_warn("[%s] unsupported earlycon uart clkrate option\n",
     129              :                                 options);
     130            0 :                 length = min(strcspn(options, " ") + 1,
     131              :                              (size_t)(sizeof(device->options)));
     132            0 :                 strscpy(device->options, options, length);
     133            0 :         }
     134              : 
     135            0 :         return 0;
     136            0 : }
     137              : 
     138            0 : static int __init register_earlycon(char *buf, const struct earlycon_id *match)
     139              : {
     140            0 :         int err;
     141            0 :         struct uart_port *port = &early_console_dev.port;
     142              : 
     143              :         /* On parsing error, pass the options buf to the setup function */
     144            0 :         if (buf && !parse_options(&early_console_dev, buf))
     145            0 :                 buf = NULL;
     146              : 
     147            0 :         spin_lock_init(&port->lock);
     148            0 :         if (!port->uartclk)
     149            0 :                 port->uartclk = BASE_BAUD * 16;
     150            0 :         if (port->mapbase)
     151            0 :                 port->membase = earlycon_map(port->mapbase, 64);
     152              : 
     153            0 :         earlycon_init(&early_console_dev, match->name);
     154            0 :         err = match->setup(&early_console_dev, buf);
     155            0 :         earlycon_print_info(&early_console_dev);
     156            0 :         if (err < 0)
     157            0 :                 return err;
     158            0 :         if (!early_console_dev.con->write)
     159            0 :                 return -ENODEV;
     160              : 
     161            0 :         register_console(early_console_dev.con);
     162            0 :         return 0;
     163            0 : }
     164              : 
     165              : /**
     166              :  *      setup_earlycon - match and register earlycon console
     167              :  *      @buf:   earlycon param string
     168              :  *
     169              :  *      Registers the earlycon console matching the earlycon specified
     170              :  *      in the param string @buf. Acceptable param strings are of the form
     171              :  *         <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 the
     177              :  *      <options> string in the 'options' parameter; all other forms set
     178              :  *      the parameter to NULL.
     179              :  *
     180              :  *      Returns 0 if an attempt to register the earlycon was made,
     181              :  *      otherwise negative error code
     182              :  */
     183            0 : int __init setup_earlycon(char *buf)
     184              : {
     185            0 :         const struct earlycon_id *match;
     186            0 :         bool empty_compatible = true;
     187              : 
     188            0 :         if (!buf || !buf[0])
     189            0 :                 return -EINVAL;
     190              : 
     191            0 :         if (console_is_registered(&early_con))
     192            0 :                 return -EALREADY;
     193              : 
     194              : again:
     195            0 :         for (match = __earlycon_table; match < __earlycon_table_end; match++) {
     196            0 :                 size_t len = strlen(match->name);
     197              : 
     198            0 :                 if (strncmp(buf, match->name, len))
     199            0 :                         continue;
     200              : 
     201              :                 /* prefer entries with empty compatible */
     202            0 :                 if (empty_compatible && *match->compatible)
     203            0 :                         continue;
     204              : 
     205            0 :                 if (buf[len]) {
     206            0 :                         if (buf[len] != ',')
     207            0 :                                 continue;
     208            0 :                         buf += len + 1;
     209            0 :                 } else
     210            0 :                         buf = NULL;
     211              : 
     212            0 :                 return register_earlycon(buf, match);
     213            0 :         }
     214              : 
     215            0 :         if (empty_compatible) {
     216            0 :                 empty_compatible = false;
     217            0 :                 goto again;
     218              :         }
     219              : 
     220            0 :         return -ENOENT;
     221            0 : }
     222              : 
     223              : /*
     224              :  * This defers the initialization of the early console until after ACPI has
     225              :  * been initialized.
     226              :  */
     227              : bool earlycon_acpi_spcr_enable __initdata;
     228              : 
     229              : /* early_param wrapper for setup_earlycon() */
     230            0 : static int __init param_setup_earlycon(char *buf)
     231              : {
     232            0 :         int err;
     233              : 
     234              :         /* Just 'earlycon' is a valid param for devicetree and ACPI SPCR. */
     235            0 :         if (!buf || !buf[0]) {
     236              :                 if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
     237            0 :                         earlycon_acpi_spcr_enable = true;
     238            0 :                         return 0;
     239              :                 } else if (!buf) {
     240              :                         return early_init_dt_scan_chosen_stdout();
     241              :                 }
     242              :         }
     243              : 
     244            0 :         err = setup_earlycon(buf);
     245            0 :         if (err == -ENOENT || err == -EALREADY)
     246            0 :                 return 0;
     247            0 :         return err;
     248            0 : }
     249              : early_param("earlycon", param_setup_earlycon);
     250              : 
     251              : /*
     252              :  * The `console` parameter is overloaded. It's handled here as an early param
     253              :  * and in `printk.c` as a late param. It's possible to specify an early
     254              :  * `bootconsole` using `earlycon=uartXXXX` (handled above), or via
     255              :  * the `console=uartXXX` alias. See the comment in `8250_early.c`.
     256              :  */
     257            0 : static int __init param_setup_earlycon_console_alias(char *buf)
     258              : {
     259              :         /*
     260              :          * A plain `console` parameter must not enable the SPCR `bootconsole`
     261              :          * like a plain `earlycon` does.
     262              :          *
     263              :          * A `console=` parameter that specifies an empty value is used to
     264              :          * disable the `console`, not the `earlycon` `bootconsole`. The
     265              :          * disabling of the `console` is handled by `printk.c`.
     266              :          */
     267            0 :         if (!buf || !buf[0])
     268            0 :                 return 0;
     269              : 
     270            0 :         return param_setup_earlycon(buf);
     271            0 : }
     272              : early_param("console", param_setup_earlycon_console_alias);
     273              : 
     274              : #ifdef CONFIG_OF_EARLY_FLATTREE
     275              : 
     276              : int __init of_setup_earlycon(const struct earlycon_id *match,
     277              :                              unsigned long node,
     278              :                              const char *options)
     279              : {
     280              :         int err;
     281              :         struct uart_port *port = &early_console_dev.port;
     282              :         const __be32 *val;
     283              :         bool big_endian;
     284              :         u64 addr;
     285              : 
     286              :         if (console_is_registered(&early_con))
     287              :                 return -EALREADY;
     288              : 
     289              :         spin_lock_init(&port->lock);
     290              :         port->iotype = UPIO_MEM;
     291              :         addr = of_flat_dt_translate_address(node);
     292              :         if (addr == OF_BAD_ADDR) {
     293              :                 pr_warn("[%s] bad address\n", match->name);
     294              :                 return -ENXIO;
     295              :         }
     296              :         port->mapbase = addr;
     297              : 
     298              :         val = of_get_flat_dt_prop(node, "reg-offset", NULL);
     299              :         if (val)
     300              :                 port->mapbase += be32_to_cpu(*val);
     301              :         port->membase = earlycon_map(port->mapbase, SZ_4K);
     302              : 
     303              :         val = of_get_flat_dt_prop(node, "reg-shift", NULL);
     304              :         if (val)
     305              :                 port->regshift = be32_to_cpu(*val);
     306              :         big_endian = of_get_flat_dt_prop(node, "big-endian", NULL) != NULL ||
     307              :                 (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&
     308              :                  of_get_flat_dt_prop(node, "native-endian", NULL) != NULL);
     309              :         val = of_get_flat_dt_prop(node, "reg-io-width", NULL);
     310              :         if (val) {
     311              :                 switch (be32_to_cpu(*val)) {
     312              :                 case 1:
     313              :                         port->iotype = UPIO_MEM;
     314              :                         break;
     315              :                 case 2:
     316              :                         port->iotype = UPIO_MEM16;
     317              :                         break;
     318              :                 case 4:
     319              :                         port->iotype = (big_endian) ? UPIO_MEM32BE : UPIO_MEM32;
     320              :                         break;
     321              :                 default:
     322              :                         pr_warn("[%s] unsupported reg-io-width\n", match->name);
     323              :                         return -EINVAL;
     324              :                 }
     325              :         }
     326              : 
     327              :         val = of_get_flat_dt_prop(node, "current-speed", NULL);
     328              :         if (val)
     329              :                 early_console_dev.baud = be32_to_cpu(*val);
     330              : 
     331              :         val = of_get_flat_dt_prop(node, "clock-frequency", NULL);
     332              :         if (val)
     333              :                 port->uartclk = be32_to_cpu(*val);
     334              : 
     335              :         if (options) {
     336              :                 early_console_dev.baud = simple_strtoul(options, NULL, 0);
     337              :                 strscpy(early_console_dev.options, options,
     338              :                         sizeof(early_console_dev.options));
     339              :         }
     340              :         earlycon_init(&early_console_dev, match->name);
     341              :         err = match->setup(&early_console_dev, options);
     342              :         earlycon_print_info(&early_console_dev);
     343              :         if (err < 0)
     344              :                 return err;
     345              :         if (!early_console_dev.con->write)
     346              :                 return -ENODEV;
     347              : 
     348              : 
     349              :         register_console(early_console_dev.con);
     350              :         return 0;
     351              : }
     352              : 
     353              : #endif /* CONFIG_OF_EARLY_FLATTREE */
        

Generated by: LCOV version 2.0-1