aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-04-27 18:07:50 +0800
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-05-02 16:42:53 -0700
commita7114230f6bd925f1c734d8ca1c32c93bf956aed (patch)
treec7e08619e6b5815fe5634120c3954ecbdb8c50e2 /drivers/usb
parent0ed9a57e052a3d20df052a2ff12a3b42380867aa (diff)
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/usb')
-rw-r--r--drivers/usb/core/hub.c33
-rw-r--r--drivers/usb/host/xhci-hub.c49
2 files changed, 41 insertions, 41 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index dcd78c15f8cd..93035d862c63 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 4a3ca99fc64e..e3ddc6a95afe 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. */