xref: /aosp_15_r20/external/coreboot/src/soc/amd/common/block/xhci/elog.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <amdblocks/xhci.h>
4 #include <console/console.h>
5 #include <cpu/x86/smm.h>
6 #include <device/pci_def.h>
7 #include <device/pci_ids.h>
8 #include <device/pci_ops.h>
9 #include <device/pci_type.h>
10 #include <device/xhci.h>
11 #include <elog.h>
12 
13 #include <inttypes.h>
14 
15 #define PORTSC_OFFSET 0x400
16 #define PORTSC_STRIDE 0x10
17 #define XHCI_PROG_ID 0x30
18 
xhci_port_wake_check(uintptr_t base,uint8_t controller,uint8_t num,uint8_t event)19 static void xhci_port_wake_check(uintptr_t base, uint8_t controller, uint8_t num, uint8_t event)
20 {
21 	for (uint8_t i = 0; i < num; i++) {
22 		uint32_t portsc = read32p(base + i * PORTSC_STRIDE);
23 
24 		/* Encode the controller number and port number. */
25 		uint32_t payload = controller << 8 | i;
26 
27 		/* Ensure that we've read a valid value. */
28 		if (portsc == 0xffffffff)
29 			continue;
30 
31 		/* Check for connect/disconnect wake. */
32 		if (xhci_portsc_csc(portsc) && xhci_portsc_wake_capable(portsc)) {
33 			elog_add_event_wake(event, payload);
34 			continue;
35 		}
36 
37 		if (xhci_portsc_plc(portsc) && xhci_portsc_resume(portsc))
38 			elog_add_event_wake(event, payload);
39 	}
40 }
41 
42 struct xhci_context {
43 	uintptr_t bar;
44 	uint8_t controller;
45 };
46 
xhci_cap_callback(void * data,const struct xhci_supported_protocol * protocol)47 static void xhci_cap_callback(void *data, const struct xhci_supported_protocol *protocol)
48 {
49 	const struct xhci_context *context = (const struct xhci_context *)data;
50 	uint8_t count = protocol->port_count;
51 	const struct xhci_capability_regs *cap_regs =
52 		(const struct xhci_capability_regs *)context->bar;
53 	uint8_t controller = context->controller;
54 	/* PORTSC registers start at operational base + 0x400 + 0x10 * (n - 1). */
55 	uintptr_t op_base = context->bar + cap_regs->caplength;
56 	uintptr_t addr = op_base + PORTSC_OFFSET + PORTSC_STRIDE * (protocol->port_offset - 1);
57 
58 	switch (protocol->major_rev) {
59 	case 2:
60 		xhci_port_wake_check(addr, controller, count, ELOG_WAKE_SOURCE_PME_XHCI_USB_2);
61 		break;
62 
63 	case 3:
64 		xhci_port_wake_check(addr, controller, count, ELOG_WAKE_SOURCE_PME_XHCI_USB_3);
65 		break;
66 
67 	default:
68 		printk(BIOS_WARNING, "Skipping logging XHCI events for controller %u, unsupported protocol",
69 		       controller);
70 		break;
71 	}
72 }
73 
soc_xhci_log_wake_events(void)74 void soc_xhci_log_wake_events(void)
75 {
76 	const volatile struct smm_pci_resource_info *res_store;
77 	size_t res_count;
78 	uint8_t i_xhci = 0;
79 
80 	smm_pci_get_stored_resources(&res_store, &res_count);
81 	for (size_t i_slot = 0; i_slot < res_count; i_slot++) {
82 		/* Skip any non-XHCI controller devices. */
83 		if (res_store[i_slot].class_device != PCI_CLASS_SERIAL_USB ||
84 		    res_store[i_slot].class_prog != XHCI_PROG_ID) {
85 			continue;
86 		}
87 
88 		/* Validate our BAR. */
89 		uintptr_t stored_bar = res_store[i_slot].resources[0].base;
90 		uintptr_t bar = pci_s_read_config32(res_store[i_slot].pci_addr,
91 						    PCI_BASE_ADDRESS_0);
92 		bar &= ~PCI_BASE_ADDRESS_MEM_ATTR_MASK;
93 
94 		if (!stored_bar || !bar || bar != stored_bar) {
95 			printk(BIOS_WARNING, "Skipping logging XHCI events for controller %u, resource error, stored %" PRIxPTR ", found %" PRIxPTR "\n",
96 			       i_xhci, stored_bar, bar);
97 			i_xhci++;
98 			continue;
99 		}
100 
101 		struct xhci_context context = {
102 			.bar = bar,
103 			.controller = i_xhci,
104 		};
105 
106 		const struct resource *res = (const struct resource *) &res_store[i_slot].resources[0];
107 		enum cb_err err
108 			= xhci_resource_for_each_supported_usb_cap(res, &context,
109 								   &xhci_cap_callback);
110 		if (err)
111 			printk(BIOS_ERR, "Failed to iterate over capabilities for XHCI controller %u (%d)\n",
112 			       i_xhci, err);
113 
114 		i_xhci++;
115 	}
116 }
117