1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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++;
}
}
|