瀏覽代碼

x86: acpi: Support generation of the DBG2 table

Add an implementation of the DBG2 (Debug Port Table 2) ACPI table.
Adjust one of the header includes to be in the correct order, before
adding more.

Note that the DBG2 table is generic but the PCI UART is x86-specific at
present since it assumes an ns16550 UART. It can be generalised later
if necessary.

Signed-off-by: Simon Glass <sjg@chromium.org>
Simon Glass 3 年之前
父節點
當前提交
f37979e7b7
共有 4 個文件被更改,包括 156 次插入0 次删除
  1. 11 0
      arch/x86/include/asm/acpi_table.h
  2. 41 0
      arch/x86/lib/acpi_table.c
  3. 40 0
      include/acpi/acpi_table.h
  4. 64 0
      lib/acpi/acpi_table.c

+ 11 - 0
arch/x86/include/asm/acpi_table.h

@@ -46,6 +46,17 @@ u32 acpi_fill_csrt(u32 current);
  */
 int acpi_write_hpet(struct acpi_ctx *ctx);
 
+/**
+ * acpi_write_dbg2_pci_uart() - Write out a DBG2 table
+ *
+ * @ctx: Current ACPI context
+ * @dev: Debug UART device to describe
+ * @access_size: Access size for UART (e.g. ACPI_ACCESS_SIZE_DWORD_ACCESS)
+ * @return 0 if OK, -ve on error
+ */
+int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev,
+			     uint access_size);
+
 /**
  * acpi_create_gnvs() - Create a GNVS (Global Non Volatile Storage) table
  *

+ 41 - 0
arch/x86/lib/acpi_table.c

@@ -15,6 +15,7 @@
 #include <serial.h>
 #include <version.h>
 #include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
 #include <acpi/acpi_table.h>
 #include <asm/acpi/global_nvs.h>
 #include <asm/ioapic.h>
@@ -588,3 +589,43 @@ int acpi_write_hpet(struct acpi_ctx *ctx)
 
 	return 0;
 }
+
+int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev,
+			     uint access_size)
+{
+	struct acpi_dbg2_header *dbg2 = ctx->current;
+	char path[ACPI_PATH_MAX];
+	struct acpi_gen_regaddr address;
+	phys_addr_t addr;
+	int ret;
+
+	if (!device_active(dev)) {
+		log_info("Device not enabled\n");
+		return -EACCES;
+	}
+	/*
+	 * PCI devices don't remember their resource allocation information in
+	 * U-Boot at present. We assume that MMIO is used for the UART and that
+	 * the address space is 32 bytes: ns16550 uses 8 registers of up to
+	 * 32-bits each. This is only for debugging so it is not a big deal.
+	 */
+	addr = dm_pci_read_bar32(dev, 0);
+	printf("UART addr %lx\n", (ulong)addr);
+
+	memset(&address, '\0', sizeof(address));
+	address.space_id = ACPI_ADDRESS_SPACE_MEMORY;
+	address.addrl = (uint32_t)addr;
+	address.addrh = (uint32_t)((addr >> 32) & 0xffffffff);
+	address.access_size = access_size;
+
+	ret = acpi_device_path(dev, path, sizeof(path));
+	if (ret)
+		return log_msg_ret("path", ret);
+	acpi_create_dbg2(dbg2, ACPI_DBG2_SERIAL_PORT,
+			 ACPI_DBG2_16550_COMPATIBLE, &address, 0x1000, path);
+
+	acpi_inc_align(ctx, dbg2->header.length);
+	acpi_add_table(ctx, dbg2);
+
+	return 0;
+}

+ 40 - 0
include/acpi/acpi_table.h

@@ -448,6 +448,29 @@ struct __packed acpi_dmar {
 
 #define ACPI_DBG2_UNKNOWN		0x00FF
 
+/* DBG2: Microsoft Debug Port Table 2 header */
+struct __packed acpi_dbg2_header {
+	struct acpi_table_header header;
+	u32 devices_offset;
+	u32 devices_count;
+};
+
+/* DBG2: Microsoft Debug Port Table 2 device entry */
+struct __packed acpi_dbg2_device {
+	u8  revision;
+	u16 length;
+	u8 address_count;
+	u16 namespace_string_length;
+	u16 namespace_string_offset;
+	u16 oem_data_length;
+	u16 oem_data_offset;
+	u16 port_type;
+	u16 port_subtype;
+	u8  reserved[2];
+	u16 base_address_offset;
+	u16 address_size_offset;
+};
+
 /* SPCR (Serial Port Console Redirection table) */
 struct __packed acpi_spcr {
 	struct acpi_table_header header;
@@ -522,6 +545,23 @@ int acpi_get_table_revision(enum acpi_tables table);
  */
 int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags);
 
+/**
+ * acpi_create_dbg2() - Create a DBG2 table
+ *
+ * This table describes how to access the debug UART
+ *
+ * @dbg2: Place to put information
+ * @port_type: Serial port type (see ACPI_DBG2_...)
+ * @port_subtype: Serial port sub-type (see ACPI_DBG2_...)
+ * @address: ACPI address of port
+ * @address_size: Size of address space
+ * @device_path: Path of device (created using acpi_device_path())
+ */
+void acpi_create_dbg2(struct acpi_dbg2_header *dbg2,
+		      int port_type, int port_subtype,
+		      struct acpi_gen_regaddr *address, uint32_t address_size,
+		      const char *device_path);
+
 /**
  * acpi_fill_header() - Set up a new table header
  *

+ 64 - 0
lib/acpi/acpi_table.c

@@ -264,3 +264,67 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start)
 	 */
 	acpi_align64(ctx);
 }
+
+void acpi_create_dbg2(struct acpi_dbg2_header *dbg2,
+		      int port_type, int port_subtype,
+		      struct acpi_gen_regaddr *address, u32 address_size,
+		      const char *device_path)
+{
+	uintptr_t current;
+	struct acpi_dbg2_device *device;
+	u32 *dbg2_addr_size;
+	struct acpi_table_header *header;
+	size_t path_len;
+	const char *path;
+	char *namespace;
+
+	/* Fill out header fields. */
+	current = (uintptr_t)dbg2;
+	memset(dbg2, '\0', sizeof(struct acpi_dbg2_header));
+	header = &dbg2->header;
+
+	header->revision = acpi_get_table_revision(ACPITAB_DBG2);
+	acpi_fill_header(header, "DBG2");
+	header->aslc_revision = ASL_REVISION;
+
+	/* One debug device defined */
+	dbg2->devices_offset = sizeof(struct acpi_dbg2_header);
+	dbg2->devices_count = 1;
+	current += sizeof(struct acpi_dbg2_header);
+
+	/* Device comes after the header */
+	device = (struct acpi_dbg2_device *)current;
+	memset(device, 0, sizeof(struct acpi_dbg2_device));
+	current += sizeof(struct acpi_dbg2_device);
+
+	device->revision = 0;
+	device->address_count = 1;
+	device->port_type = port_type;
+	device->port_subtype = port_subtype;
+
+	/* Base Address comes after device structure */
+	memcpy((void *)current, address, sizeof(struct acpi_gen_regaddr));
+	device->base_address_offset = current - (uintptr_t)device;
+	current += sizeof(struct acpi_gen_regaddr);
+
+	/* Address Size comes after address structure */
+	dbg2_addr_size = (uint32_t *)current;
+	device->address_size_offset = current - (uintptr_t)device;
+	*dbg2_addr_size = address_size;
+	current += sizeof(uint32_t);
+
+	/* Namespace string comes last, use '.' if not provided */
+	path = device_path ? : ".";
+	/* Namespace string length includes NULL terminator */
+	path_len = strlen(path) + 1;
+	namespace = (char *)current;
+	device->namespace_string_length = path_len;
+	device->namespace_string_offset = current - (uintptr_t)device;
+	strncpy(namespace, path, path_len);
+	current += path_len;
+
+	/* Update structure lengths and checksum */
+	device->length = current - (uintptr_t)device;
+	header->length = current - (uintptr_t)dbg2;
+	header->checksum = table_compute_checksum(dbg2, header->length);
+}