Skip to content

Commit 9aac588

Browse files
robherringgregkh
authored andcommitted
tty/serial: add generic serial earlycon
This introduces generic earlycon infrastructure for serial devices based on the 8250 earlycon. This allows for supporting earlycon option with other serial devices. The earlycon output is enabled at the time early_params are processed. Only architectures that have fixmap support or have functional ioremap when early_params are processed are supported. This is the same restriction that the 8250 driver had. Signed-off-by: Rob Herring <[email protected]> Cc: Jiri Slaby <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent d20642f commit 9aac588

File tree

4 files changed

+177
-0
lines changed

4 files changed

+177
-0
lines changed

drivers/tty/serial/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ if TTY
77
menu "Serial drivers"
88
depends on HAS_IOMEM
99

10+
config SERIAL_EARLYCON
11+
bool
12+
help
13+
Support for early consoles with the earlycon parameter. This enables
14+
the console before standard serial driver is probed. The console is
15+
enabled when early_param is processed.
16+
1017
source "drivers/tty/serial/8250/Kconfig"
1118

1219
comment "Non-8250 serial port support"

drivers/tty/serial/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
66
obj-$(CONFIG_SERIAL_21285) += 21285.o
77

8+
obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
9+
810
# These Sparc drivers have to appear before others such as 8250
911
# which share ttySx minor node space. Otherwise console device
1012
# names change and other unplesantries.

drivers/tty/serial/earlycon.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (C) 2014 Linaro Ltd.
3+
* Author: Rob Herring <[email protected]>
4+
*
5+
* Based on 8250 earlycon:
6+
* (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
7+
* Bjorn Helgaas <[email protected]>
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+
#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+
19+
#ifdef CONFIG_FIX_EARLYCON_MEM
20+
#include <asm/fixmap.h>
21+
#endif
22+
23+
#include <asm/serial.h>
24+
25+
static struct console early_con = {
26+
.name = "earlycon",
27+
.flags = CON_PRINTBUFFER | CON_BOOT,
28+
.index = -1,
29+
};
30+
31+
static struct earlycon_device early_console_dev = {
32+
.con = &early_con,
33+
};
34+
35+
static void __iomem * __init earlycon_map(unsigned long paddr, size_t size)
36+
{
37+
void __iomem *base;
38+
#ifdef CONFIG_FIX_EARLYCON_MEM
39+
set_fixmap_io(FIX_EARLYCON_MEM_BASE, paddr & PAGE_MASK);
40+
base = (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
41+
base += paddr & ~PAGE_MASK;
42+
#else
43+
base = ioremap(paddr, size);
44+
#endif
45+
if (!base)
46+
pr_err("%s: Couldn't map 0x%llx\n", __func__,
47+
(unsigned long long)paddr);
48+
49+
return base;
50+
}
51+
52+
static int __init parse_options(struct earlycon_device *device,
53+
char *options)
54+
{
55+
struct uart_port *port = &device->port;
56+
int mmio, mmio32, length, ret;
57+
unsigned long addr;
58+
59+
if (!options)
60+
return -ENODEV;
61+
62+
mmio = !strncmp(options, "mmio,", 5);
63+
mmio32 = !strncmp(options, "mmio32,", 7);
64+
if (mmio || mmio32) {
65+
port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
66+
options += mmio ? 5 : 7;
67+
ret = kstrtoul(options, 0, &addr);
68+
if (ret)
69+
return ret;
70+
port->mapbase = addr;
71+
if (mmio32)
72+
port->regshift = 2;
73+
} else if (!strncmp(options, "io,", 3)) {
74+
port->iotype = UPIO_PORT;
75+
options += 3;
76+
ret = kstrtoul(options, 0, &addr);
77+
if (ret)
78+
return ret;
79+
port->iobase = addr;
80+
mmio = 0;
81+
} else if (!strncmp(options, "0x", 2)) {
82+
port->iotype = UPIO_MEM;
83+
ret = kstrtoul(options, 0, &addr);
84+
if (ret)
85+
return ret;
86+
port->mapbase = addr;
87+
} else {
88+
return -EINVAL;
89+
}
90+
91+
port->uartclk = BASE_BAUD * 16;
92+
93+
options = strchr(options, ',');
94+
if (options) {
95+
options++;
96+
ret = kstrtouint(options, 0, &device->baud);
97+
if (ret)
98+
return ret;
99+
length = min(strcspn(options, " ") + 1,
100+
(size_t)(sizeof(device->options)));
101+
strlcpy(device->options, options, length);
102+
}
103+
104+
if (mmio || mmio32)
105+
pr_info("Early serial console at MMIO%s 0x%llx (options '%s')\n",
106+
mmio32 ? "32" : "",
107+
(unsigned long long)port->mapbase,
108+
device->options);
109+
else
110+
pr_info("Early serial console at I/O port 0x%lx (options '%s')\n",
111+
port->iobase,
112+
device->options);
113+
114+
return 0;
115+
}
116+
117+
int __init setup_earlycon(char *buf, const char *match,
118+
int (*setup)(struct earlycon_device *, const char *))
119+
{
120+
int err;
121+
size_t len;
122+
struct uart_port *port = &early_console_dev.port;
123+
124+
if (!buf || !match || !setup)
125+
return 0;
126+
127+
len = strlen(match);
128+
if (strncmp(buf, match, len))
129+
return 0;
130+
if (buf[len] && (buf[len] != ','))
131+
return 0;
132+
133+
buf += len + 1;
134+
135+
err = parse_options(&early_console_dev, buf);
136+
/* On parsing error, pass the options buf to the setup function */
137+
if (!err)
138+
buf = NULL;
139+
140+
if (port->mapbase)
141+
port->membase = earlycon_map(port->mapbase, 64);
142+
143+
early_console_dev.con->data = &early_console_dev;
144+
err = setup(&early_console_dev, buf);
145+
if (err < 0)
146+
return err;
147+
if (!early_console_dev.con->write)
148+
return -ENODEV;
149+
150+
register_console(early_console_dev.con);
151+
return 0;
152+
}

include/linux/serial_core.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,22 @@ static inline int uart_poll_timeout(struct uart_port *port)
285285
/*
286286
* Console helpers.
287287
*/
288+
struct earlycon_device {
289+
struct console *con;
290+
struct uart_port port;
291+
char options[16]; /* e.g., 115200n8 */
292+
unsigned int baud;
293+
};
294+
int setup_earlycon(char *buf, const char *match,
295+
int (*setup)(struct earlycon_device *, const char *));
296+
297+
#define EARLYCON_DECLARE(name, func) \
298+
static int __init name ## _setup_earlycon(char *buf) \
299+
{ \
300+
return setup_earlycon(buf, __stringify(name), func); \
301+
} \
302+
early_param("earlycon", name ## _setup_earlycon);
303+
288304
struct uart_port *uart_get_console(struct uart_port *ports, int nr,
289305
struct console *c);
290306
void uart_parse_options(char *options, int *baud, int *parity, int *bits,

0 commit comments

Comments
 (0)