aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Makarov <aleksey.makarov@linaro.org>2016-09-27 16:54:13 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-28 11:46:46 -0400
commitad1696f6f09daacfdf2bf04bc83cd8f48d80e34a (patch)
tree5621bb984dfcd615ce17180fd00f08d8163eb756
parentd503187b6cc4e41c21c02e695e0e7b5acdd066de (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/Kconfig3
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/spcr.c111
-rw-r--r--drivers/tty/serial/earlycon.c19
-rw-r--r--include/linux/acpi.h6
-rw-r--r--include/linux/serial_core.h9
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
78endif 78endif
79 79
80config ACPI_SPCR_TABLE
81 bool
82
80config ACPI_SLEEP 83config 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
81obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o 81obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
82obj-$(CONFIG_ACPI_BGRT) += bgrt.o 82obj-$(CONFIG_ACPI_BGRT) += bgrt.o
83obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o 83obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
84obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o
84obj-$(CONFIG_ACPI_DEBUGGER_USER) += acpi_dbg.o 85obj-$(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 */
32int __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
108done:
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 */
208bool earlycon_init_is_deferred __initdata;
209
201/* early_param wrapper for setup_earlycon() */ 210/* early_param wrapper for setup_earlycon() */
202static int __init param_setup_earlycon(char *buf) 211static 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);
1074static inline void acpi_table_upgrade(void) { } 1074static inline void acpi_table_upgrade(void) { }
1075#endif 1075#endif
1076 1076
1077#ifdef CONFIG_ACPI_SPCR_TABLE
1078int parse_spcr(bool earlycon);
1079#else
1080static 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
370extern int setup_earlycon(char *buf);
371extern int of_setup_earlycon(const struct earlycon_id *match, 370extern 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
375extern bool earlycon_init_is_deferred __initdata;
376int setup_earlycon(char *buf);
377#else
378static const bool earlycon_init_is_deferred;
379static inline int setup_earlycon(char *buf) { return 0; }
380#endif
381
375struct uart_port *uart_get_console(struct uart_port *ports, int nr, 382struct uart_port *uart_get_console(struct uart_port *ports, int nr,
376 struct console *c); 383 struct console *c);
377int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr, 384int uart_parse_earlycon(char *p, unsigned char *iotype, resource_size_t *addr,