diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 660a7d0f79a4..d3d3dadaca10 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1071,6 +1071,7 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, struct xhci_bus_state *bus_state; u32 link_state; u32 portnum; + int ret; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -1086,30 +1087,23 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, if (link_state == XDEV_U2) *status |= USB_PORT_STAT_L1; if (link_state == XDEV_U0) { + if (port->resume_timestamp) + usb_hcd_end_port_resume(&port->rhub->hcd->self, + portnum); + port->resume_timestamp = 0; + clear_bit(portnum, &bus_state->resuming_ports); if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); bus_state->port_c_suspend |= 1 << portnum; } } if (link_state == XDEV_RESUME) { - xhci_handle_usb2_port_link_resume(port, status, portsc, - flags); + ret = xhci_handle_usb2_port_link_resume(port, status, + portsc, flags); + if (ret) + return; } } - - /* - * Clear usb2 resume signalling variables if port is no longer suspended - * or resuming. Port either resumed to U0/U1/U2, disconnected, or in a - * error state. Resume related variables should be cleared in all those cases. - */ - if ((link_state != XDEV_U3 && - link_state != XDEV_RESUME) && - (port->resume_timestamp || - test_bit(portnum, &bus_state->resuming_ports))) { - port->resume_timestamp = 0; - clear_bit(portnum, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&port->rhub->hcd->self, portnum); - } } /* @@ -1164,6 +1158,18 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, else xhci_get_usb2_port_status(port, &status, raw_port_status, flags); + /* + * Clear stale usb2 resume signalling variables in case port changed + * state during resume signalling. For example on error + */ + if ((port->resume_timestamp || + test_bit(wIndex, &bus_state->resuming_ports)) && + (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && + (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { + port->resume_timestamp = 0; + clear_bit(wIndex, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&hcd->self, wIndex); + } if (bus_state->port_c_suspend & (1 << wIndex)) status |= USB_PORT_STAT_C_SUSPEND << 16;