x86: acpi: Generate SPCR table
authorAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Tue, 20 Nov 2018 21:52:38 +0000 (23:52 +0200)
committerSimon Glass <sjg@chromium.org>
Wed, 5 Dec 2018 14:23:16 +0000 (07:23 -0700)
Microsoft specifies a SPCR (Serial Port Console Redirection Table) [1].
Let's provide it in U-Boot.

[1]: https://docs.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table

Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
arch/x86/include/asm/acpi_table.h
arch/x86/lib/acpi_table.c

index 51cc80667346d7b137b39a15be5ada5e50b63420..e3b65cff66d907cccaffdfc79ad92e433eecf38e 100644 (file)
@@ -327,6 +327,8 @@ struct acpi_global_nvs;
 #define ACPI_DBG2_USB_XHCI             0x0000
 #define ACPI_DBG2_USB_EHCI             0x0001
 
+#define ACPI_DBG2_UNKNOWN              0x00FF
+
 /* SPCR (Serial Port Console Redirection table) */
 struct __packed acpi_spcr {
        struct acpi_table_header header;
index e48c9b957447e7a84a7ab0161436f9091f382515..79bc2000bdaa14df5370681e498d2a957bb670f0 100644 (file)
@@ -10,6 +10,7 @@
 #include <cpu.h>
 #include <dm.h>
 #include <dm/uclass-internal.h>
+#include <serial.h>
 #include <version.h>
 #include <asm/acpi/global_nvs.h>
 #include <asm/acpi_table.h>
@@ -336,6 +337,115 @@ static void acpi_create_mcfg(struct acpi_mcfg *mcfg)
        header->checksum = table_compute_checksum((void *)mcfg, header->length);
 }
 
+static void acpi_create_spcr(struct acpi_spcr *spcr)
+{
+       struct acpi_table_header *header = &(spcr->header);
+       struct serial_device_info serial_info = {0};
+       ulong serial_address, serial_offset;
+       uint serial_config;
+       uint serial_width;
+       int access_size;
+       int space_id;
+       int ret;
+
+       /* Fill out header fields */
+       acpi_fill_header(header, "SPCR");
+       header->length = sizeof(struct acpi_spcr);
+       header->revision = 2;
+
+       ret = serial_getinfo(&serial_info);
+       if (ret)
+               serial_info.type = SERIAL_CHIP_UNKNOWN;
+
+       /* Encode chip type */
+       switch (serial_info.type) {
+       case SERIAL_CHIP_16550_COMPATIBLE:
+               spcr->interface_type = ACPI_DBG2_16550_COMPATIBLE;
+               break;
+       case SERIAL_CHIP_UNKNOWN:
+       default:
+               spcr->interface_type = ACPI_DBG2_UNKNOWN;
+               break;
+       }
+
+       /* Encode address space */
+       switch (serial_info.addr_space) {
+       case SERIAL_ADDRESS_SPACE_MEMORY:
+               space_id = ACPI_ADDRESS_SPACE_MEMORY;
+               break;
+       case SERIAL_ADDRESS_SPACE_IO:
+       default:
+               space_id = ACPI_ADDRESS_SPACE_IO;
+               break;
+       }
+
+       serial_width = serial_info.reg_width * 8;
+       serial_offset = serial_info.reg_offset << serial_info.reg_shift;
+       serial_address = serial_info.addr + serial_offset;
+
+       /* Encode register access size */
+       switch (serial_info.reg_shift) {
+       case 0:
+               access_size = ACPI_ACCESS_SIZE_BYTE_ACCESS;
+               break;
+       case 1:
+               access_size = ACPI_ACCESS_SIZE_WORD_ACCESS;
+               break;
+       case 2:
+               access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+               break;
+       case 3:
+               access_size = ACPI_ACCESS_SIZE_QWORD_ACCESS;
+               break;
+       default:
+               access_size = ACPI_ACCESS_SIZE_UNDEFINED;
+               break;
+       }
+
+       debug("UART type %u @ %lx\n", spcr->interface_type, serial_address);
+
+       /* Fill GAS */
+       spcr->serial_port.space_id = space_id;
+       spcr->serial_port.bit_width = serial_width;
+       spcr->serial_port.bit_offset = 0;
+       spcr->serial_port.access_size = access_size;
+       spcr->serial_port.addrl = lower_32_bits(serial_address);
+       spcr->serial_port.addrh = upper_32_bits(serial_address);
+
+       /* Encode baud rate */
+       switch (serial_info.baudrate) {
+       case 9600:
+               spcr->baud_rate = 3;
+               break;
+       case 19200:
+               spcr->baud_rate = 4;
+               break;
+       case 57600:
+               spcr->baud_rate = 6;
+               break;
+       case 115200:
+               spcr->baud_rate = 7;
+               break;
+       default:
+               spcr->baud_rate = 0;
+               break;
+       }
+
+       ret = serial_getconfig(&serial_config);
+       if (ret)
+               serial_config = SERIAL_DEFAULT_CONFIG;
+
+       spcr->parity = SERIAL_GET_PARITY(serial_config);
+       spcr->stop_bits = SERIAL_GET_STOP(serial_config);
+
+       /* No PCI devices for now */
+       spcr->pci_device_id = 0xffff;
+       spcr->pci_vendor_id = 0xffff;
+
+       /* Fix checksum */
+       header->checksum = table_compute_checksum((void *)spcr, header->length);
+}
+
 /*
  * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
  */
@@ -350,6 +460,7 @@ ulong write_acpi_tables(ulong start)
        struct acpi_fadt *fadt;
        struct acpi_mcfg *mcfg;
        struct acpi_madt *madt;
+       struct acpi_spcr *spcr;
        int i;
 
        current = start;
@@ -438,6 +549,13 @@ ulong write_acpi_tables(ulong start)
        acpi_add_table(rsdp, mcfg);
        current = ALIGN(current, 16);
 
+       debug("ACPI:    * SPCR\n");
+       spcr = (struct acpi_spcr *)current;
+       acpi_create_spcr(spcr);
+       current += spcr->header.length;
+       acpi_add_table(rsdp, spcr);
+       current = ALIGN(current, 16);
+
        debug("current = %x\n", current);
 
        acpi_rsdp_addr = (unsigned long)rsdp;