diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 56c600be272a..660a7d0f79a4 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -905,7 +905,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, } static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, - u32 portsc, + u32 *status, u32 portsc, unsigned long *flags) { struct xhci_bus_state *bus_state; @@ -920,6 +920,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex = port->hcd_portnum; if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) { + *status = 0xffffffff; return -EINVAL; } /* did port event handler already start resume timing? */ @@ -953,8 +954,6 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, port->resume_timestamp = 0; clear_bit(wIndex, &bus_state->resuming_ports); - - reinit_completion(&port->rexit_done); port->rexit_active = true; xhci_test_and_clear_bit(xhci, port, PORT_PLC); @@ -971,6 +970,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex + 1); if (!slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); + *status = 0xffffffff; return -ENODEV; } xhci_ring_device(xhci, slot_id); @@ -979,19 +979,22 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", hcd->self.busnum, wIndex + 1, port_status); - /* - * keep rexit_active set if U0 transition failed so we - * know to report PORT_STAT_SUSPEND status back to - * usbcore. It will be cleared later once the port is - * out of RESUME/U3 state - */ + *status |= USB_PORT_STAT_SUSPEND; + port->rexit_active = false; } usb_hcd_end_port_resume(&hcd->self, wIndex); bus_state->port_c_suspend |= 1 << wIndex; bus_state->suspended_ports &= ~(1 << wIndex); + } else { + /* + * The resume has been signaling for less than + * 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; } - return 0; } @@ -1068,7 +1071,6 @@ 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 err; bus_state = &port->rhub->bus_state; link_state = portsc & PORT_PLS_MASK; @@ -1090,12 +1092,8 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, } } if (link_state == XDEV_RESUME) { - err = xhci_handle_usb2_port_link_resume(port, portsc, - flags); - if (err < 0) - *status = 0xffffffff; - else if (port->resume_timestamp || port->rexit_active) - *status |= USB_PORT_STAT_SUSPEND; + xhci_handle_usb2_port_link_resume(port, status, portsc, + flags); } } @@ -1104,14 +1102,13 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, * 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) { - if (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); - } - port->rexit_active = 0; + 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); } }