diff options
| author | Andiry Xu <andiry.xu@amd.com> | 2011-04-27 18:07:50 +0800 | 
|---|---|---|
| committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-05-02 16:42:53 -0700 | 
| commit | a7114230f6bd925f1c734d8ca1c32c93bf956aed (patch) | |
| tree | c7e08619e6b5815fe5634120c3954ecbdb8c50e2 /drivers | |
| parent | 0ed9a57e052a3d20df052a2ff12a3b42380867aa (diff) | |
| download | olio-linux-3.10-a7114230f6bd925f1c734d8ca1c32c93bf956aed.tar.xz olio-linux-3.10-a7114230f6bd925f1c734d8ca1c32c93bf956aed.zip  | |
usbcore: Refine USB3.0 device suspend and resume
In the past, we use USB2.0 request to suspend and resume a USB3.0 device.
Actually, USB3.0 hub does not support Set/Clear PORT_SUSPEND request,
instead, it uses Set PORT_LINK_STATE request. This patch makes USB3.0 device
suspend/resume comply with USB3.0 specification.
This patch fixes the issue that USB3.0 device can not be suspended when
connected to a USB3.0 external hub.
Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/usb/core/hub.c | 33 | ||||
| -rw-r--r-- | drivers/usb/host/xhci-hub.c | 49 | 
2 files changed, 41 insertions, 41 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index dcd78c15f8c..93035d862c6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2307,14 +2307,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)  	}  	/* see 7.1.7.6 */ -	/* Clear PORT_POWER if it's a USB3.0 device connected to USB 3.0 -	 * external hub. -	 * FIXME: this is a temporary workaround to make the system able -	 * to suspend/resume. -	 */ -	if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev)) -		status = clear_port_feature(hub->hdev, port1, -						USB_PORT_FEAT_POWER); +	if (hub_is_superspeed(hub->hdev)) +		status = set_port_feature(hub->hdev, +				port1 | (USB_SS_PORT_LS_U3 << 3), +				USB_PORT_FEAT_LINK_STATE);  	else  		status = set_port_feature(hub->hdev, port1,  						USB_PORT_FEAT_SUSPEND); @@ -2469,8 +2465,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)  	set_bit(port1, hub->busy_bits);  	/* see 7.1.7.7; affects power usage, but not budgeting */ -	status = clear_port_feature(hub->hdev, -			port1, USB_PORT_FEAT_SUSPEND); +	if (hub_is_superspeed(hub->hdev)) +		status = set_port_feature(hub->hdev, +				port1 | (USB_SS_PORT_LS_U0 << 3), +				USB_PORT_FEAT_LINK_STATE); +	else +		status = clear_port_feature(hub->hdev, +				port1, USB_PORT_FEAT_SUSPEND);  	if (status) {  		dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",  				port1, status); @@ -2492,9 +2493,15 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)   SuspendCleared:  	if (status == 0) { -		if (portchange & USB_PORT_STAT_C_SUSPEND) -			clear_port_feature(hub->hdev, port1, -					USB_PORT_FEAT_C_SUSPEND); +		if (hub_is_superspeed(hub->hdev)) { +			if (portchange & USB_PORT_STAT_C_LINK_STATE) +				clear_port_feature(hub->hdev, port1, +					USB_PORT_FEAT_C_PORT_LINK_STATE); +		} else { +			if (portchange & USB_PORT_STAT_C_SUSPEND) +				clear_port_feature(hub->hdev, port1, +						USB_PORT_FEAT_C_SUSPEND); +		}  	}  	clear_bit(port1, hub->busy_bits); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 4a3ca99fc64..e3ddc6a95af 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -483,7 +483,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,  			&& (temp & PORT_POWER)  			&& (bus_state->suspended_ports & (1 << wIndex))) {  			bus_state->suspended_ports &= ~(1 << wIndex); -			bus_state->port_c_suspend |= 1 << wIndex; +			if (hcd->speed != HCD_USB3) +				bus_state->port_c_suspend |= 1 << wIndex;  		}  		if (temp & PORT_CONNECT) {  			status |= USB_PORT_STAT_CONNECTION; @@ -656,35 +657,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,  			if (temp & XDEV_U3) {  				if ((temp & PORT_PE) == 0)  					goto error; -				if (DEV_SUPERSPEED(temp)) { -					temp = xhci_port_state_to_neutral(temp); -					temp &= ~PORT_PLS_MASK; -					temp |= PORT_LINK_STROBE | XDEV_U0; -					xhci_writel(xhci, temp, -							port_array[wIndex]); -					xhci_readl(xhci, port_array[wIndex]); -				} else { -					temp = xhci_port_state_to_neutral(temp); -					temp &= ~PORT_PLS_MASK; -					temp |= PORT_LINK_STROBE | XDEV_RESUME; -					xhci_writel(xhci, temp, -							port_array[wIndex]); -					spin_unlock_irqrestore(&xhci->lock, -							       flags); -					msleep(20); -					spin_lock_irqsave(&xhci->lock, flags); +				temp = xhci_port_state_to_neutral(temp); +				temp &= ~PORT_PLS_MASK; +				temp |= PORT_LINK_STROBE | XDEV_RESUME; +				xhci_writel(xhci, temp, +						port_array[wIndex]); -					temp = xhci_readl(xhci, -							port_array[wIndex]); -					temp = xhci_port_state_to_neutral(temp); -					temp &= ~PORT_PLS_MASK; -					temp |= PORT_LINK_STROBE | XDEV_U0; -					xhci_writel(xhci, temp, -							port_array[wIndex]); -				} -				bus_state->port_c_suspend |= 1 << wIndex; +				spin_unlock_irqrestore(&xhci->lock, +						       flags); +				msleep(20); +				spin_lock_irqsave(&xhci->lock, flags); + +				temp = xhci_readl(xhci, +						port_array[wIndex]); +				temp = xhci_port_state_to_neutral(temp); +				temp &= ~PORT_PLS_MASK; +				temp |= PORT_LINK_STROBE | XDEV_U0; +				xhci_writel(xhci, temp, +						port_array[wIndex]);  			} +			bus_state->port_c_suspend |= 1 << wIndex;  			slot_id = xhci_find_slot_id_by_port(hcd, xhci,  					wIndex + 1); @@ -755,7 +748,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)  	memset(buf, 0, retval);  	status = 0; -	mask = PORT_CSC | PORT_PEC | PORT_OCC; +	mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC;  	spin_lock_irqsave(&xhci->lock, flags);  	/* For each port, did anything change?  If so, set that bit in buf. */  |