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:
parent
779e178353
commit
88c873a07e
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue