diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
| -rw-r--r-- | drivers/usb/host/xhci-hub.c | 41 | 
1 files changed, 31 insertions, 10 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 673ad120c43..2732ef660c5 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -475,6 +475,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,  	struct xhci_bus_state *bus_state;  	u16 link_state = 0;  	u16 wake_mask = 0; +	u16 timeout = 0;  	max_ports = xhci_get_ports(hcd, &port_array);  	bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -558,6 +559,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,  				xhci_dbg(xhci, "Resume USB2 port %d\n",  					wIndex + 1);  				bus_state->resume_done[wIndex] = 0; +				clear_bit(wIndex, &bus_state->resuming_ports);  				xhci_set_link_state(xhci, port_array, wIndex,  							XDEV_U0);  				xhci_dbg(xhci, "set port %d resume\n", @@ -622,6 +624,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,  			link_state = (wIndex & 0xff00) >> 3;  		if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)  			wake_mask = wIndex & 0xff00; +		/* The MSB of wIndex is the U1/U2 timeout */ +		timeout = (wIndex & 0xff00) >> 8;  		wIndex &= 0xff;  		if (!wIndex || wIndex > max_ports)  			goto error; @@ -746,6 +750,22 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,  			temp = xhci_readl(xhci, port_array[wIndex]);  			break; +		case USB_PORT_FEAT_U1_TIMEOUT: +			if (hcd->speed != HCD_USB3) +				goto error; +			temp = xhci_readl(xhci, port_array[wIndex] + 1); +			temp &= ~PORT_U1_TIMEOUT_MASK; +			temp |= PORT_U1_TIMEOUT(timeout); +			xhci_writel(xhci, temp, port_array[wIndex] + 1); +			break; +		case USB_PORT_FEAT_U2_TIMEOUT: +			if (hcd->speed != HCD_USB3) +				goto error; +			temp = xhci_readl(xhci, port_array[wIndex] + 1); +			temp &= ~PORT_U2_TIMEOUT_MASK; +			temp |= PORT_U2_TIMEOUT(timeout); +			xhci_writel(xhci, temp, port_array[wIndex] + 1); +			break;  		default:  			goto error;  		} @@ -845,7 +865,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)  	/* Initial status is no changes */  	retval = (max_ports + 8) / 8;  	memset(buf, 0, retval); -	status = 0; + +	/* +	 * Inform the usbcore about resume-in-progress by returning +	 * a non-zero value even if there are no status changes. +	 */ +	status = bus_state->resuming_ports;  	mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC; @@ -885,15 +910,11 @@ int xhci_bus_suspend(struct usb_hcd *hcd)  	spin_lock_irqsave(&xhci->lock, flags);  	if (hcd->self.root_hub->do_remote_wakeup) { -		port_index = max_ports; -		while (port_index--) { -			if (bus_state->resume_done[port_index] != 0) { -				spin_unlock_irqrestore(&xhci->lock, flags); -				xhci_dbg(xhci, "suspend failed because " -						"port %d is resuming\n", -						port_index + 1); -				return -EBUSY; -			} +		if (bus_state->resuming_ports) { +			spin_unlock_irqrestore(&xhci->lock, flags); +			xhci_dbg(xhci, "suspend failed because " +						"a port is resuming\n"); +			return -EBUSY;  		}  	}  |