diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 6e692f4..13688c0 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -21,6 +21,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); + /* 1 to indicate PSCI 0.2+ is implemented */ static inline bool acpi_psci_present(void) { diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index d109b2f..90ae187 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -28,6 +28,9 @@ #include #include +#include +#include + int acpi_noirq; /* skip ACPI IRQ initialization */ int acpi_disabled; EXPORT_SYMBOL(acpi_disabled); @@ -40,6 +43,12 @@ static int enabled_cpus; /* Processors (GICC) with enabled flag in MADT */ static char *boot_method; static u64 parked_address[NR_CPUS]; +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); + /* * Since we're on ARM, the default interrupt routing model * clearly has to be GIC. @@ -376,3 +385,106 @@ static int __init parse_acpi(char *arg) return 0; } early_param("acpi", parse_acpi); + +bool acpi_spcr_console_check(struct acpi_device *adev, char *name, int index) +{ + printk("JCM: acpi_spcr_console_check: enter\n"); + if (!adev || adev != acpi_spcr_serial_device || console_set_on_cmdline) + { + printk("JCM: conditional test failed\n"); + return false; + } + printk("JCM: would set up preferred console\n"); + printk("JCM: return !add_preferred_console(%s, %d, %s)\n", + name, index, acpi_spcr_serial_options); + + return !add_preferred_console(name, index, + kstrdup(acpi_spcr_serial_options, + GFP_KERNEL)); +} + +static int __init acpi_parse_spcr(struct acpi_table_header *table) +{ + struct acpi_table_spcr *spcr = (struct acpi_table_spcr *)table; + + /* Handle possible alignment issues */ + memcpy(&spcr_serial_addr, + &spcr->serial_port.address, sizeof(spcr_serial_addr)); + + 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; + } + + printk("SPCR serial device: 0x%llx (options: %s)\n", + spcr_serial_addr, acpi_spcr_serial_options); + + return -EOPNOTSUPP; /* value not used yet */ +} + +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; + } + + status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); + if (ACPI_FAILURE(status)) { + return AE_OK; /* continue */ + } + + if (adr == spcr_serial_addr) { + + adev = acpi_bus_get_acpi_device(handle); + + if (!adev) { + printk("Error locating SPCR device from ACPI handle\n"); + return AE_OK; /* skip this one */ + } + + acpi_spcr_serial_device = adev; + + printk("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() +{ + if (0 != acpi_table_parse(ACPI_SIG_SPCR, acpi_parse_spcr)) { + printk("SPCR table not found - automatic 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 053a9b4..f66109e 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -605,6 +605,7 @@ const struct seq_operations cpuinfo_op = { */ static int __init arm64_console_setup(void) { + /* Allow cmdline to override our assumed preferences */ if (console_set_on_cmdline) return 0; @@ -621,3 +622,12 @@ static int __init arm64_console_setup(void) return 0; } early_initcall(arm64_console_setup); + +static int __init arm64_spcr_setup(void) +{ + /* scan the ACPI tables for automatic console configuration */ + 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 57ca61b..143adad 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -2664,8 +2665,12 @@ 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) + if (uport->cons && uport->dev) { + printk("JCM: serial_core: checking for console\n"); of_console_check(uport->dev->of_node, uport->cons->name, uport->line); + acpi_spcr_console_check(ACPI_COMPANION(uport->dev), + uport->cons->name, uport->line); + } uart_configure_port(drv, state, uport);