summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDuncan Laurie <dlaurie@chromium.org>2013-09-16 13:51:08 -0700
committerPatrick Georgi <patrick@georgi-clan.de>2014-07-05 10:12:07 +0200
commit88c873a07ecff2c6f5ec04d178251315cf01e06a (patch)
tree1ed8949af9ff3b00a483e2005482016ee23f32a0
parent779e178353a1adb6e6bee8fcad688bcbceb172cf (diff)
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 <dlaurie@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/169548 Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: http://review.coreboot.org/6015 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
-rw-r--r--src/southbridge/intel/lynxpoint/usb_xhci.c63
1 files changed, 46 insertions, 17 deletions
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
}