diff options
author | Robert Zieba <robertzieba@google.com> | 2022-08-11 11:33:47 -0600 |
---|---|---|
committer | Felix Held <felix-coreboot@felixheld.de> | 2023-03-09 19:36:59 +0000 |
commit | 6cf287efa3749deee51f4ad57482cfeb0cc3103a (patch) | |
tree | 16b4f7dbf64dcbea8e7c7d63194276e01e1dd8bb /src | |
parent | ab0e680c8e027ef76b47a87d4d9e13068b50c630 (diff) |
soc/amd/common/xhci: Add support for logging XHCI wake events
AMD SoCs currently only log the GPE# when an XHCI controller wakes the
system. Add code to log XHCI wake events to the elog.
BRANCH=guybrush
BUG=b:186792595
TEST=builds
Change-Id: Ic0489e1df55c4e63cb8a306099e3f31c82eebd58
Signed-off-by: Robert Zieba <robertzieba@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/67936
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Karthik Ramasubramanian <kramasub@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/soc/amd/common/block/acpi/elog.c | 10 | ||||
-rw-r--r-- | src/soc/amd/common/block/include/amdblocks/xhci.h | 29 | ||||
-rw-r--r-- | src/soc/amd/common/block/xhci/Kconfig | 16 | ||||
-rw-r--r-- | src/soc/amd/common/block/xhci/Makefile.inc | 2 | ||||
-rw-r--r-- | src/soc/amd/common/block/xhci/elog.c | 116 | ||||
-rw-r--r-- | src/soc/amd/common/block/xhci/xhci.c | 19 |
6 files changed, 191 insertions, 1 deletions
diff --git a/src/soc/amd/common/block/acpi/elog.c b/src/soc/amd/common/block/acpi/elog.c index b9268aa7a8..85e4f93498 100644 --- a/src/soc/amd/common/block/acpi/elog.c +++ b/src/soc/amd/common/block/acpi/elog.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include <amdblocks/acpi.h> +#include <amdblocks/xhci.h> #include <elog.h> #include <soc/southbridge.h> @@ -26,9 +27,16 @@ static void elog_gpe_events(const struct acpi_pm_gpe_state *state) int i; uint32_t valid_gpe = state->gpe0_sts & state->gpe0_en; + if (!ENV_SMM) + return; + for (i = 0; i <= 31; i++) { - if (valid_gpe & (1U << i)) + if (valid_gpe & (1U << i)) { elog_add_event_wake(ELOG_WAKE_SOURCE_GPE, i); + + if (CONFIG(SOC_AMD_COMMON_BLOCK_XHCI_ELOG) && i == XHCI_GEVENT) + soc_xhci_log_wake_events(); + } } } diff --git a/src/soc/amd/common/block/include/amdblocks/xhci.h b/src/soc/amd/common/block/include/amdblocks/xhci.h new file mode 100644 index 0000000000..6d1bc260a4 --- /dev/null +++ b/src/soc/amd/common/block/include/amdblocks/xhci.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef AMD_BLOCK_XHCI_H +#define AMD_BLOCK_XHCI_H + +#include <cpu/x86/smm.h> +#include <device/pci_type.h> +#include <device/pci_def.h> +#include <device/xhci.h> + +#include <types.h> + +#define XHCI_GEVENT GEVENT_31 + +#define SOC_XHCI_DEVICES {\ + SOC_XHCI_0,\ + SOC_XHCI_1,\ + SOC_XHCI_2,\ + SOC_XHCI_3,\ + SOC_XHCI_4,\ + SOC_XHCI_5,\ + SOC_XHCI_6,\ + SOC_XHCI_7,\ +} + +void soc_xhci_store_resources(struct smm_pci_resource_info *slots, size_t count); +void soc_xhci_log_wake_events(void); + +#endif /* AMD_BLOCK_XHCI_H */ diff --git a/src/soc/amd/common/block/xhci/Kconfig b/src/soc/amd/common/block/xhci/Kconfig new file mode 100644 index 0000000000..ba9a8e81de --- /dev/null +++ b/src/soc/amd/common/block/xhci/Kconfig @@ -0,0 +1,16 @@ +config SOC_AMD_COMMON_BLOCK_XHCI + bool + help + Select this option to use AMD common XHCI support. + +if SOC_AMD_COMMON_BLOCK_XHCI + +config SOC_AMD_COMMON_BLOCK_XHCI_ELOG + bool + default y + depends on ELOG + select SMM_PCI_RESOURCE_STORE + help + Enables logging of XHCI events in the elog + +endif diff --git a/src/soc/amd/common/block/xhci/Makefile.inc b/src/soc/amd/common/block/xhci/Makefile.inc new file mode 100644 index 0000000000..30f79b73cc --- /dev/null +++ b/src/soc/amd/common/block/xhci/Makefile.inc @@ -0,0 +1,2 @@ +ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_XHCI) += xhci.c +smm-$(CONFIG_SOC_AMD_COMMON_BLOCK_XHCI_ELOG) += elog.c diff --git a/src/soc/amd/common/block/xhci/elog.c b/src/soc/amd/common/block/xhci/elog.c new file mode 100644 index 0000000000..64a2c4811f --- /dev/null +++ b/src/soc/amd/common/block/xhci/elog.c @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <amdblocks/xhci.h> +#include <console/console.h> +#include <cpu/x86/smm.h> +#include <device/pci_def.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <device/pci_type.h> +#include <device/xhci.h> +#include <elog.h> + +#include <inttypes.h> + +#define PORTSC_OFFSET 0x400 +#define PORTSC_STRIDE 0x10 +#define XHCI_PROG_ID 0x30 + +static void xhci_port_wake_check(uintptr_t base, uint8_t controller, uint8_t num, uint8_t event) +{ + for (uint8_t i = 0; i < num; i++) { + uint32_t portsc = read32p(base + i * PORTSC_STRIDE); + + /* Encode the controller number and port number. */ + uint32_t payload = controller << 8 | i; + + /* Ensure that we've read a valid value. */ + if (portsc == 0xffffffff) + continue; + + /* Check for connect/disconnect wake. */ + if (xhci_portsc_csc(portsc) && xhci_portsc_wake_capable(portsc)) { + elog_add_event_wake(event, payload); + continue; + } + + if (xhci_portsc_plc(portsc) && xhci_portsc_resume(portsc)) + elog_add_event_wake(event, payload); + } +} + +struct xhci_context { + uintptr_t bar; + uint8_t controller; +}; + +static void xhci_cap_callback(void *data, const struct xhci_supported_protocol *protocol) +{ + const struct xhci_context *context = (const struct xhci_context *)data; + uint8_t count = protocol->port_count; + const struct xhci_capability_regs *cap_regs = + (const struct xhci_capability_regs *)context->bar; + uint8_t controller = context->controller; + /* PORTSC registers start at operational base + 0x400 + 0x10 * (n - 1). */ + uintptr_t op_base = context->bar + cap_regs->caplength; + uintptr_t addr = op_base + PORTSC_OFFSET + PORTSC_STRIDE * (protocol->port_offset - 1); + + switch (protocol->major_rev) { + case 2: + xhci_port_wake_check(addr, controller, count, ELOG_WAKE_SOURCE_PME_XHCI_USB_2); + break; + + case 3: + xhci_port_wake_check(addr, controller, count, ELOG_WAKE_SOURCE_PME_XHCI_USB_3); + break; + + default: + printk(BIOS_WARNING, "Skipping logging XHCI events for controller %u, unsupported protocol", + controller); + break; + } +} + +void soc_xhci_log_wake_events(void) +{ + const volatile struct smm_pci_resource_info *res_store; + size_t res_count; + uint8_t i_xhci = 0; + + smm_pci_get_stored_resources(&res_store, &res_count); + for (size_t i_slot = 0; i_slot < res_count; i_slot++) { + /* Skip any non-XHCI controller devices. */ + if (res_store[i_slot].class_device != PCI_CLASS_SERIAL_USB || + res_store[i_slot].class_prog != XHCI_PROG_ID) { + continue; + } + + /* Validate our BAR. */ + uintptr_t stored_bar = res_store[i_slot].resources[0].base; + uintptr_t bar = pci_s_read_config32(res_store[i_slot].pci_addr, + PCI_BASE_ADDRESS_0); + bar &= ~PCI_BASE_ADDRESS_MEM_ATTR_MASK; + + if (!stored_bar || !bar || bar != stored_bar) { + printk(BIOS_WARNING, "Skipping logging XHCI events for controller %u, resource error, stored %" PRIxPTR ", found %" PRIxPTR "\n", + i_xhci, stored_bar, bar); + i_xhci++; + continue; + } + + struct xhci_context context = { + .bar = bar, + .controller = i_xhci, + }; + + const struct resource *res = (const struct resource *) &res_store[i_slot].resources[0]; + enum cb_err err + = xhci_resource_for_each_supported_usb_cap(res, &context, + &xhci_cap_callback); + if (err) + printk(BIOS_ERR, "Failed to iterate over capabilities for XHCI controller %u (%d)\n", + i_xhci, err); + + i_xhci++; + } +} diff --git a/src/soc/amd/common/block/xhci/xhci.c b/src/soc/amd/common/block/xhci/xhci.c new file mode 100644 index 0000000000..e5c431acde --- /dev/null +++ b/src/soc/amd/common/block/xhci/xhci.c @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <amdblocks/xhci.h> +#include <cpu/x86/smm.h> +#include <device/device.h> +#include <soc/xhci.h> + +void soc_xhci_store_resources(struct smm_pci_resource_info *slots, size_t count) +{ + const struct device *devices[] = SOC_XHCI_DEVICES; + size_t devices_count; + + for (devices_count = 0; devices_count < ARRAY_SIZE(devices); devices_count++) { + if (!devices[devices_count]) + break; + } + + smm_pci_resource_store_fill_resources(slots, count, &devices[0], devices_count); +} |