|
@@ -733,8 +733,30 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
|
|
if ((raw_port_status & PORT_RESET) ||
|
|
|
!(raw_port_status & PORT_PE))
|
|
|
return 0xffffffff;
|
|
|
- if (time_after_eq(jiffies,
|
|
|
- bus_state->resume_done[wIndex])) {
|
|
|
+ /* did port event handler already start resume timing? */
|
|
|
+ if (!bus_state->resume_done[wIndex]) {
|
|
|
+ /* If not, maybe we are in a host initated resume? */
|
|
|
+ if (test_bit(wIndex, &bus_state->resuming_ports)) {
|
|
|
+ /* Host initated resume doesn't time the resume
|
|
|
+ * signalling using resume_done[].
|
|
|
+ * It manually sets RESUME state, sleeps 20ms
|
|
|
+ * and sets U0 state. This should probably be
|
|
|
+ * changed, but not right now.
|
|
|
+ */
|
|
|
+ } else {
|
|
|
+ /* port resume was discovered now and here,
|
|
|
+ * start resume timing
|
|
|
+ */
|
|
|
+ unsigned long timeout = jiffies +
|
|
|
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
|
|
|
+
|
|
|
+ set_bit(wIndex, &bus_state->resuming_ports);
|
|
|
+ bus_state->resume_done[wIndex] = timeout;
|
|
|
+ mod_timer(&hcd->rh_timer, timeout);
|
|
|
+ }
|
|
|
+ /* Has resume been signalled for USB_RESUME_TIME yet? */
|
|
|
+ } else if (time_after_eq(jiffies,
|
|
|
+ bus_state->resume_done[wIndex])) {
|
|
|
int time_left;
|
|
|
|
|
|
xhci_dbg(xhci, "Resume USB2 port %d\n",
|
|
@@ -775,13 +797,26 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,
|
|
|
} else {
|
|
|
/*
|
|
|
* The resume has been signaling for less than
|
|
|
- * 20ms. Report the port status as SUSPEND,
|
|
|
- * let the usbcore check port status again
|
|
|
- * and clear resume signaling later.
|
|
|
+ * USB_RESUME_TIME. Report the port status as SUSPEND,
|
|
|
+ * let the usbcore check port status again and clear
|
|
|
+ * resume signaling later.
|
|
|
*/
|
|
|
status |= USB_PORT_STAT_SUSPEND;
|
|
|
}
|
|
|
}
|
|
|
+ /*
|
|
|
+ * Clear stale usb2 resume signalling variables in case port changed
|
|
|
+ * state during resume signalling. For example on error
|
|
|
+ */
|
|
|
+ if ((bus_state->resume_done[wIndex] ||
|
|
|
+ test_bit(wIndex, &bus_state->resuming_ports)) &&
|
|
|
+ (raw_port_status & PORT_PLS_MASK) != XDEV_U3 &&
|
|
|
+ (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) {
|
|
|
+ bus_state->resume_done[wIndex] = 0;
|
|
|
+ clear_bit(wIndex, &bus_state->resuming_ports);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 &&
|
|
|
(raw_port_status & PORT_POWER)) {
|
|
|
if (bus_state->suspended_ports & (1 << wIndex)) {
|
|
@@ -1115,6 +1150,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
if ((temp & PORT_PE) == 0)
|
|
|
goto error;
|
|
|
|
|
|
+ set_bit(wIndex, &bus_state->resuming_ports);
|
|
|
xhci_set_link_state(xhci, port_array, wIndex,
|
|
|
XDEV_RESUME);
|
|
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
|
@@ -1122,6 +1158,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|
|
spin_lock_irqsave(&xhci->lock, flags);
|
|
|
xhci_set_link_state(xhci, port_array, wIndex,
|
|
|
XDEV_U0);
|
|
|
+ clear_bit(wIndex, &bus_state->resuming_ports);
|
|
|
}
|
|
|
bus_state->port_c_suspend |= 1 << wIndex;
|
|
|
|