aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-04-27 06:07:50 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-05-02 19:42:53 -0400
commita7114230f6bd925f1c734d8ca1c32c93bf956aed (patch)
treec7e08619e6b5815fe5634120c3954ecbdb8c50e2 /drivers/usb/core
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/core')
-rw-r--r--drivers/usb/core/hub.c33
1 files changed, 20 insertions, 13 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)
2307 } 2307 }
2308 2308
2309 /* see 7.1.7.6 */ 2309 /* see 7.1.7.6 */
2310 /* Clear PORT_POWER if it's a USB3.0 device connected to USB 3.0 2310 if (hub_is_superspeed(hub->hdev))
2311 * external hub. 2311 status = set_port_feature(hub->hdev,
2312 * FIXME: this is a temporary workaround to make the system able 2312 port1 | (USB_SS_PORT_LS_U3 << 3),
2313 * to suspend/resume. 2313 USB_PORT_FEAT_LINK_STATE);
2314 */
2315 if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev))
2316 status = clear_port_feature(hub->hdev, port1,
2317 USB_PORT_FEAT_POWER);
2318 else 2314 else
2319 status = set_port_feature(hub->hdev, port1, 2315 status = set_port_feature(hub->hdev, port1,
2320 USB_PORT_FEAT_SUSPEND); 2316 USB_PORT_FEAT_SUSPEND);
@@ -2469,8 +2465,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
2469 set_bit(port1, hub->busy_bits); 2465 set_bit(port1, hub->busy_bits);
2470 2466
2471 /* see 7.1.7.7; affects power usage, but not budgeting */ 2467 /* see 7.1.7.7; affects power usage, but not budgeting */
2472 status = clear_port_feature(hub->hdev, 2468 if (hub_is_superspeed(hub->hdev))
2473 port1, USB_PORT_FEAT_SUSPEND); 2469 status = set_port_feature(hub->hdev,
2470 port1 | (USB_SS_PORT_LS_U0 << 3),
2471 USB_PORT_FEAT_LINK_STATE);
2472 else
2473 status = clear_port_feature(hub->hdev,
2474 port1, USB_PORT_FEAT_SUSPEND);
2474 if (status) { 2475 if (status) {
2475 dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", 2476 dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
2476 port1, status); 2477 port1, status);
@@ -2492,9 +2493,15 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
2492 2493
2493 SuspendCleared: 2494 SuspendCleared:
2494 if (status == 0) { 2495 if (status == 0) {
2495 if (portchange & USB_PORT_STAT_C_SUSPEND) 2496 if (hub_is_superspeed(hub->hdev)) {
2496 clear_port_feature(hub->hdev, port1, 2497 if (portchange & USB_PORT_STAT_C_LINK_STATE)
2497 USB_PORT_FEAT_C_SUSPEND); 2498 clear_port_feature(hub->hdev, port1,
2499 USB_PORT_FEAT_C_PORT_LINK_STATE);
2500 } else {
2501 if (portchange & USB_PORT_STAT_C_SUSPEND)
2502 clear_port_feature(hub->hdev, port1,
2503 USB_PORT_FEAT_C_SUSPEND);
2504 }
2498 } 2505 }
2499 2506
2500 clear_bit(port1, hub->busy_bits); 2507 clear_bit(port1, hub->busy_bits);