From d93814cfadb131fb219359d4b7008c728421c552 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 24 Jan 2012 16:39:02 -0800 Subject: [PATCH] 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 Acked-by: Andiry Xu --- drivers/usb/host/xhci-ring.c | 29 +++++++++++++++++------------ 1 file 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, } if (DEV_SUPERSPEED(temp)) { - xhci_dbg(xhci, "resume SS port %d\n", port_id); + xhci_dbg(xhci, "remote wake SS port %d\n", port_id); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); xhci_set_link_state(xhci, port_array, faked_port_index, XDEV_U0); - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - faked_port_index + 1); - if (!slot_id) { - xhci_dbg(xhci, "slot_id is zero\n"); - goto cleanup; - } - xhci_ring_device(xhci, slot_id); - xhci_dbg(xhci, "resume SS port %d finished\n", port_id); - /* Clear PORT_PLC */ - xhci_test_and_clear_bit(xhci, port_array, - faked_port_index, PORT_PLC); + /* Need to wait until the next link state change + * indicates the device is actually in U0. + */ + bogus_port_status = true; + goto cleanup; } else { xhci_dbg(xhci, "resume HS port %d\n", port_id); bus_state->resume_done[faked_port_index] = jiffies + @@ -1345,6 +1341,15 @@ static void handle_port_status(struct xhci_hcd *xhci, } } + if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 && + DEV_SUPERSPEED(temp)) { + xhci_dbg(xhci, "resume SS port %d finished\n", port_id); + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + faked_port_index + 1); + if (slot_id && xhci->devs[slot_id]) + xhci_ring_device(xhci, slot_id); + } + if (hcd->speed != HCD_USB3) xhci_test_and_clear_bit(xhci, port_array, faked_port_index, PORT_PLC); -- 2.30.2