summaryrefslogtreecommitdiff
path: root/src/soc/intel/common/block/xhci/elog.c
blob: e6a1c0f5daf3c79a7dd826bff7c78d841ba79034 (plain)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* SPDX-License-Identifier: GPL-2.0-only */

#include <device/mmio.h>
#include <device/pci_ops.h>
#include <elog.h>
#include <intelblocks/xhci.h>
#include <soc/pci_devs.h>
#include <stdint.h>

/* Wake on disconnect enable */
#define XHCI_STATUS_WDE			(1 << 26)
/* Wake on connect enable */
#define XHCI_STATUS_WCE			(1 << 25)
/* Port link status change */
#define XHCI_STATUS_PLC			(1 << 22)
/* Connect status change */
#define XHCI_STATUS_CSC			(1 << 17)
/* Port link status */
#define XHCI_STATUS_PLS_SHIFT		(5)
#define XHCI_STATUS_PLS_MASK		(0xF << XHCI_STATUS_PLS_SHIFT)
#define XHCI_STATUS_PLS_RESUME		(15 << XHCI_STATUS_PLS_SHIFT)

static bool xhci_csc_set(uint32_t port_status)
{
	return !!(port_status & XHCI_STATUS_CSC);
}

static bool xhci_wake_capable(uint32_t port_status)
{
	return !!((port_status & XHCI_STATUS_WCE) |
		  (port_status & XHCI_STATUS_WDE));
}

static bool xhci_plc_set(uint32_t port_status)
{
	return !!(port_status & XHCI_STATUS_PLC);
}

static bool xhci_resume(uint32_t port_status)
{
	return (port_status & XHCI_STATUS_PLS_MASK) == XHCI_STATUS_PLS_RESUME;
}

/*
 * Check if a particular USB port caused wake by:
 * 1. Change in connect/disconnect status (if enabled)
 * 2. USB device activity
 *
 * Params:
 * base  : MMIO address of first port.
 * num   : Number of ports.
 * event : Event that needs to be added in case wake source is found.
 *
 * Return value:
 * true  : Wake source was found.
 * false : Wake source was not found.
 */
static bool xhci_port_wake_check(uintptr_t base, uint8_t num, uint8_t host_event, uint8_t event)
{
	uint32_t i, port_status;
	bool found = false;

	for (i = 0; i < num; i++, base += 0x10) {
		/* Read port status and control register for the port. */
		port_status = read32((void *)base);

		/* Ensure that the status is not all 1s. */
		if (port_status == 0xffffffff)
			continue;

		/*
		 * Check if CSC bit is set and port is capable of wake on
		 * connect/disconnect to identify if the port caused wake
		 * event for USB attach/detach.
		 */
		if (xhci_csc_set(port_status) &&
		    xhci_wake_capable(port_status)) {
			elog_add_event_wake(host_event, 0);
			elog_add_event_wake(event, i + 1);
			found = true;
			continue;
		}

		/*
		 * Check if PLC is set and PLS indicates resume to identify if
		 * the port caused wake event for USB activity.
		 */
		if (xhci_plc_set(port_status) &&
		    xhci_resume(port_status)) {
			elog_add_event_wake(host_event, 0);
			elog_add_event_wake(event, i + 1);
			found = true;
		}
	}
	return found;
}

bool xhci_update_wake_event(const struct xhci_wake_info *wake_info,
			    size_t wake_info_count)
{
	const struct xhci_usb_info *usb_info;
	uintptr_t mmio_base;
	bool event_found = false;
	size_t i;

	for (i = 0; i < wake_info_count; ++i) {
		/* Assumes BAR0 is MBAR */
		pci_devfn_t devfn = PCI_DEV(0, PCI_SLOT(wake_info[i].xhci_dev),
					    PCI_FUNC(wake_info[i].xhci_dev));
		mmio_base = pci_s_read_config32(devfn, PCI_BASE_ADDRESS_0);
		mmio_base &= ~PCI_BASE_ADDRESS_MEM_ATTR_MASK;
		usb_info = soc_get_xhci_usb_info(wake_info[i].xhci_dev);

		/* Check USB2 port status & control registers */
		if (xhci_port_wake_check(mmio_base + usb_info->usb2_port_status_reg,
					 usb_info->num_usb2_ports,
					 wake_info[i].elog_wake_type_host,
					 ELOG_WAKE_SOURCE_PME_XHCI_USB_2))
			event_found = true;

		/* Check USB3 port status & control registers */
		if (xhci_port_wake_check(mmio_base + usb_info->usb3_port_status_reg,
					 usb_info->num_usb3_ports,
					 wake_info[i].elog_wake_type_host,
					 ELOG_WAKE_SOURCE_PME_XHCI_USB_3))
			event_found = true;
	}

	return event_found;
}