summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorez Smith <torez@redhat.com>2015-02-24 21:44:58 -0600
committerGraeme Gregory <graeme.gregory@linaro.org>2015-08-31 11:58:18 +0100
commit311bfd7290b127f783d4561795f8b154ce85a8bc (patch)
tree328c93c6d4e800b9fcfb813a6ef1c59b71d664c1
parent1c12d7839e8857c183d602af0f122e67bc252ace (diff)
tty/console: use SPCR table to define console
If console= is not added to the kernel command line, the console is not registered until much further into the booting process. This patch adds support to parse the SPCR ACPI table to pull console support out, then use the appropriate drivers to set up console support earlier in the boot process. Signed-off-by: Jon Masters <jcm@redhat.com> [rebased and cleaned up] Signed-off-by: Torez Smith <torez@redhat.com>
-rw-r--r--arch/arm64/include/asm/acpi.h7
-rw-r--r--arch/arm64/kernel/acpi.c129
-rw-r--r--arch/arm64/kernel/setup.c13
-rw-r--r--drivers/tty/serial/serial_core.c17
4 files changed, 163 insertions, 3 deletions
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index 406485ed110a..221355973835 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -48,6 +48,13 @@ extern int acpi_disabled;
extern int acpi_noirq;
extern int acpi_pci_disabled;
+extern u64 spcr_serial_addr;
+extern int acpi_setup_spcr(void);
+extern struct acpi_device *acpi_spcr_serial_device;
+extern char *acpi_spcr_serial_options;
+extern bool acpi_spcr_console_check(struct acpi_device *adev,
+ char *name, int index);
+
static inline void disable_acpi(void)
{
acpi_disabled = 1;
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 19de7537e7d3..c098e3f2044a 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -24,6 +24,8 @@
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <linux/smp.h>
+#include <linux/console.h>
+#include <linux/tty.h>
#include <asm/cputype.h>
#include <asm/cpu_ops.h>
@@ -69,6 +71,12 @@ static int __init dt_scan_depth1_nodes(unsigned long node,
return 0;
}
+u64 spcr_serial_addr;
+struct acpi_device *acpi_spcr_serial_device;
+EXPORT_SYMBOL(acpi_spcr_serial_device);
+char *acpi_spcr_serial_options;
+EXPORT_SYMBOL(acpi_spcr_serial_options);
+
/*
* __acpi_map_table() will be called before page_init(), so early_ioremap()
* or early_memremap() should be called here to for ACPI table mapping.
@@ -230,3 +238,124 @@ void __init acpi_gic_init(void)
early_acpi_os_unmap_memory((char *)table, tbl_size);
}
+
+bool acpi_spcr_console_check(struct acpi_device *adev, char *name, int index)
+{
+ if (!adev || adev != acpi_spcr_serial_device || console_set_on_cmdline)
+ return false;
+
+ pr_info("adding preferred console [%s]\n", name);
+
+ return !add_preferred_console(name, index,
+ kstrdup(acpi_spcr_serial_options,
+ GFP_KERNEL));
+}
+
+/*
+ * Parse the SPCR table. If we are not working with version 2 or
+ * higher, bail.
+ * Otherwise, pull out the baud rate and address to the console device.
+ */
+static int __init acpi_parse_spcr(struct acpi_table_header *table)
+{
+ struct acpi_table_spcr *spcr = (struct acpi_table_spcr *)table;
+
+ if (table->revision < 2)
+ return -EOPNOTSUPP;
+
+ /* Handle possible alignment issues */
+ memcpy(&spcr_serial_addr,
+ &spcr->serial_port.address, sizeof(spcr_serial_addr));
+
+ /*
+ * The baud rate the BIOS used for redirection. Valid values are....
+ * 3 = 9600
+ * 4 = 19200
+ * 6 = 57600
+ * 7 = 115200
+ * 0-2, 5, 8 - 255 = reserved
+ */
+ switch (spcr->baud_rate) {
+ case 3:
+ acpi_spcr_serial_options = "9600";
+ break;
+ case 4:
+ acpi_spcr_serial_options = "19200";
+ break;
+ case 6:
+ acpi_spcr_serial_options = "57600";
+ break;
+ case 7:
+ acpi_spcr_serial_options = "115200";
+ break;
+ default:
+ acpi_spcr_serial_options = "";
+ break;
+ }
+
+ pr_info("SPCR serial device: 0x%llx (options: %s)\n",
+ spcr_serial_addr, acpi_spcr_serial_options);
+
+ return 0;
+}
+
+/*
+ * Parse an ACPI "Device" to determine if it represents the
+ * data found in the SPCR table. If the associated Device has
+ * and Address entry, and, that Address matches the one found
+ * in our SPCR table, it's the entry we are interested in.
+ *
+ */
+static acpi_status acpi_spcr_device_scan(acpi_handle handle,
+ u32 level, void *context, void **retv)
+{
+ unsigned long long adr;
+ struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status = AE_OK;
+ struct acpi_device *adev;
+
+ status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &name_buffer);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ /*
+ * does thie APCI entry have an associated Address?
+ */
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return AE_OK; /* not device we are interested in... */
+
+ if (adr == spcr_serial_addr) {
+
+ adev = acpi_bus_get_acpi_device(handle);
+
+ if (!adev) {
+ pr_err("Err locating SPCR device from ACPI handle\n");
+ return AE_OK; /* skip this one */
+ }
+
+ acpi_spcr_serial_device = adev;
+
+ pr_info("SPCR serial console: %s (0x%llx)\n",
+ (char *)(name_buffer.pointer), adr);
+
+ return AE_OK; /* harmless to continue */
+ }
+
+ /* continue */
+ return AE_OK; /* continue */
+}
+
+int __init acpi_setup_spcr(void)
+{
+ if (0 != acpi_table_parse(ACPI_SIG_SPCR, acpi_parse_spcr)) {
+ pr_warn("SPCR table not found - auto console disabled\n");
+ return -ENODEV;
+ }
+
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX, acpi_spcr_device_scan,
+ NULL, NULL, NULL);
+
+ return 0;
+}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 634f547a9e67..8669062f2e8b 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -592,3 +592,16 @@ static int __init arm64_console_setup(void)
return 0;
}
early_initcall(arm64_console_setup);
+
+static int __init arm64_spcr_setup(void)
+{
+ /*
+ * If ACPI is enabled, scan the tables for
+ * automatic console configuration
+ */
+ if (!acpi_disabled)
+ acpi_setup_spcr();
+
+ return 0;
+}
+device_initcall(arm64_spcr_setup);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index f36852067f20..e1b4d916898d 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -34,6 +34,7 @@
#include <linux/serial_core.h>
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/acpi.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
@@ -2705,9 +2706,19 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);
}
- if (uport->cons && uport->dev)
- of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
-
+ /*
+ * Support both open FW and ACPI access to console definitions.
+ * Both of_console_check() and acpi_spcr_console_check() will
+ * call add_preferred_console() if a console definition is found.
+ */
+ if (uport->cons && uport->dev) {
+ if (acpi_disabled)
+ of_console_check(uport->dev->of_node, uport->cons->name,
+ uport->line);
+ else
+ acpi_spcr_console_check(ACPI_COMPANION(uport->dev),
+ uport->cons->name, uport->line);
+ }
uart_configure_port(drv, state, uport);
num_groups = 2;