aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2012-01-24 19:39:02 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-02-14 15:11:50 -0500
commitd93814cfadb131fb219359d4b7008c728421c552 (patch)
tree67e97af19569cebf85fa04384712aeb3e9bedf63 /drivers/usb/host/xhci-ring.c
parent50e5dfb6c4111c860bfa4d93dfe115bedf6b0fb1 (diff)
xHCI: Kick khubd when USB3 resume really completes.
xHCI roothubs go through slightly different port state machines when either a device initiates a remote wakeup and signals resume, or when the host initiates a resume. According to section 4.19.1.2.13 of the xHCI 1.0 spec, on host-initiated resume, the xHC port state machine automatically goes through the U3Exit state into the U0 state, setting the port link state change (PLC) bit in the process. When a device initiates resume, the xHCI port state machine goes into the "Resume" state and sets the PLC bit. Then the xHCI driver writes U0 into the port link state register to transition the port to U0 from the Resume state. We can't be sure the device is actually in the U0 state until we receive the next port status change event with the PLC bit set. We really don't want khubd to be polling the roothub port status bits until the device is really in U0. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Acked-by: Andiry Xu <andiry.xu@amd.com>
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r--drivers/usb/host/xhci-ring.c29
1 files changed, 17 insertions, 12 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index b62037bff688..ca38483c9f56 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1321,20 +1321,16 @@ static void handle_port_status(struct xhci_hcd *xhci,
1321 } 1321 }
1322 1322
1323 if (DEV_SUPERSPEED(temp)) { 1323 if (DEV_SUPERSPEED(temp)) {
1324 xhci_dbg(xhci, "resume SS port %d\n", port_id); 1324 xhci_dbg(xhci, "remote wake SS port %d\n", port_id);
1325 xhci_test_and_clear_bit(xhci, port_array,
1326 faked_port_index, PORT_PLC);
1325 xhci_set_link_state(xhci, port_array, faked_port_index, 1327 xhci_set_link_state(xhci, port_array, faked_port_index,
1326 XDEV_U0); 1328 XDEV_U0);
1327 slot_id = xhci_find_slot_id_by_port(hcd, xhci, 1329 /* Need to wait until the next link state change
1328 faked_port_index + 1); 1330 * indicates the device is actually in U0.
1329 if (!slot_id) { 1331 */
1330 xhci_dbg(xhci, "slot_id is zero\n"); 1332 bogus_port_status = true;
1331 goto cleanup; 1333 goto cleanup;
1332 }
1333 xhci_ring_device(xhci, slot_id);
1334 xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
1335 /* Clear PORT_PLC */
1336 xhci_test_and_clear_bit(xhci, port_array,
1337 faked_port_index, PORT_PLC);
1338 } else { 1334 } else {
1339 xhci_dbg(xhci, "resume HS port %d\n", port_id); 1335 xhci_dbg(xhci, "resume HS port %d\n", port_id);
1340 bus_state->resume_done[faked_port_index] = jiffies + 1336 bus_state->resume_done[faked_port_index] = jiffies +
@@ -1345,6 +1341,15 @@ static void handle_port_status(struct xhci_hcd *xhci,
1345 } 1341 }
1346 } 1342 }
1347 1343
1344 if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 &&
1345 DEV_SUPERSPEED(temp)) {
1346 xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
1347 slot_id = xhci_find_slot_id_by_port(hcd, xhci,
1348 faked_port_index + 1);
1349 if (slot_id && xhci->devs[slot_id])
1350 xhci_ring_device(xhci, slot_id);
1351 }
1352
1348 if (hcd->speed != HCD_USB3) 1353 if (hcd->speed != HCD_USB3)
1349 xhci_test_and_clear_bit(xhci, port_array, faked_port_index, 1354 xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
1350 PORT_PLC); 1355 PORT_PLC);