summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci-hub.c44
-rw-r--r--drivers/usb/host/xhci-mem.c1
-rw-r--r--drivers/usb/host/xhci-ring.c1
-rw-r--r--drivers/usb/host/xhci.h1
4 files changed, 34 insertions, 13 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index dde46d1d5f6f..af99d2509f16 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1308,7 +1308,33 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
wIndex, link_state);
goto error;
}
+
+ if (link_state == USB_SS_PORT_LS_U0) {
+ if ((temp & PORT_PLS_MASK) == XDEV_U0)
+ break;
+
+ if (!((temp & PORT_PLS_MASK) == XDEV_U1 ||
+ (temp & PORT_PLS_MASK) == XDEV_U2 ||
+ (temp & PORT_PLS_MASK) == XDEV_U3)) {
+ xhci_warn(xhci, "Can only set port %d to U0 from U state\n",
+ wIndex);
+ goto error;
+ }
+ reinit_completion(&bus_state->u3exit_done[wIndex]);
+ xhci_set_link_state(xhci, ports[wIndex],
+ USB_SS_PORT_LS_U0);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex],
+ msecs_to_jiffies(100)))
+ xhci_dbg(xhci, "missing U0 port change event for port %d\n",
+ wIndex);
+ spin_lock_irqsave(&xhci->lock, flags);
+ temp = readl(ports[wIndex]->addr);
+ break;
+ }
+
if (link_state == USB_SS_PORT_LS_U3) {
+ int retries = 16;
slot_id = xhci_find_slot_id_by_port(hcd, xhci,
wIndex + 1);
if (slot_id) {
@@ -1319,26 +1345,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_stop_device(xhci, slot_id, 1);
spin_lock_irqsave(&xhci->lock, flags);
}
- }
-
- xhci_set_link_state(xhci, ports[wIndex], link_state);
-
- spin_unlock_irqrestore(&xhci->lock, flags);
- if (link_state == USB_SS_PORT_LS_U3) {
- int retries = 16;
-
+ xhci_set_link_state(xhci, ports[wIndex], USB_SS_PORT_LS_U3);
+ spin_unlock_irqrestore(&xhci->lock, flags);
while (retries--) {
usleep_range(4000, 8000);
temp = readl(ports[wIndex]->addr);
if ((temp & PORT_PLS_MASK) == XDEV_U3)
break;
}
- }
- spin_lock_irqsave(&xhci->lock, flags);
-
- temp = readl(ports[wIndex]->addr);
- if (link_state == USB_SS_PORT_LS_U3)
+ spin_lock_irqsave(&xhci->lock, flags);
+ temp = readl(ports[wIndex]->addr);
bus_state->suspended_ports |= 1 << wIndex;
+ }
break;
case USB_PORT_FEAT_POWER:
/*
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 884c601bfa15..9764122c9cdf 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -2552,6 +2552,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci->usb3_rhub.bus_state.resume_done[i] = 0;
/* Only the USB 2.0 completions will ever be used. */
init_completion(&xhci->usb2_rhub.bus_state.rexit_done[i]);
+ init_completion(&xhci->usb3_rhub.bus_state.u3exit_done[i]);
}
if (scratchpad_alloc(xhci, flags))
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index ba512b25901a..a78787bb5133 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1681,6 +1681,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
(portsc & PORT_PLS_MASK) == XDEV_U1 ||
(portsc & PORT_PLS_MASK) == XDEV_U2)) {
xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
+ complete(&bus_state->u3exit_done[hcd_portnum]);
/* We've just brought the device into U0/1/2 through either the
* Resume state after a device remote wakeup, or through the
* U3Exit state after a host-initiated resume. If it's a device
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 3ff199c9aea6..3289bb516201 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1694,6 +1694,7 @@ struct xhci_bus_state {
/* Which ports are waiting on RExit to U0 transition. */
unsigned long rexit_ports;
struct completion rexit_done[USB_MAXCHILDREN];
+ struct completion u3exit_done[USB_MAXCHILDREN];
};