From 88c873a07ecff2c6f5ec04d178251315cf01e06a Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Mon, 16 Sep 2013 13:51:08 -0700 Subject: intel/lynxpoint: xhci: Port reset changes on suspend/resume Some USB3 devices are not showing up after suspend/resume cycles. In particular if a device uses a lower power state like U2 it may take longer to come up and the firmware needs to wait after sending a warm port reset. In addition skipping port reset to connected ports in the way into suspend was causing problems so instead send all ports a reset before suspend. BUG=chrome-os-partner:22402 BRANCH=falco,peppy,leon,wolf TEST=manual: Suspend/resume with ADATA HE720 HDD (and other devices) both connected at suspend and connecting while in suspend and ensure that the devices always show up in the kernel. Change-Id: Ib7b15dc65792742b4ceb7dcfc4b2c83192eafcc2 Signed-off-by: Duncan Laurie Reviewed-on: https://chromium-review.googlesource.com/169548 Reviewed-by: Aaron Durbin Reviewed-on: http://review.coreboot.org/6015 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi --- src/southbridge/intel/lynxpoint/usb_xhci.c | 63 ++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 17 deletions(-) (limited to 'src/southbridge/intel') diff --git a/src/southbridge/intel/lynxpoint/usb_xhci.c b/src/southbridge/intel/lynxpoint/usb_xhci.c index 0bffd69af1..f6409b4381 100644 --- a/src/southbridge/intel/lynxpoint/usb_xhci.c +++ b/src/southbridge/intel/lynxpoint/usb_xhci.c @@ -75,11 +75,11 @@ static void usb_xhci_reset_port_usb3(u32 mem_base, int port) write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR); } -#ifdef __SMM__ - #define XHCI_RESET_DELAY_US 1000 /* 1ms */ #define XHCI_RESET_TIMEOUT 100 /* 100ms */ +#ifdef __SMM__ + /* * 1) Wait until port is done polling * 2) If port is disconnected @@ -87,7 +87,7 @@ static void usb_xhci_reset_port_usb3(u32 mem_base, int port) * b) Poll for warm reset complete * c) Write 1 to port change status bits */ -static void usb_xhci_reset_usb3(device_t dev, int all) +static void usb_xhci_reset_usb3(device_t dev) { u32 status, port_disabled; int timeout, port; @@ -127,10 +127,7 @@ static void usb_xhci_reset_usb3(device_t dev, int all) continue; status = read32(portsc) & XHCI_USB3_PORTSC_PLS; /* Reset all or only disconnected ports */ - if (all || status == XHCI_PLSR_RXDETECT) - usb_xhci_reset_port_usb3(mem_base, port); - else - port_disabled |= 1 << port; /* No reset */ + usb_xhci_reset_port_usb3(mem_base, port); } /* Wait for warm reset complete on all reset ports */ @@ -184,7 +181,7 @@ void usb_xhci_sleep_prepare(device_t dev, u8 slp_typ) write32(mem_base + 0x816c, reg32); /* Reset disconnected USB3 ports */ - usb_xhci_reset_usb3(dev, 0); + usb_xhci_reset_usb3(dev); /* Set MMIO 0x80e0[15] */ reg32 = read32(mem_base + 0x80e0); @@ -236,7 +233,7 @@ void usb_xhci_route_all(void) usb_ehci_disable(PCH_EHCI2_DEV); /* Reset and clear port change status */ - usb_xhci_reset_usb3(PCH_XHCI_DEV, 1); + usb_xhci_reset_usb3(PCH_XHCI_DEV); } #else /* !__SMM__ */ @@ -296,6 +293,8 @@ static void usb_xhci_enable_ports_usb3(device_t dev) u32 portsc, status, disabled; u32 mem_base = usb_xhci_mem_base(dev); int port_count = usb_xhci_port_count_usb3(dev); + u8 port_reset = 0; + int timeout; if (!mem_base || !port_count) return; @@ -309,25 +308,55 @@ static void usb_xhci_enable_ports_usb3(device_t dev) continue; portsc = mem_base + XHCI_USB3_PORTSC(port); status = read32(portsc) & XHCI_USB3_PORTSC_PLS; - switch (status) { case XHCI_PLSR_RXDETECT: /* Clear change status */ - printk(BIOS_DEBUG, "usb_xhci reset port %d\n", port); + printk(BIOS_DEBUG, "usb_xhci reset status %d\n", port); usb_xhci_reset_status_usb3(mem_base, port); break; case XHCI_PLSR_DISABLED: default: - /* Transition to enabled */ - printk(BIOS_DEBUG, "usb_xhci enable port %d\n", port); + /* Reset port */ + printk(BIOS_DEBUG, "usb_xhci reset port %d\n", port); usb_xhci_reset_port_usb3(mem_base, port); - status = read32(portsc); - status &= ~XHCI_USB3_PORTSC_PLS; - status |= XHCI_PLSW_ENABLE | XHCI_USB3_PORTSC_LWS; - write32(portsc, status); + port_reset |= 1 << port; break; } } + + if (!port_reset) + return; + + /* Wait for warm reset complete on all reset ports */ + for (timeout = XHCI_RESET_TIMEOUT; timeout; timeout--) { + int complete = 1; + for (port = 0; port < port_count; port++) { + /* Only check ports that were reset */ + if (!(port_reset & (1 << port))) + continue; + /* Check if warm reset is complete */ + status = read32(mem_base + XHCI_USB3_PORTSC(port)); + if (!(status & XHCI_USB3_PORTSC_WRC)) + complete = 0; + } + /* Check for warm reset complete in any port */ + if (complete) + break; + udelay(XHCI_RESET_DELAY_US); + } + + /* Enable ports that were reset */ + for (port = 0; port < port_count; port++) { + /* Only check ports that were reset */ + if (!(port_reset & (1 << port))) + continue; + /* Transition to enabled */ + portsc = mem_base + XHCI_USB3_PORTSC(port); + status = read32(portsc); + status &= ~(XHCI_USB3_PORTSC_PLS | XHCI_USB3_PORTSC_PED); + status |= XHCI_PLSW_ENABLE | XHCI_USB3_PORTSC_LWS; + write32(portsc, status); + } #endif } -- cgit v1.2.3