aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2013-08-20 11:12:12 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2013-09-23 18:43:31 -0400
commit8b3d45705e54075cfb9d4212dbca9ea82c85c4b8 (patch)
tree362b9118807505fec75e65761fff4be2344682d0 /drivers/usb/host/xhci-ring.c
parentec7e43e2d98173483866fe2e4e690143626b659c (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.c13
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);