From a7114230f6bd925f1c734d8ca1c32c93bf956aed Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Wed, 27 Apr 2011 18:07:50 +0800 Subject: 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 Signed-off-by: Sarah Sharp --- drivers/usb/core/hub.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'drivers/usb/core') 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); -- cgit v1.2.1