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>
This commit is contained in:
Duncan Laurie 2013-09-16 13:51:08 -07:00 committed by Patrick Georgi
parent 779e178353
commit 88c873a07e
1 changed files with 46 additions and 17 deletions

View File

@ -75,11 +75,11 @@ static void usb_xhci_reset_port_usb3(u32 mem_base, int port)
write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR); write32(portsc, read32(portsc) | XHCI_USB3_PORTSC_WPR);
} }
#ifdef __SMM__
#define XHCI_RESET_DELAY_US 1000 /* 1ms */ #define XHCI_RESET_DELAY_US 1000 /* 1ms */
#define XHCI_RESET_TIMEOUT 100 /* 100ms */ #define XHCI_RESET_TIMEOUT 100 /* 100ms */
#ifdef __SMM__
/* /*
* 1) Wait until port is done polling * 1) Wait until port is done polling
* 2) If port is disconnected * 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 * b) Poll for warm reset complete
* c) Write 1 to port change status bits * 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; u32 status, port_disabled;
int timeout, port; int timeout, port;
@ -127,10 +127,7 @@ static void usb_xhci_reset_usb3(device_t dev, int all)
continue; continue;
status = read32(portsc) & XHCI_USB3_PORTSC_PLS; status = read32(portsc) & XHCI_USB3_PORTSC_PLS;
/* Reset all or only disconnected ports */ /* Reset all or only disconnected ports */
if (all || status == XHCI_PLSR_RXDETECT)
usb_xhci_reset_port_usb3(mem_base, port); usb_xhci_reset_port_usb3(mem_base, port);
else
port_disabled |= 1 << port; /* No reset */
} }
/* Wait for warm reset complete on all reset ports */ /* 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); write32(mem_base + 0x816c, reg32);
/* Reset disconnected USB3 ports */ /* Reset disconnected USB3 ports */
usb_xhci_reset_usb3(dev, 0); usb_xhci_reset_usb3(dev);
/* Set MMIO 0x80e0[15] */ /* Set MMIO 0x80e0[15] */
reg32 = read32(mem_base + 0x80e0); reg32 = read32(mem_base + 0x80e0);
@ -236,7 +233,7 @@ void usb_xhci_route_all(void)
usb_ehci_disable(PCH_EHCI2_DEV); usb_ehci_disable(PCH_EHCI2_DEV);
/* Reset and clear port change status */ /* Reset and clear port change status */
usb_xhci_reset_usb3(PCH_XHCI_DEV, 1); usb_xhci_reset_usb3(PCH_XHCI_DEV);
} }
#else /* !__SMM__ */ #else /* !__SMM__ */
@ -296,6 +293,8 @@ static void usb_xhci_enable_ports_usb3(device_t dev)
u32 portsc, status, disabled; u32 portsc, status, disabled;
u32 mem_base = usb_xhci_mem_base(dev); u32 mem_base = usb_xhci_mem_base(dev);
int port_count = usb_xhci_port_count_usb3(dev); int port_count = usb_xhci_port_count_usb3(dev);
u8 port_reset = 0;
int timeout;
if (!mem_base || !port_count) if (!mem_base || !port_count)
return; return;
@ -309,25 +308,55 @@ static void usb_xhci_enable_ports_usb3(device_t dev)
continue; continue;
portsc = mem_base + XHCI_USB3_PORTSC(port); portsc = mem_base + XHCI_USB3_PORTSC(port);
status = read32(portsc) & XHCI_USB3_PORTSC_PLS; status = read32(portsc) & XHCI_USB3_PORTSC_PLS;
switch (status) { switch (status) {
case XHCI_PLSR_RXDETECT: case XHCI_PLSR_RXDETECT:
/* Clear change status */ /* 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); usb_xhci_reset_status_usb3(mem_base, port);
break; break;
case XHCI_PLSR_DISABLED: case XHCI_PLSR_DISABLED:
default: default:
/* Transition to enabled */ /* Reset port */
printk(BIOS_DEBUG, "usb_xhci enable port %d\n", port); printk(BIOS_DEBUG, "usb_xhci reset port %d\n", port);
usb_xhci_reset_port_usb3(mem_base, port); usb_xhci_reset_port_usb3(mem_base, port);
status = read32(portsc); port_reset |= 1 << port;
status &= ~XHCI_USB3_PORTSC_PLS;
status |= XHCI_PLSW_ENABLE | XHCI_USB3_PORTSC_LWS;
write32(portsc, status);
break; 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 #endif
} }