diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2013-08-20 11:12:12 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2013-09-23 18:43:31 -0400 |
commit | 8b3d45705e54075cfb9d4212dbca9ea82c85c4b8 (patch) | |
tree | 362b9118807505fec75e65761fff4be2344682d0 /drivers/usb/host/xhci-ring.c | |
parent | ec7e43e2d98173483866fe2e4e690143626b659c (diff) |
usb: Fix xHCI host issues on remote wakeup.
When a device signals remote wakeup on a roothub, and the suspend change
bit is set, the host controller driver must not give control back to the
USB core until the port goes back into the active state.
EHCI accomplishes this by waiting in the get port status function until
the PORT_RESUME bit is cleared:
/* stop resume signaling */
temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
ehci_writel(ehci, temp, status_reg);
clear_bit(wIndex, &ehci->resuming_ports);
retval = ehci_handshake(ehci, status_reg,
PORT_RESUME, 0, 2000 /* 2msec */);
Similarly, the xHCI host should wait until the port goes into U0, before
passing control up to the USB core. When the port transitions from the
RExit state to U0, the xHCI driver will get a port status change event.
We need to wait for that event before passing control up to the USB
core.
After the port transitions to the active state, the USB core should time
a recovery interval before it talks to the device. The length of that
recovery interval is TRSMRCY, 10 ms, mentioned in the USB 2.0 spec,
section 7.1.7.7. The previous xHCI code (which did not wait for the
port to go into U0) would cause the USB core to violate that recovery
interval.
This bug caused numerous USB device disconnects on remote wakeup under
ChromeOS and a Lynx Point LP xHCI host that takes up to 20 ms to move
from RExit to U0. ChromeOS is very aggressive about power savings, and
sets the autosuspend_delay to 100 ms, and disables USB persist.
I attempted to replicate this bug with Ubuntu 12.04, but could not. I
used Ubuntu 12.04 on the same platform, with the same BIOS that the bug
was triggered on ChromeOS with. I also changed the USB sysfs settings
as described above, but still could not reproduce the bug under Ubuntu.
It may be that ChromeOS userspace triggers this bug through additional
settings.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9ac9672d4498..dd02402700d5 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c | |||
@@ -1759,6 +1759,19 @@ static void handle_port_status(struct xhci_hcd *xhci, | |||
1759 | } | 1759 | } |
1760 | } | 1760 | } |
1761 | 1761 | ||
1762 | /* | ||
1763 | * Check to see if xhci-hub.c is waiting on RExit to U0 transition (or | ||
1764 | * RExit to a disconnect state). If so, let the the driver know it's | ||
1765 | * out of the RExit state. | ||
1766 | */ | ||
1767 | if (!DEV_SUPERSPEED(temp) && | ||
1768 | test_and_clear_bit(faked_port_index, | ||
1769 | &bus_state->rexit_ports)) { | ||
1770 | complete(&bus_state->rexit_done[faked_port_index]); | ||
1771 | bogus_port_status = true; | ||
1772 | goto cleanup; | ||
1773 | } | ||
1774 | |||
1762 | if (hcd->speed != HCD_USB3) | 1775 | if (hcd->speed != HCD_USB3) |
1763 | xhci_test_and_clear_bit(xhci, port_array, faked_port_index, | 1776 | xhci_test_and_clear_bit(xhci, port_array, faked_port_index, |
1764 | PORT_PLC); | 1777 | PORT_PLC); |