diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 64 |
1 files changed, 52 insertions, 12 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 5d2d7e954bd4..b30b4ce294d3 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -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,19 +797,35 @@ 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; } } - if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 - && (raw_port_status & PORT_POWER) - && (bus_state->suspended_ports & (1 << wIndex))) { - bus_state->suspended_ports &= ~(1 << wIndex); - if (hcd->speed < HCD_USB3) - bus_state->port_c_suspend |= 1 << wIndex; + /* + * 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)) { + bus_state->suspended_ports &= ~(1 << wIndex); + if (hcd->speed < HCD_USB3) + bus_state->port_c_suspend |= 1 << wIndex; + } + bus_state->resume_done[wIndex] = 0; + clear_bit(wIndex, &bus_state->resuming_ports); } if (raw_port_status & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; @@ -817,7 +855,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, xhci_hub_report_usb2_link_state(&status, raw_port_status); } if (bus_state->port_c_suspend & (1 << wIndex)) - status |= 1 << USB_PORT_FEAT_C_SUSPEND; + status |= USB_PORT_STAT_C_SUSPEND << 16; return status; } @@ -1112,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); @@ -1119,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; |