Skip to content

Commit ad1696f

Browse files
Aleksey Makarovgregkh
authored andcommitted
ACPI: parse SPCR and enable matching console
'ARM Server Base Boot Requiremets' [1] mentions SPCR (Serial Port Console Redirection Table) [2] as a mandatory ACPI table that specifies the configuration of serial console. Defer initialization of DT earlycon until ACPI/DT decision is made. Parse the ACPI SPCR table, setup earlycon if required, enable specified console. Thanks to Peter Hurley for explaining how this should work. [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044a/index.html [2] https://msdn.microsoft.com/en-us/library/windows/hardware/dn639132(v=vs.85).aspx Signed-off-by: Aleksey Makarov <aleksey.makarov@linaro.org> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Peter Hurley <peter@hurleysoftware.com> Tested-by: Kefeng Wang <wangkefeng.wang@huawei.com> Tested-by: Christopher Covington <cov@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent d503187 commit ad1696f

File tree

6 files changed

+146
-3
lines changed

6 files changed

+146
-3
lines changed

drivers/acpi/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ config ACPI_DEBUGGER_USER
7777

7878
endif
7979

80+
config ACPI_SPCR_TABLE
81+
bool
82+
8083
config ACPI_SLEEP
8184
bool
8285
depends on SUSPEND || HIBERNATION

drivers/acpi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
8181
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
8282
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
8383
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
84+
obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o
8485
obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o
8586

8687
# processor has its own "processor." module_param namespace

drivers/acpi/spcr.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright (c) 2012, Intel Corporation
3+
* Copyright (c) 2015, Red Hat, Inc.
4+
* Copyright (c) 2015, 2016 Linaro Ltd.
5+
*
6+
* This program is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License version 2 as
8+
* published by the Free Software Foundation.
9+
*
10+
*/
11+
12+
#define pr_fmt(fmt) "ACPI: SPCR: " fmt
13+
14+
#include <linux/acpi.h>
15+
#include <linux/console.h>
16+
#include <linux/kernel.h>
17+
#include <linux/serial_core.h>
18+
19+
/**
20+
* parse_spcr() - parse ACPI SPCR table and add preferred console
21+
*
22+
* @earlycon: set up earlycon for the console specified by the table
23+
*
24+
* For the architectures with support for ACPI, CONFIG_ACPI_SPCR_TABLE may be
25+
* defined to parse ACPI SPCR table. As a result of the parsing preferred
26+
* console is registered and if @earlycon is true, earlycon is set up.
27+
*
28+
* When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called
29+
* from arch inintialization code as soon as the DT/ACPI decision is made.
30+
*
31+
*/
32+
int __init parse_spcr(bool earlycon)
33+
{
34+
static char opts[64];
35+
struct acpi_table_spcr *table;
36+
acpi_size table_size;
37+
acpi_status status;
38+
char *uart;
39+
char *iotype;
40+
int baud_rate;
41+
int err;
42+
43+
if (acpi_disabled)
44+
return -ENODEV;
45+
46+
status = acpi_get_table_with_size(ACPI_SIG_SPCR, 0,
47+
(struct acpi_table_header **)&table,
48+
&table_size);
49+
50+
if (ACPI_FAILURE(status))
51+
return -ENOENT;
52+
53+
if (table->header.revision < 2) {
54+
err = -ENOENT;
55+
pr_err("wrong table version\n");
56+
goto done;
57+
}
58+
59+
iotype = table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY ?
60+
"mmio" : "io";
61+
62+
switch (table->interface_type) {
63+
case ACPI_DBG2_ARM_SBSA_32BIT:
64+
iotype = "mmio32";
65+
/* fall through */
66+
case ACPI_DBG2_ARM_PL011:
67+
case ACPI_DBG2_ARM_SBSA_GENERIC:
68+
case ACPI_DBG2_BCM2835:
69+
uart = "pl011";
70+
break;
71+
case ACPI_DBG2_16550_COMPATIBLE:
72+
case ACPI_DBG2_16550_SUBSET:
73+
uart = "uart";
74+
break;
75+
default:
76+
err = -ENOENT;
77+
goto done;
78+
}
79+
80+
switch (table->baud_rate) {
81+
case 3:
82+
baud_rate = 9600;
83+
break;
84+
case 4:
85+
baud_rate = 19200;
86+
break;
87+
case 6:
88+
baud_rate = 57600;
89+
break;
90+
case 7:
91+
baud_rate = 115200;
92+
break;
93+
default:
94+
err = -ENOENT;
95+
goto done;
96+
}
97+
98+
snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype,
99+
table->serial_port.address, baud_rate);
100+
101+
pr_info("console: %s\n", opts);
102+
103+
if (earlycon)
104+
setup_earlycon(opts);
105+
106+
err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
107+
108+
done:
109+
early_acpi_os_unmap_memory((void __iomem *)table, table_size);
110+
return err;
111+
}

drivers/tty/serial/earlycon.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <linux/sizes.h>
2222
#include <linux/of.h>
2323
#include <linux/of_fdt.h>
24+
#include <linux/acpi.h>
2425

2526
#ifdef CONFIG_FIX_EARLYCON_MEM
2627
#include <asm/fixmap.h>
@@ -198,6 +199,14 @@ int __init setup_earlycon(char *buf)
198199
return -ENOENT;
199200
}
200201

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+
*/
208+
bool earlycon_init_is_deferred __initdata;
209+
201210
/* early_param wrapper for setup_earlycon() */
202211
static int __init param_setup_earlycon(char *buf)
203212
{
@@ -207,8 +216,14 @@ static int __init param_setup_earlycon(char *buf)
207216
* Just 'earlycon' is a valid param for devicetree earlycons;
208217
* don't generate a warning from parse_early_params() in that case
209218
*/
210-
if (!buf || !buf[0])
211-
return early_init_dt_scan_chosen_stdout();
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+
}
212227

213228
err = setup_earlycon(buf);
214229
if (err == -ENOENT || err == -EALREADY)

include/linux/acpi.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,4 +1074,10 @@ void acpi_table_upgrade(void);
10741074
static inline void acpi_table_upgrade(void) { }
10751075
#endif
10761076

1077+
#ifdef CONFIG_ACPI_SPCR_TABLE
1078+
int parse_spcr(bool earlycon);
1079+
#else
1080+
static inline int parse_spcr(bool earlycon) { return 0; }
1081+
#endif
1082+
10771083
#endif /*_LINUX_ACPI_H*/

include/linux/serial_core.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,18 @@ extern const struct earlycon_id __earlycon_table_end[];
367367

368368
#define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn)
369369

370-
extern int setup_earlycon(char *buf);
371370
extern int of_setup_earlycon(const struct earlycon_id *match,
372371
unsigned long node,
373372
const char *options);
374373

374+
#ifdef CONFIG_SERIAL_EARLYCON
375+
extern bool earlycon_init_is_deferred __initdata;
376+
int setup_earlycon(char *buf);
377+
#else
378+
static const bool earlycon_init_is_deferred;
379+
static inline int setup_earlycon(char *buf) { return 0; }
380+
#endif
381+
375382
struct uart_port *uart_get_console(struct uart_port *ports, int nr,
376383
struct console *c);
377384
int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr,

0 commit comments

Comments
 (0)