diff options
author | Aleksey Makarov <aleksey.makarov@linaro.org> | 2016-09-27 16:54:13 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-09-28 11:46:46 -0400 |
commit | ad1696f6f09daacfdf2bf04bc83cd8f48d80e34a (patch) | |
tree | 5621bb984dfcd615ce17180fd00f08d8163eb756 | |
parent | d503187b6cc4e41c21c02e695e0e7b5acdd066de (diff) |
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>
-rw-r--r-- | drivers/acpi/Kconfig | 3 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/acpi/spcr.c | 111 | ||||
-rw-r--r-- | drivers/tty/serial/earlycon.c | 19 | ||||
-rw-r--r-- | include/linux/acpi.h | 6 | ||||
-rw-r--r-- | include/linux/serial_core.h | 9 |
6 files changed, 146 insertions, 3 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 445ce28475b3..a984cc7dcba4 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -77,6 +77,9 @@ config ACPI_DEBUGGER_USER | |||
77 | 77 | ||
78 | endif | 78 | endif |
79 | 79 | ||
80 | config ACPI_SPCR_TABLE | ||
81 | bool | ||
82 | |||
80 | config ACPI_SLEEP | 83 | config ACPI_SLEEP |
81 | bool | 84 | bool |
82 | depends on SUSPEND || HIBERNATION | 85 | depends on SUSPEND || HIBERNATION |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 5ae9d85c5159..c469516062c6 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
@@ -81,6 +81,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o | |||
81 | obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o | 81 | obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o |
82 | obj-$(CONFIG_ACPI_BGRT) += bgrt.o | 82 | obj-$(CONFIG_ACPI_BGRT) += bgrt.o |
83 | obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o | 83 | obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o |
84 | obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o | ||
84 | obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o | 85 | obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o |
85 | 86 | ||
86 | # processor has its own "processor." module_param namespace | 87 | # processor has its own "processor." module_param namespace |
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c new file mode 100644 index 000000000000..e8d7bc7d4da8 --- /dev/null +++ b/drivers/acpi/spcr.c | |||
@@ -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 | } | ||
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 836cad222fcc..c3651540e1ba 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/sizes.h> | 21 | #include <linux/sizes.h> |
22 | #include <linux/of.h> | 22 | #include <linux/of.h> |
23 | #include <linux/of_fdt.h> | 23 | #include <linux/of_fdt.h> |
24 | #include <linux/acpi.h> | ||
24 | 25 | ||
25 | #ifdef CONFIG_FIX_EARLYCON_MEM | 26 | #ifdef CONFIG_FIX_EARLYCON_MEM |
26 | #include <asm/fixmap.h> | 27 | #include <asm/fixmap.h> |
@@ -198,6 +199,14 @@ int __init setup_earlycon(char *buf) | |||
198 | return -ENOENT; | 199 | return -ENOENT; |
199 | } | 200 | } |
200 | 201 | ||
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 | |||
201 | /* early_param wrapper for setup_earlycon() */ | 210 | /* early_param wrapper for setup_earlycon() */ |
202 | static int __init param_setup_earlycon(char *buf) | 211 | static int __init param_setup_earlycon(char *buf) |
203 | { | 212 | { |
@@ -207,8 +216,14 @@ static int __init param_setup_earlycon(char *buf) | |||
207 | * Just 'earlycon' is a valid param for devicetree earlycons; | 216 | * Just 'earlycon' is a valid param for devicetree earlycons; |
208 | * don't generate a warning from parse_early_params() in that case | 217 | * don't generate a warning from parse_early_params() in that case |
209 | */ | 218 | */ |
210 | if (!buf || !buf[0]) | 219 | if (!buf || !buf[0]) { |
211 | return early_init_dt_scan_chosen_stdout(); | 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 | } | ||
212 | 227 | ||
213 | err = setup_earlycon(buf); | 228 | err = setup_earlycon(buf); |
214 | if (err == -ENOENT || err == -EALREADY) | 229 | if (err == -ENOENT || err == -EALREADY) |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index c5eaf2f80a4c..2353827731d2 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -1074,4 +1074,10 @@ void acpi_table_upgrade(void); | |||
1074 | static inline void acpi_table_upgrade(void) { } | 1074 | static inline void acpi_table_upgrade(void) { } |
1075 | #endif | 1075 | #endif |
1076 | 1076 | ||
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 | |||
1077 | #endif /*_LINUX_ACPI_H*/ | 1083 | #endif /*_LINUX_ACPI_H*/ |
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 378d80a8dd43..344201437017 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h | |||
@@ -367,11 +367,18 @@ extern const struct earlycon_id __earlycon_table_end[]; | |||
367 | 367 | ||
368 | #define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn) | 368 | #define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn) |
369 | 369 | ||
370 | extern int setup_earlycon(char *buf); | ||
371 | extern int of_setup_earlycon(const struct earlycon_id *match, | 370 | extern int of_setup_earlycon(const struct earlycon_id *match, |
372 | unsigned long node, | 371 | unsigned long node, |
373 | const char *options); | 372 | const char *options); |
374 | 373 | ||
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 | |||
375 | struct uart_port *uart_get_console(struct uart_port *ports, int nr, | 382 | struct uart_port *uart_get_console(struct uart_port *ports, int nr, |
376 | struct console *c); | 383 | struct console *c); |
377 | int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, | 384 | int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, |