Commit 4bec233f authored by Aleksey Makarov's avatar Aleksey Makarov Committed by Kleber Sacilotto de Souza

ACPI: parse SPCR and enable matching console

BugLink: https://bugs.launchpad.net/bugs/1744754

'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).aspxSigned-off-by: default avatarAleksey Makarov <aleksey.makarov@linaro.org>
Acked-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Tested-by: default avatarKefeng Wang <wangkefeng.wang@huawei.com>
Tested-by: default avatarChristopher Covington <cov@codeaurora.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
(backported from commit ad1696f6)
[ dannf: offset adjustments ]
Signed-off-by: default avatardann frazier <dann.frazier@canonical.com>
Acked-by: default avatarStefan Bader <stefan.bader@canonical.com>
Acked-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent 2e859003
...@@ -66,6 +66,9 @@ config ACPI_DEBUGGER ...@@ -66,6 +66,9 @@ config ACPI_DEBUGGER
This is still under development, currently enabling this only This is still under development, currently enabling this only
results in the compilation of the ACPICA debugger files. results in the compilation of the ACPICA debugger files.
config ACPI_SPCR_TABLE
bool
config ACPI_SLEEP config ACPI_SLEEP
bool bool
depends on SUSPEND || HIBERNATION depends on SUSPEND || HIBERNATION
......
...@@ -78,6 +78,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o ...@@ -78,6 +78,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_acpi.o
obj-$(CONFIG_ACPI_SPCR_TABLE) += spcr.o
# processor has its own "processor." module_param namespace # processor has its own "processor." module_param namespace
processor-y := processor_driver.o processor-y := processor_driver.o
......
/*
* Copyright (c) 2012, Intel Corporation
* Copyright (c) 2015, Red Hat, Inc.
* Copyright (c) 2015, 2016 Linaro Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#define pr_fmt(fmt) "ACPI: SPCR: " fmt
#include <linux/acpi.h>
#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/serial_core.h>
/**
* parse_spcr() - parse ACPI SPCR table and add preferred console
*
* @earlycon: set up earlycon for the console specified by the table
*
* For the architectures with support for ACPI, CONFIG_ACPI_SPCR_TABLE may be
* defined to parse ACPI SPCR table. As a result of the parsing preferred
* console is registered and if @earlycon is true, earlycon is set up.
*
* When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called
* from arch inintialization code as soon as the DT/ACPI decision is made.
*
*/
int __init parse_spcr(bool earlycon)
{
static char opts[64];
struct acpi_table_spcr *table;
acpi_size table_size;
acpi_status status;
char *uart;
char *iotype;
int baud_rate;
int err;
if (acpi_disabled)
return -ENODEV;
status = acpi_get_table_with_size(ACPI_SIG_SPCR, 0,
(struct acpi_table_header **)&table,
&table_size);
if (ACPI_FAILURE(status))
return -ENOENT;
if (table->header.revision < 2) {
err = -ENOENT;
pr_err("wrong table version\n");
goto done;
}
iotype = table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY ?
"mmio" : "io";
switch (table->interface_type) {
case ACPI_DBG2_ARM_SBSA_32BIT:
iotype = "mmio32";
/* fall through */
case ACPI_DBG2_ARM_PL011:
case ACPI_DBG2_ARM_SBSA_GENERIC:
case ACPI_DBG2_BCM2835:
uart = "pl011";
break;
case ACPI_DBG2_16550_COMPATIBLE:
case ACPI_DBG2_16550_SUBSET:
uart = "uart";
break;
default:
err = -ENOENT;
goto done;
}
switch (table->baud_rate) {
case 3:
baud_rate = 9600;
break;
case 4:
baud_rate = 19200;
break;
case 6:
baud_rate = 57600;
break;
case 7:
baud_rate = 115200;
break;
default:
err = -ENOENT;
goto done;
}
snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype,
table->serial_port.address, baud_rate);
pr_info("console: %s\n", opts);
if (earlycon)
setup_earlycon(opts);
err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
done:
early_acpi_os_unmap_memory((void __iomem *)table, table_size);
return err;
}
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/of_fdt.h> #include <linux/of_fdt.h>
#include <linux/acpi.h>
#ifdef CONFIG_FIX_EARLYCON_MEM #ifdef CONFIG_FIX_EARLYCON_MEM
#include <asm/fixmap.h> #include <asm/fixmap.h>
...@@ -179,6 +180,14 @@ int __init setup_earlycon(char *buf) ...@@ -179,6 +180,14 @@ int __init setup_earlycon(char *buf)
return -ENOENT; return -ENOENT;
} }
/*
* When CONFIG_ACPI_SPCR_TABLE is defined, "earlycon" without parameters in
* command line does not start DT earlycon immediately, instead it defers
* starting it until DT/ACPI decision is made. At that time if ACPI is enabled
* call parse_spcr(), else call early_init_dt_scan_chosen_stdout()
*/
bool earlycon_init_is_deferred __initdata;
/* early_param wrapper for setup_earlycon() */ /* early_param wrapper for setup_earlycon() */
static int __init param_setup_earlycon(char *buf) static int __init param_setup_earlycon(char *buf)
{ {
...@@ -188,8 +197,14 @@ static int __init param_setup_earlycon(char *buf) ...@@ -188,8 +197,14 @@ static int __init param_setup_earlycon(char *buf)
* Just 'earlycon' is a valid param for devicetree earlycons; * Just 'earlycon' is a valid param for devicetree earlycons;
* don't generate a warning from parse_early_params() in that case * don't generate a warning from parse_early_params() in that case
*/ */
if (!buf || !buf[0]) if (!buf || !buf[0]) {
return early_init_dt_scan_chosen_stdout(); if (IS_ENABLED(CONFIG_ACPI_SPCR_TABLE)) {
earlycon_init_is_deferred = true;
return 0;
} else {
return early_init_dt_scan_chosen_stdout();
}
}
err = setup_earlycon(buf); err = setup_earlycon(buf);
if (err == -ENOENT || err == -EALREADY) if (err == -ENOENT || err == -EALREADY)
......
...@@ -932,4 +932,10 @@ static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev, ...@@ -932,4 +932,10 @@ static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
#define acpi_probe_device_table(t) ({ int __r = 0; __r;}) #define acpi_probe_device_table(t) ({ int __r = 0; __r;})
#endif #endif
#ifdef CONFIG_ACPI_SPCR_TABLE
int parse_spcr(bool earlycon);
#else
static inline int parse_spcr(bool earlycon) { return 0; }
#endif
#endif /*_LINUX_ACPI_H*/ #endif /*_LINUX_ACPI_H*/
...@@ -344,7 +344,6 @@ struct earlycon_id { ...@@ -344,7 +344,6 @@ struct earlycon_id {
int (*setup)(struct earlycon_device *, const char *options); int (*setup)(struct earlycon_device *, const char *options);
} __aligned(32); } __aligned(32);
extern int setup_earlycon(char *buf);
extern int of_setup_earlycon(unsigned long addr, extern int of_setup_earlycon(unsigned long addr,
int (*setup)(struct earlycon_device *, const char *)); int (*setup)(struct earlycon_device *, const char *));
...@@ -357,6 +356,14 @@ extern int of_setup_earlycon(unsigned long addr, ...@@ -357,6 +356,14 @@ extern int of_setup_earlycon(unsigned long addr,
#define OF_EARLYCON_DECLARE(name, compat, fn) \ #define OF_EARLYCON_DECLARE(name, compat, fn) \
_OF_DECLARE(earlycon, name, compat, fn, void *) _OF_DECLARE(earlycon, name, compat, fn, void *)
#ifdef CONFIG_SERIAL_EARLYCON
extern bool earlycon_init_is_deferred __initdata;
int setup_earlycon(char *buf);
#else
static const bool earlycon_init_is_deferred;
static inline int setup_earlycon(char *buf) { return 0; }
#endif
struct uart_port *uart_get_console(struct uart_port *ports, int nr, struct uart_port *uart_get_console(struct uart_port *ports, int nr,
struct console *c); struct console *c);
int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment