diff options
author | Andiry Xu <andiry.xu@amd.com> | 2011-04-27 06:07:50 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-05-02 19:42:53 -0400 |
commit | a7114230f6bd925f1c734d8ca1c32c93bf956aed (patch) | |
tree | c7e08619e6b5815fe5634120c3954ecbdb8c50e2 /drivers/usb/core | |
parent | 0ed9a57e052a3d20df052a2ff12a3b42380867aa (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.c | 33 |
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); |