Martin Roth has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/49317 )
Change subject: drivers/uart: Add ACPI SPCR table generation. ......................................................................
drivers/uart: Add ACPI SPCR table generation.
The SPCR ACPI table is used to indicate whether a serial port or a non-legacy UART interface is available for use with Microsoft Windows Emergency Management Services (EMS).
It is also used by Linux and BITS to determine the serial port configuration.
For more information, see the Reference Serial Port Console Redirection Table. https://web.archive.org/web/20210111235235/https://docs.microsoft.com/en-us/...
BUG=b:74392237 TEST=Build
Signed-off-by: Martin Roth martin@coreboot.org Change-Id: Ib1e7c17adcf5b2a8206a8308e057855819ec413a --- M src/acpi/acpi.c M src/drivers/uart/acpi/acpi.c M src/drivers/uart/acpi/chip.h M src/drivers/uart/uart8250io.c M src/drivers/uart/uart8250mem.c M src/drivers/uart/util.c M src/include/acpi/acpi.h M src/include/acpi/acpi_device.h M src/include/console/uart.h 9 files changed, 241 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/17/49317/1
diff --git a/src/acpi/acpi.c b/src/acpi/acpi.c index a16800b..727d2ab 100644 --- a/src/acpi/acpi.c +++ b/src/acpi/acpi.c @@ -1665,6 +1665,8 @@ return 1; case CRAT: return 1; + case SPCR: + return 1; default: return -1; } diff --git a/src/drivers/uart/acpi/acpi.c b/src/drivers/uart/acpi/acpi.c index d4b14aa..67f384e 100644 --- a/src/drivers/uart/acpi/acpi.c +++ b/src/drivers/uart/acpi/acpi.c @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */
+#include <acpi/acpi.h> #include <acpi/acpi_device.h> #include <acpi/acpigen.h> #include <console/console.h> +#include <console/uart.h> #include <device/path.h> #include <string.h> #include "chip.h" @@ -149,11 +151,132 @@ return name; }
+/* Update the SPCR table from devicetree settings */ +static void dev_update_spcr(const struct device *dev, struct acpi_spcr *spcr) +{ + struct drivers_uart_acpi_config *config = dev->chip_info; + + if (config->spcr.base_address.addrh) { + spcr->base_address.access_size = config->spcr.base_address.access_size; + spcr->base_address.addrh = config->spcr.base_address.addrh; + spcr->base_address.addrl = config->spcr.base_address.addrl; + spcr->base_address.bit_width = config->spcr.base_address.bit_width; + spcr->base_address.space_id = config->spcr.base_address.space_id; + } + if (config->spcr.base_address.bit_offset) + spcr->base_address.bit_offset = config->spcr.base_address.bit_offset; + if (config->spcr.baud_rate) + spcr->baud_rate = config->spcr.baud_rate; + if (config->spcr.flow_control) + spcr->flow_control = config->spcr.flow_control; + if (config->spcr.global_system_interrupt) + spcr->global_system_interrupt = config->spcr.global_system_interrupt; + if (config->spcr.interface_type) + spcr->interface_type = config->spcr.interface_type; + if (config->spcr.interrupt_type) + spcr->interrupt_type = config->spcr.interrupt_type; + if (config->spcr.irq) + spcr->irq = config->spcr.irq; + if (config->spcr.irq) + spcr->parity = config->spcr.parity; + if (config->spcr.pci_vendor_id) { + spcr->pci_device_number = config->spcr.pci_device_number; + spcr->pci_flags = config->spcr.pci_flags; + spcr->pci_segment = config->spcr.pci_segment; + spcr->pci_vendor_id = config->spcr.pci_vendor_id; + } + if ( config->spcr.pci_bus_number || config->spcr.pci_device_id || + config->spcr.pci_function_number) { + spcr->pci_bus_number = config->spcr.pci_bus_number; + spcr->pci_device_id = config->spcr.pci_device_id; + spcr->pci_function_number = config->spcr.pci_function_number; + } + if (config->spcr.stop_bits) + spcr->stop_bits = config->spcr.stop_bits; + if (config->spcr.terminal_type) + spcr->terminal_type = config->spcr.terminal_type; +} + +/* Return SPCR encoded baudrate */ +static uint8_t get_spcr_baudrate(void) +{ + switch (get_uart_baudrate()) { + case 115200: + return SPCR_115200_BAUD; + case 57600: + return SPCR_57600_BAUD; + case 119200: + return SPCR_19200_BAUD; + case 9600: + return SPCR_9600_BAUD; + default: + printk(BIOS_WARNING, "SPCR Error: Unsupported baud rate.\n" + "SPCR table supports 115200, 57600, 19200, or 9600.\n"); + } + + return 0; +} + +/* + * Serial Port Console Redirection Table (SPCR) + */ +static unsigned long uart_acpi_write_spcr(const struct device *dev, + unsigned long current, + struct acpi_rsdp *rsdp) +{ + struct acpi_table_header *header; + + struct acpi_spcr *spcr = (void *)current; + if (!CONFIG(ENABLE_UART) || !current) + return current; + + printk(BIOS_DEBUG, "ACPI: * SPCR\n"); + memset((void *)spcr, 0, sizeof(struct acpi_spcr)); + + /* Prepare the header */ + header = &(spcr->header); + memcpy(header->signature, "SPCR", 4); + header->length = sizeof(struct acpi_spcr); + header->revision = get_acpi_table_revision(SPCR); + memcpy(header->oem_id, OEM_ID, 6); + memcpy(header->oem_table_id, ACPI_TABLE_CREATOR, 8); + memcpy(header->asl_compiler_id, ASLC, 4); + header->asl_compiler_revision = 0; + /* Set some default values. Override these at the uart level + * with update_spcr_data() + */ + spcr->interrupt_type = SPCR_NO_INTERRUPT_SUPPORTED; + spcr->irq = SPCR_NO_IRQ; + spcr->global_system_interrupt = SPCR_NO_GSI; + spcr->baud_rate = get_spcr_baudrate(); + spcr->parity = SPCR_NO_PARITY; + spcr->stop_bits = SPCR_1_STOP_BIT; + spcr->flow_control = SPCR_NO_FLOW_CONTROL; + spcr->terminal_type = SPCR_TERM_VT100; + spcr->pci_device_id = SPCR_DID_NOT_A_PCI_DEVICE; + spcr->pci_vendor_id = SPCR_VID_NOT_A_PCI_DEVICE; + spcr->pci_bus_number = SPCR_BUS_NOT_A_PCI_DEVICE; + spcr->pci_device_number = SPCR_DEV_NOT_A_PCI_DEVICE; + spcr->pci_function_number = SPCR_FUNC_NOT_A_PCI_DEVICE; + spcr->pci_flags = SPCR_PCI_FLAG_NOT_A_PCI_DEVICE; + spcr->pci_segment = SPCR_PCI_SEGMENT_0; + if (CONFIG(ENABLE_UART)) + update_spcr_data(spcr); + dev_update_spcr(dev, spcr); + if (spcr->baud_rate == 0 || spcr->base_address.addrl == 0) + return current; + + header->checksum = acpi_checksum((u8 *)spcr, spcr->header.length); + acpi_add_table(rsdp, spcr); + return current + sizeof(struct acpi_spcr); +} + static struct device_operations uart_acpi_dev_ops = { .read_resources = noop_read_resources, .set_resources = noop_set_resources, .acpi_fill_ssdt = uart_acpi_fill_ssdt, .acpi_name = uart_acpi_name, + .write_acpi_tables = uart_acpi_write_spcr, };
static void uart_acpi_enable(struct device *dev) diff --git a/src/drivers/uart/acpi/chip.h b/src/drivers/uart/acpi/chip.h index 0311165..9b6264c 100644 --- a/src/drivers/uart/acpi/chip.h +++ b/src/drivers/uart/acpi/chip.h @@ -43,6 +43,9 @@ unsigned int stop_delay_ms; /* Delay to be inserted after enabling stop. */ unsigned int stop_off_delay_ms; + + /* SPCR table */ + struct acpi_spcr spcr; };
#endif /* __DRIVERS_UART_ACPI_H__ */ diff --git a/src/drivers/uart/uart8250io.c b/src/drivers/uart/uart8250io.c index aa8c969..f56b383 100644 --- a/src/drivers/uart/uart8250io.c +++ b/src/drivers/uart/uart8250io.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */
+#include <acpi/acpi_device.h> #include <arch/io.h> #include <boot/coreboot_tables.h> #include <console/uart.h> @@ -119,3 +120,13 @@
lb_add_console(LB_TAG_CONSOLE_SERIAL8250, data); } + +void update_spcr_8250io(struct acpi_spcr *spcr) +{ + spcr->base_address.addrl = uart_platform_base(CONFIG_UART_FOR_CONSOLE); + if (!spcr->base_address.addrl) + return; + spcr->base_address.addrh = 0; /* currently 32 bit addresses only */ + spcr->base_address.space_id = ACPI_ADDRESS_SPACE_IO; + spcr->base_address.access_size = ACPI_ACCESS_SIZE_UNDEFINED; +} diff --git a/src/drivers/uart/uart8250mem.c b/src/drivers/uart/uart8250mem.c index 1834095..ee9b846 100644 --- a/src/drivers/uart/uart8250mem.c +++ b/src/drivers/uart/uart8250mem.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */
+#include <acpi/acpi_device.h> #include <device/mmio.h> #include <boot/coreboot_tables.h> #include <console/uart.h> @@ -151,3 +152,19 @@
lb_add_console(LB_TAG_CONSOLE_SERIAL8250MEM, data); } + +void update_spcr_8250mem(struct acpi_spcr *spcr) +{ + spcr->base_address.addrl = uart_platform_base(CONFIG_UART_FOR_CONSOLE); + if (!spcr->base_address.addrl) + return; + spcr->base_address.space_id = ACPI_ADDRESS_SPACE_MEMORY; + spcr->base_address.addrh = 0; /* currently 32 bit addresses only */ + if (CONFIG(DRIVERS_UART_8250MEM_32)) + spcr->base_address.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS; + else + spcr->base_address.access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS; + spcr->pci_bus_number = (CONFIG_UART_PCI_ADDR >> 20) & 0xff; + spcr->pci_device_number = (CONFIG_UART_PCI_ADDR >> 15) & 0x1f; + spcr->pci_function_number = (CONFIG_UART_PCI_ADDR >> 12) & 0x07; +} diff --git a/src/drivers/uart/util.c b/src/drivers/uart/util.c index 1ac994e..d4dca53 100644 --- a/src/drivers/uart/util.c +++ b/src/drivers/uart/util.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */
+#include <acpi/acpi_device.h> #include <console/uart.h> #include <types.h> #include <timer.h> @@ -70,3 +71,12 @@ while (stopwatch_duration_usecs(&sw) < i * MHz / baud_rate) stopwatch_tick(&sw); } + +/* Generate the SPCR table based on what's enabled */ +void update_spcr_data(struct acpi_spcr *spcr) +{ + if (CONFIG(DRIVERS_UART_8250IO)) + return update_spcr_8250io(spcr); + if (CONFIG(DRIVERS_UART_8250MEM)) + return update_spcr_8250mem(spcr); +} diff --git a/src/include/acpi/acpi.h b/src/include/acpi/acpi.h index 1c364a0..3d46059 100644 --- a/src/include/acpi/acpi.h +++ b/src/include/acpi/acpi.h @@ -71,7 +71,7 @@ enum acpi_tables { /* Tables defined by ACPI and used by coreboot */ BERT, DBG2, DMAR, DSDT, FACS, FADT, HEST, HPET, IVRS, MADT, MCFG, - RSDP, RSDT, SLIT, SRAT, SSDT, TCPA, TPM2, XSDT, ECDT, + RSDP, RSDT, SLIT, SRAT, SSDT, TCPA, TPM2, XSDT, ECDT, SPCR, /* Additional proprietary tables used by coreboot */ VFCT, NHLT, SPMI, CRAT }; diff --git a/src/include/acpi/acpi_device.h b/src/include/acpi/acpi_device.h index 301f9b0..8aa89be 100644 --- a/src/include/acpi/acpi_device.h +++ b/src/include/acpi/acpi_device.h @@ -3,6 +3,7 @@ #ifndef __ACPI_ACPI_DEVICE_H__ #define __ACPI_ACPI_DEVICE_H__
+#include <acpi/acpi.h> #include <device/i2c.h> #include <spi-generic.h> #include <types.h> @@ -458,6 +459,75 @@ unsigned int stop_off_delay_ms; };
+/* SPCR (Serial Port Console Redirection) */ +struct acpi_spcr { + struct acpi_table_header header; + u8 interface_type; + u8 reserved_1; + u8 reserved_2; + u8 reserved_3; + struct acpi_gen_regaddr base_address; + u8 interrupt_type; + u8 irq; + u32 global_system_interrupt; + u8 baud_rate; + u8 parity; + u8 stop_bits; + u8 flow_control; + u8 terminal_type; + u8 reserved_4; + u16 pci_device_id; + u16 pci_vendor_id; + u8 pci_bus_number; + u8 pci_device_number; + u8 pci_function_number; + u32 pci_flags; + u8 pci_segment; + u32 reserved_5; +} __packed; + +#define SPCR_16550_INTERFACE 0 +#define SPCR_16450_INTERFACE 1 + +#define SPCR_RESERVED_MUST_BE_ZERO 0 + +#define SPCR_NO_INTERRUPT_SUPPORTED 0 +#define SPCR_PIC_INTERRUPT_SUPPORTED (1 << 0) +#define SPCR_APIC_INTERRUPT_SUPPORTED (1 << 1) +#define SPCR_SAPIC_INTERRUPT_SUPPORTED (1 << 2) + +#define SPCR_NO_IRQ 0 +#define SPCR_NO_GSI 0 + +#define SPCR_9600_BAUD 3 +#define SPCR_19200_BAUD 4 +#define SPCR_57600_BAUD 6 +#define SPCR_115200_BAUD 7 + +#define SPCR_NO_PARITY 0 + +#define SPCR_1_STOP_BIT 1 + +#define SPCR_NO_FLOW_CONTROL 0 +#define SPCR_DCD_FLOW_CONTROL (1 << 0) +#define SPCR_RTS_CTS_FLOW_CONTROL (1 << 1) +#define SPCR_XON_XOFF_FLOW_CONTROL (1 << 2) + +#define SPCR_TERM_VT100 0 +#define SPCR_TERM_VT100_PLUS 1 +#define SPCR_TERM_VT_UTF8 2 +#define SPCR_TERM_ANSI 3 + +#define SPCR_DID_NOT_A_PCI_DEVICE 0xffff +#define SPCR_VID_NOT_A_PCI_DEVICE 0xffff +#define SPCR_BUS_NOT_A_PCI_DEVICE 0x00 +#define SPCR_DEV_NOT_A_PCI_DEVICE 0x00 +#define SPCR_FUNC_NOT_A_PCI_DEVICE 0x00 +#define SPCR_PCI_FLAG_NOT_A_PCI_DEVICE 0x00 +#define SPCR_PCI_FLAG_NONE 0x00 +#define SPCR_PCI_FLAG_DO_NOT_SUPPRESS_PNP_ENUM (1 << 0) +#define SPCR_PCI_SEGMENT_0 0x00 + /* * Add a basic PowerResource block for a device that includes * GPIOs to control enable, reset and stop operation of the device. Each diff --git a/src/include/console/uart.h b/src/include/console/uart.h index 2e23d43..b7abaff 100644 --- a/src/include/console/uart.h +++ b/src/include/console/uart.h @@ -3,6 +3,7 @@ #ifndef CONSOLE_UART_H #define CONSOLE_UART_H
+#include <acpi/acpi_device.h> #include <stdint.h>
/* Return the clock frequency UART uses as reference clock for @@ -97,5 +98,8 @@ return uart_rx_byte(CONF_UART_FOR_GDB); } #endif +void update_spcr_data(struct acpi_spcr *spcr); +void update_spcr_8250mem(struct acpi_spcr *spcr); +void update_spcr_8250io(struct acpi_spcr *spcr);
#endif /* CONSOLE_UART_H */