Furquan Shaikh has uploaded this change for review. ( https://review.coreboot.org/20122
Change subject: soc/intel/skylake: Add USB port number information to wake source ......................................................................
soc/intel/skylake: Add USB port number information to wake source
USB port status register can be used to decide if a particular port was responsible for generating PME# resulting in device wake: 1. CCS bit is set and port is capable of waking on connect/disconnect 2. PLC bit is set and port is in resume state
BUG=b:37088992 TEST=Verified with wake on USB2.0 port 3, mosys shows:
19 | 2017-06-08 15:43:30 | Wake Source | PME - XHCI (USB 2.0 port) | 3
Change-Id: Ie4fa87393d8f096c4b3dca5f7a97f194cb065468 Signed-off-by: Furquan Shaikh furquan@chromium.org --- M src/soc/intel/skylake/elog.c 1 file changed, 120 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/22/20122/1
diff --git a/src/soc/intel/skylake/elog.c b/src/soc/intel/skylake/elog.c index d127fcc..4cdcd2c 100644 --- a/src/soc/intel/skylake/elog.c +++ b/src/soc/intel/skylake/elog.c @@ -23,6 +23,7 @@ #include <soc/pci_devs.h> #include <soc/pm.h> #include <soc/smbus.h> +#include <stdint.h>
static void pch_log_gpio_gpe(u32 gpe0_sts, u32 gpe0_en, int start) { @@ -36,6 +37,112 @@ } }
+#define XHCI_USB2_PORT_STATUS_REG 0x480 +#define XHCI_USB3_PORT_STATUS_REG 0x540 +#define XHCI_USB2_PORT_NUM 10 +#define XHCI_USB3_PORT_NUM 6 +/* 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 pch_xhci_csc_set(uint32_t port_status) +{ + return !!(port_status & XHCI_STATUS_CSC); +} + +static bool pch_xhci_wake_capable(uint32_t port_status) +{ + return (port_status & XHCI_STATUS_WCE) | + (port_status & XHCI_STATUS_WDE); +} + +static bool pch_xhci_plc_set(uint32_t port_status) +{ + return !!(port_status & XHCI_STATUS_PLC); +} + +static bool pch_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 + * + * Return value: + * 0 = None of the ports caused a wake + * n = Port number that caused the wake + */ +static uint32_t pch_xhci_port_wake_check(uintptr_t base, uint8_t num) +{ + uint8_t i; + uint32_t port_status; + + for (i = 0; i < num; i++, base += 0x10) { + /* Read port status and control register for the port. */ + port_status = read32((void *)base); + + /* + * 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 (pch_xhci_csc_set(port_status) && + pch_xhci_wake_capable(port_status)) + return i + 1; + + /* + * Check if PLC is set and PLS indicates resume to identify if + * the port caused wake event for usb activity. + */ + if (pch_xhci_plc_set(port_status) && + pch_xhci_resume(port_status)) + return i + 1; + } + return 0; +} + +/* + * Update elog event and instance depending upon the USB2/USB3 port that caused + * the wake event. + */ +static void pch_xhci_update_wake_event(device_t dev, uint32_t *event, + uint32_t *instance) +{ + uintptr_t mmio_base; + + mmio_base = ALIGN_DOWN(pci_read_config32(dev, PCI_BASE_ADDRESS_0), 16); + + /* USB2 ports */ + *instance = pch_xhci_port_wake_check(mmio_base + + XHCI_USB2_PORT_STATUS_REG, + XHCI_USB2_PORT_NUM); + + if (*instance) { + *event = ELOG_WAKE_SOURCE_PME_XHCI_USB_2; + return; + } + + /* USB3 ports */ + *instance = pch_xhci_port_wake_check(mmio_base + + XHCI_USB3_PORT_STATUS_REG, + XHCI_USB3_PORT_NUM); + + if (*instance) + *event = ELOG_WAKE_SOURCE_PME_XHCI_USB_3; +} + struct pme_status_info { int devfn; uint8_t reg_offset; @@ -43,6 +150,18 @@ };
#define PME_STS_BIT (1 << 15) + +static void pch_log_add_elog_event(const struct pme_status_info *info, + device_t dev) +{ + uint32_t instance = 0; + uint32_t event = info->elog_event; + + if (info->devfn == PCH_DEVFN_XHCI) + pch_xhci_update_wake_event(dev, &event, &instance); + + elog_add_event_wake(event, instance); +}
static void pch_log_pme_internal_wake_source(void) { @@ -87,7 +206,7 @@ if ((val == 0xFFFF) || !(val & PME_STS_BIT)) continue;
- elog_add_event_wake(pme_status_info[i].elog_event, 0); + pch_log_add_elog_event(&pme_status_info[i], dev); dev_found = true; }