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>
This commit is contained in:
parent
ab0e680c8e
commit
6cf287efa3
6 changed files with 191 additions and 1 deletions
|
@ -1,6 +1,7 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
#include <amdblocks/acpi.h>
|
#include <amdblocks/acpi.h>
|
||||||
|
#include <amdblocks/xhci.h>
|
||||||
#include <elog.h>
|
#include <elog.h>
|
||||||
#include <soc/southbridge.h>
|
#include <soc/southbridge.h>
|
||||||
|
|
||||||
|
@ -26,9 +27,16 @@ static void elog_gpe_events(const struct acpi_pm_gpe_state *state)
|
||||||
int i;
|
int i;
|
||||||
uint32_t valid_gpe = state->gpe0_sts & state->gpe0_en;
|
uint32_t valid_gpe = state->gpe0_sts & state->gpe0_en;
|
||||||
|
|
||||||
|
if (!ENV_SMM)
|
||||||
|
return;
|
||||||
|
|
||||||
for (i = 0; i <= 31; i++) {
|
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);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
29
src/soc/amd/common/block/include/amdblocks/xhci.h
Normal file
29
src/soc/amd/common/block/include/amdblocks/xhci.h
Normal file
|
@ -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 */
|
16
src/soc/amd/common/block/xhci/Kconfig
Normal file
16
src/soc/amd/common/block/xhci/Kconfig
Normal file
|
@ -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
|
2
src/soc/amd/common/block/xhci/Makefile.inc
Normal file
2
src/soc/amd/common/block/xhci/Makefile.inc
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ramstage-$(CONFIG_SOC_AMD_COMMON_BLOCK_XHCI) += xhci.c
|
||||||
|
smm-$(CONFIG_SOC_AMD_COMMON_BLOCK_XHCI_ELOG) += elog.c
|
116
src/soc/amd/common/block/xhci/elog.c
Normal file
116
src/soc/amd/common/block/xhci/elog.c
Normal file
|
@ -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++;
|
||||||
|
}
|
||||||
|
}
|
19
src/soc/amd/common/block/xhci/xhci.c
Normal file
19
src/soc/amd/common/block/xhci/xhci.c
Normal file
|
@ -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);
|
||||||
|
}
|
Loading…
Reference in a new issue