aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Makarov <aleksey.makarov@linaro.org>2016-01-25 17:45:22 +0600
committerGraeme Gregory <graeme.gregory@linaro.org>2016-02-01 09:14:36 +0000
commit676816e3082d5223f2499851fee718cf0807e5f6 (patch)
tree6a3dbfca51c9147bc412bf47727d97f07e6c285f
parentc566b355df920c6080508ea0ba35c3afdacd201f (diff)
downloadleg-kernel-676816e3082d5223f2499851fee718cf0807e5f6.tar.gz
ACPI: parse SPCR and enable matching console
'ARM Server Base Boot Requiremets' [1] mention SPCR (Serial Port Console Redirection Table) [2] as a mandatory ACPI table that specifies the configuration of serial console. Parse this table and check if any registered console match the description. If it does, enable that console. To implement that, introduce a new member int (*acpi_match)(struct console *, struct acpi_table_spcr *) of struct console. It allows drivers to check if they provide a matching console device. [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044a/index.html [2] http://msdn.microsoft.com/en-us/library/windows/hardware/dn639131(v=vs.85).aspx Signed-off-by: Aleksey Makarov <aleksey.makarov@linaro.org>
-rw-r--r--arch/arm64/Kconfig1
-rw-r--r--drivers/acpi/Kconfig3
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/spcr.c85
-rw-r--r--include/linux/console.h13
-rw-r--r--kernel/printk/printk.c82
6 files changed, 168 insertions, 17 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 871f21783866..0887e39faa7b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -3,6 +3,7 @@ config ARM64
select ACPI_CCA_REQUIRED if ACPI
select ACPI_GENERIC_GSI if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
+ select ACPI_SPCR_TABLE if ACPI
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_GCOV_PROFILE_ALL
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5eef4cb4f70e..8ba3657507e2 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT
config ACPI_CCA_REQUIRED
bool
+config ACPI_SPCR_TABLE
+ bool
+
config ACPI_DEBUGGER
bool "AML debugger interface (EXPERIMENTAL)"
select ACPI_DEBUG
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 675eaf337178..af84db417910 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -79,6 +79,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
obj-$(CONFIG_ACPI_BGRT) += bgrt.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-y := processor_driver.o
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
new file mode 100644
index 000000000000..ccb19a0d4bc2
--- /dev/null
+++ b/drivers/acpi/spcr.c
@@ -0,0 +1,85 @@
+/*
+ * 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>
+
+static struct acpi_table_spcr *spcr_table;
+
+int console_acpi_match(struct console *c, char **options)
+{
+ int err;
+
+ if (!c->acpi_match)
+ return -ENODEV;
+
+ if (!spcr_table)
+ return -EAGAIN;
+
+ err = c->acpi_match(c, spcr_table);
+ if (err < 0)
+ return err;
+
+ if (options) {
+ switch (spcr_table->baud_rate) {
+ case 3:
+ *options = "9600";
+ break;
+ case 4:
+ *options = "19200";
+ break;
+ case 6:
+ *options = "57600";
+ break;
+ case 7:
+ *options = "115200";
+ break;
+ default:
+ *options = "";
+ break;
+ }
+ }
+
+ return err;
+}
+
+static int __init spcr_table_detect(void)
+{
+ struct acpi_table_header *table;
+ acpi_status status;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ status = acpi_get_table(ACPI_SIG_SPCR, 0, &table);
+ if (ACPI_FAILURE(status)) {
+ const char *msg = acpi_format_exception(status);
+
+ pr_err("Failed to get table, %s\n", msg);
+ return -EINVAL;
+ }
+
+ if (table->revision < 2)
+ return -EOPNOTSUPP;
+
+ spcr_table = (struct acpi_table_spcr *)table;
+
+ pr_info("Console at 0x%016llx\n", spcr_table->serial_port.address);
+
+ acpi_register_consoles_try_again();
+
+ return 0;
+}
+
+arch_initcall(spcr_table_detect);
diff --git a/include/linux/console.h b/include/linux/console.h
index bd194343c346..cb9ed402cfc1 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -14,6 +14,7 @@
#ifndef _LINUX_CONSOLE_H_
#define _LINUX_CONSOLE_H_ 1
+#include <linux/errno.h>
#include <linux/types.h>
struct vc_data;
@@ -117,6 +118,7 @@ static inline int con_debug_leave(void)
#define CON_BRL (32) /* Used for a braille device */
#define CON_EXTENDED (64) /* Use the extended output format a la /dev/kmsg */
+struct acpi_table_spcr;
struct console {
char name[16];
void (*write)(struct console *, const char *, unsigned);
@@ -125,6 +127,7 @@ struct console {
void (*unblank)(void);
int (*setup)(struct console *, char *);
int (*match)(struct console *, char *name, int idx, char *options);
+ int (*acpi_match)(struct console *, struct acpi_table_spcr *);
short flags;
short index;
int cflag;
@@ -132,6 +135,16 @@ struct console {
struct console *next;
};
+#ifdef CONFIG_ACPI_SPCR_TABLE
+int console_acpi_match(struct console *c, char **options);
+#else
+static inline int console_acpi_match(struct console *c, char **options)
+{
+ return -ENODEV;
+}
+#endif
+void acpi_register_consoles_try_again(void);
+
/*
* for_each_console() allows you to iterate on each console
*/
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 37e531f6b408..3cf8cbae7b7c 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2430,6 +2430,25 @@ static int __init keep_bootcon_setup(char *str)
early_param("keep_bootcon", keep_bootcon_setup);
+static DEFINE_MUTEX(acpi_consoles_delayed_mutex);
+static struct console *acpi_consoles_delayed;
+
+void acpi_register_consoles_try_again(void)
+{
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ while (acpi_consoles_delayed) {
+
+ struct console *c = acpi_consoles_delayed;
+
+ acpi_consoles_delayed = acpi_consoles_delayed->next;
+
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+ register_console(c);
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ }
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+}
+
/*
* The console driver calls this routine during kernel initialization
* to register the console printing procedure with printk() and to
@@ -2538,8 +2557,30 @@ void register_console(struct console *newcon)
break;
}
- if (!(newcon->flags & CON_ENABLED))
- return;
+ if (!(newcon->flags & CON_ENABLED)) {
+ char *opts;
+ int err;
+
+ if (newcon->index < 0)
+ newcon->index = 0;
+
+ err = console_acpi_match(newcon, &opts);
+
+ if (err == -EAGAIN) {
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ newcon->next = acpi_consoles_delayed;
+ acpi_consoles_delayed = newcon;
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+ return;
+ } else if (err < 0) {
+ return;
+ } else {
+ if (newcon->setup && newcon->setup(newcon, opts) != 0)
+ return;
+ newcon->flags |= CON_ENABLED | CON_CONSDEV;
+ preferred_console = true;
+ }
+ }
/*
* If we have a bootconsole, and are switching to a real console,
@@ -2612,34 +2653,41 @@ void register_console(struct console *newcon)
}
EXPORT_SYMBOL(register_console);
+static int delete_from_console_list(struct console **list, struct console *c)
+{
+ while (*list) {
+ struct console *cur = *list;
+
+ if (cur == c) {
+ *list = cur->next;
+ return 0;
+ }
+ list = &cur->next;
+ }
+ return 1;
+}
+
int unregister_console(struct console *console)
{
- struct console *a, *b;
int res;
pr_info("%sconsole [%s%d] disabled\n",
(console->flags & CON_BOOT) ? "boot" : "" ,
console->name, console->index);
+ mutex_lock(&acpi_consoles_delayed_mutex);
+ res = delete_from_console_list(&acpi_consoles_delayed, console);
+ mutex_unlock(&acpi_consoles_delayed_mutex);
+ if (res == 0)
+ return res;
+
res = _braille_unregister_console(console);
if (res)
return res;
- res = 1;
console_lock();
- if (console_drivers == console) {
- console_drivers=console->next;
- res = 0;
- } else if (console_drivers) {
- for (a=console_drivers->next, b=console_drivers ;
- a; b=a, a=b->next) {
- if (a == console) {
- b->next = a->next;
- res = 0;
- break;
- }
- }
- }
+
+ res = delete_from_console_list(&console_drivers, console);
if (!res && (console->flags & CON_EXTENDED))
nr_ext_console_drivers--;