From 316541bf70281b6c3b501b442b6994c1839776ad Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 11 Jul 2011 22:59:36 +0200 Subject: USB: Remove test for NULL that'll never happen in usb_disconnect() In drivers/usb/core/hub.c::usb_disconnect(), 'udev' will never be NULL, so remove the test and printing of debug message. Signed-off-by: Jesper Juhl Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a428aa080a36..99fff6be3641 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1636,11 +1636,6 @@ void usb_disconnect(struct usb_device **pdev) int i; struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!udev) { - pr_debug ("%s nodev\n", __func__); - return; - } - /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. * this quiesces everything except pending urbs. -- cgit v1.2.2 From 29cc88979a8818cd8c5019426e945aed118b400e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 23 Aug 2011 03:12:03 -0700 Subject: USB: use usb_endpoint_maxp() instead of le16_to_cpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now ${LINUX}/drivers/usb/* can use usb_endpoint_maxp(desc) to get maximum packet size instead of le16_to_cpu(desc->wMaxPacketSize). This patch fix it up Cc: Armin Fuerst Cc: Pavel Machek Cc: Johannes Erdfelt Cc: Vojtech Pavlik Cc: Oliver Neukum Cc: David Kubicek Cc: Johan Hovold Cc: Brad Hards Acked-by: Felipe Balbi Cc: Sebastian Andrzej Siewior Cc: Thomas Dahlmann Cc: David Brownell Cc: David Lopo Cc: Alan Stern Cc: Michal Nazarewicz Cc: Xie Xiaobo Cc: Li Yang Cc: Jiang Bo Cc: Yuan-hsin Chen Cc: Darius Augulis Cc: Xiaochen Shen Cc: Yoshihiro Shimoda Cc: OKI SEMICONDUCTOR, Cc: Robert Jarzmik Cc: Ben Dooks Cc: Thomas Abraham Cc: Herbert Pötzl Cc: Arnaud Patard Cc: Roman Weissgaerber Acked-by: Sarah Sharp Cc: Tony Olech Cc: Florian Floe Echtler Cc: Christian Lucht Cc: Juergen Stuber Cc: Georges Toth Cc: Bill Ryder Cc: Kuba Ober Cc: Inaky Perez-Gonzalez Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 99fff6be3641..338f91ff54cb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3018,7 +3018,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, i = 512; else i = udev->descriptor.bMaxPacketSize0; - if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { + if (usb_endpoint_maxp(&udev->ep0.desc) != i) { if (udev->speed == USB_SPEED_LOW || !(i == 8 || i == 16 || i == 32 || i == 64)) { dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i); -- cgit v1.2.2 From e538dfdae85244fd2c4231725d82cc1f1bc4942c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 30 Aug 2011 17:11:19 +0200 Subject: usb: Provide usb_speed_string() function In a few places in the kernel, the code prints a human-readable USB device speed (eg. "high speed"). This involves a switch statement sometimes wrapped around in ({ ... }) block leading to code repetition. To mitigate this issue, this commit introduces usb_speed_string() function, which returns a human-readable name of provided speed. It also changes a few places switch was used to use this new function. This changes a bit the way the speed is printed in few instances at the same time standardising it. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 338f91ff54cb..3edc01bc41f7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2793,7 +2793,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; - char *speed, *type; + const char *speed; int devnum = udev->devnum; /* root hub ports have a slightly longer reset period @@ -2853,25 +2853,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, default: goto fail; } - - type = ""; - switch (udev->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - case USB_SPEED_SUPER: - speed = "super"; - break; - case USB_SPEED_WIRELESS: - speed = "variable"; - type = "Wireless "; - break; - default: speed = "?"; break; - } + + if (udev->speed == USB_SPEED_WIRELESS) + speed = "variable speed Wireless"; + else + speed = usb_speed_string(udev->speed); + if (udev->speed != USB_SPEED_SUPER) dev_info(&udev->dev, - "%s %s speed %sUSB device number %d using %s\n", - (udev->config) ? "reset" : "new", speed, type, + "%s %s USB device number %d using %s\n", + (udev->config) ? "reset" : "new", speed, devnum, udev->bus->controller->driver->name); /* Set up TT records, if needed */ -- cgit v1.2.2 From 75d7cf72ab9fa01dc70877aa5c68e8ef477229dc Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Tue, 13 Sep 2011 16:41:11 -0700 Subject: usbcore: refine warm reset logic Current waiting time for warm(BH) reset in hub_port_warm_reset() is too short for xHC host to complete the warm reset and report a BH reset change. This patch increases the waiting time for warm reset and merges the function into hub_port_reset(), so it can handle both cold reset and warm reset, and factor out hub_port_finish_reset() to make the code looks cleaner. This fixes the issue that driver fails to clear BH reset change and port is "dead". Signed-off-by: Andiry Xu Acked-by: Alan Stern Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 216 +++++++++++++++++++++++++------------------------ 1 file changed, 112 insertions(+), 104 deletions(-) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3edc01bc41f7..aa336f736f0c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2025,11 +2025,12 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 +#define HUB_BH_RESET_TIME 50 #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 static int hub_port_wait_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int delay_time, ret; u16 portstatus; @@ -2046,28 +2047,39 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (ret < 0) return ret; - /* Device went away? */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return -ENOTCONN; - - /* bomb out completely if the connection bounced */ - if ((portchange & USB_PORT_STAT_C_CONNECTION)) - return -ENOTCONN; - - /* if we`ve finished resetting, then break out of the loop */ - if (!(portstatus & USB_PORT_STAT_RESET) && - (portstatus & USB_PORT_STAT_ENABLE)) { - if (hub_is_wusb(hub)) - udev->speed = USB_SPEED_WIRELESS; - else if (hub_is_superspeed(hub->hdev)) - udev->speed = USB_SPEED_SUPER; - else if (portstatus & USB_PORT_STAT_HIGH_SPEED) - udev->speed = USB_SPEED_HIGH; - else if (portstatus & USB_PORT_STAT_LOW_SPEED) - udev->speed = USB_SPEED_LOW; - else - udev->speed = USB_SPEED_FULL; - return 0; + /* + * Some buggy devices require a warm reset to be issued even + * when the port appears not to be connected. + */ + if (!warm) { + /* Device went away? */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return -ENOTCONN; + + /* bomb out completely if the connection bounced */ + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + return -ENOTCONN; + + /* if we`ve finished resetting, then break out of + * the loop + */ + if (!(portstatus & USB_PORT_STAT_RESET) && + (portstatus & USB_PORT_STAT_ENABLE)) { + if (hub_is_wusb(hub)) + udev->speed = USB_SPEED_WIRELESS; + else if (hub_is_superspeed(hub->hdev)) + udev->speed = USB_SPEED_SUPER; + else if (portstatus & USB_PORT_STAT_HIGH_SPEED) + udev->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + udev->speed = USB_SPEED_LOW; + else + udev->speed = USB_SPEED_FULL; + return 0; + } + } else { + if (portchange & USB_PORT_STAT_C_BH_RESET) + return 0; } /* switch to the long delay after two short delay failures */ @@ -2075,35 +2087,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, delay = HUB_LONG_RESET_TIME; dev_dbg (hub->intfdev, - "port %d not reset yet, waiting %dms\n", - port1, delay); + "port %d not %sreset yet, waiting %dms\n", + port1, warm ? "warm " : "", delay); } return -EBUSY; } +static void hub_port_finish_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, int *status, bool warm) +{ + switch (*status) { + case 0: + if (!warm) { + struct usb_hcd *hcd; + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); + update_devnum(udev, 0); + hcd = bus_to_hcd(udev->bus); + if (hcd->driver->reset_device) { + *status = hcd->driver->reset_device(hcd, udev); + if (*status < 0) { + dev_err(&udev->dev, "Cannot reset " + "HCD device state\n"); + break; + } + } + } + /* FALL THROUGH */ + case -ENOTCONN: + case -ENODEV: + clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_C_RESET); + /* FIXME need disconnect() for NOTATTACHED device */ + if (warm) { + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_BH_PORT_RESET); + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } else { + usb_set_device_state(udev, *status + ? USB_STATE_NOTATTACHED + : USB_STATE_DEFAULT); + } + break; + } +} + +/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ static int hub_port_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int i, status; - struct usb_hcd *hcd; - hcd = bus_to_hcd(udev->bus); - /* Block EHCI CF initialization during the port reset. - * Some companion controllers don't like it when they mix. - */ - down_read(&ehci_cf_port_reset_rwsem); + if (!warm) { + /* Block EHCI CF initialization during the port reset. + * Some companion controllers don't like it when they mix. + */ + down_read(&ehci_cf_port_reset_rwsem); + } else { + if (!hub_is_superspeed(hub->hdev)) { + dev_err(hub->intfdev, "only USB3 hub support " + "warm reset\n"); + return -EINVAL; + } + } /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { - status = set_port_feature(hub->hdev, - port1, USB_PORT_FEAT_RESET); - if (status) + status = set_port_feature(hub->hdev, port1, (warm ? + USB_PORT_FEAT_BH_PORT_RESET : + USB_PORT_FEAT_RESET)); + if (status) { dev_err(hub->intfdev, - "cannot reset port %d (err = %d)\n", - port1, status); - else { - status = hub_port_wait_reset(hub, port1, udev, delay); + "cannot %sreset port %d (err = %d)\n", + warm ? "warm " : "", port1, status); + } else { + status = hub_port_wait_reset(hub, port1, udev, delay, + warm); if (status && status != -ENOTCONN) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", @@ -2111,34 +2172,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1, } /* return on disconnect or reset */ - switch (status) { - case 0: - /* TRSTRCY = 10 ms; plus some extra */ - msleep(10 + 40); - update_devnum(udev, 0); - if (hcd->driver->reset_device) { - status = hcd->driver->reset_device(hcd, udev); - if (status < 0) { - dev_err(&udev->dev, "Cannot reset " - "HCD device state\n"); - break; - } - } - /* FALL THROUGH */ - case -ENOTCONN: - case -ENODEV: - clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_C_RESET); - /* FIXME need disconnect() for NOTATTACHED device */ - usb_set_device_state(udev, status - ? USB_STATE_NOTATTACHED - : USB_STATE_DEFAULT); + if (status == 0 || status == -ENOTCONN || status == -ENODEV) { + hub_port_finish_reset(hub, port1, udev, &status, warm); goto done; } dev_dbg (hub->intfdev, - "port %d not enabled, trying reset again...\n", - port1); + "port %d not enabled, trying %sreset again...\n", + port1, warm ? "warm " : ""); delay = HUB_LONG_RESET_TIME; } @@ -2146,45 +2187,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1, "Cannot enable port %i. Maybe the USB cable is bad?\n", port1); - done: - up_read(&ehci_cf_port_reset_rwsem); - return status; -} - -/* Warm reset a USB3 protocol port */ -static int hub_port_warm_reset(struct usb_hub *hub, int port) -{ - int ret; - u16 portstatus, portchange; - - if (!hub_is_superspeed(hub->hdev)) { - dev_err(hub->intfdev, "only USB3 hub support warm reset\n"); - return -EINVAL; - } - - /* Warm reset the port */ - ret = set_port_feature(hub->hdev, - port, USB_PORT_FEAT_BH_PORT_RESET); - if (ret) { - dev_err(hub->intfdev, "cannot warm reset port %d\n", port); - return ret; - } - - msleep(20); - ret = hub_port_status(hub, port, &portstatus, &portchange); - - if (portchange & USB_PORT_STAT_C_RESET) - clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET); - - if (portchange & USB_PORT_STAT_C_BH_RESET) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_BH_PORT_RESET); - - if (portchange & USB_PORT_STAT_C_LINK_STATE) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_PORT_LINK_STATE); +done: + if (!warm) + up_read(&ehci_cf_port_reset_rwsem); - return ret; + return status; } /* Check if a port is power on */ @@ -2814,7 +2821,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ @@ -2935,7 +2942,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, buf->bMaxPacketSize0; kfree(buf); - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; if (oldspeed != udev->speed) { @@ -3556,7 +3563,8 @@ static void hub_events(void) (portstatus & USB_PORT_STAT_LINK_STATE) == USB_SS_PORT_LS_SS_INACTIVE) { dev_dbg(hub_dev, "warm reset port %d\n", i); - hub_port_warm_reset(hub, i); + hub_port_reset(hub, i, NULL, + HUB_BH_RESET_TIME, true); } if (connect_change) -- cgit v1.2.2 From 10d674a82e553cb8a1f41027bb3c3e309b3f6804 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 14 Sep 2011 14:24:52 -0700 Subject: USB: When hot reset for USB3 fails, try warm reset. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a hot reset (standard USB port reset) fails on a USB 3.0 port, the host controller transitions to the "Error" state. It reports the port link state as "Inactive", sets the link state change flag, and (if the device disconnects) also reports the disconnect and connect change status. It's also supposed to transition the link state to "RxDetect", but the NEC µPD720200 xHCI host does not. Unfortunately, Harald found that the combination of the NEC µPD720200 and a LogiLink USB 3.0 to SATA adapter triggered this issue. The USB core would reset the device, the port would go into this error state, and the device would never be enumerated. This combination works under Windows, but not under Linux. When a hot reset fails on a USB 3.0 port, and the link state is reported as Inactive, fall back to a warm port reset instead. Harald confirms that with a warm port reset (along with all the change bits being correctly cleared), the USB 3.0 device will successfully enumerate. Harald also had to add two other patches ("xhci: Set change bit when warm reset change is set." and "usbcore: refine warm reset logic") to make this setup work. Since the warm reset refinement patch is not destined for the stable kernels (it's too big), this patch should not be backported either. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=41752 Signed-off-by: Sarah Sharp Tested-by: Harald Brennich Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index aa336f736f0c..1c155123c32f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2029,6 +2029,17 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 +static int hub_port_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, unsigned int delay, bool warm); + +/* Is a USB 3.0 port in the Inactive state? */ +static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) +{ + return hub_is_superspeed(hub->hdev) && + (portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_INACTIVE; +} + static int hub_port_wait_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm) { @@ -2052,6 +2063,38 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, * when the port appears not to be connected. */ if (!warm) { + /* + * Some buggy devices can cause an NEC host controller + * to transition to the "Error" state after a hot port + * reset. This will show up as the port state in + * "Inactive", and the port may also report a + * disconnect. Forcing a warm port reset seems to make + * the device work. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 + */ + if (hub_port_inactive(hub, portstatus)) { + int ret; + + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + if (portchange & USB_PORT_STAT_C_RESET) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_RESET); + dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n", + port1); + ret = hub_port_reset(hub, port1, + udev, HUB_BH_RESET_TIME, + true); + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + return ret; + } /* Device went away? */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; -- cgit v1.2.2 From 3148bf041d169a083aa31bd69bedd5bfb7ffe215 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:47 -0700 Subject: usbcore: get BOS descriptor set This commit gets BOS(Binary Device Object Store) descriptor set for Super Speed devices and High Speed devices which support BOS descriptor. BOS descriptor is used to report additional USB device-level capabilities that are not reported via the Device descriptor. By getting BOS descriptor set, driver can check device's device-level capability such as LPM capability. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1c155123c32f..7a2514322bfd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3083,6 +3083,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } + if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) + usb_get_bos_descriptor(udev); + retval = 0; /* notify HCD that we have a device connected and addressed */ if (hcd->driver->update_device) -- cgit v1.2.2 From 1ff4df56846d10379939166713bb2908e6a5ee0e Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:48 -0700 Subject: usbcore: check device's LPM capability Check device's LPM capability by examining the bmAttibutes field of the USB2.0 Extension Descriptor. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7a2514322bfd..4ffc3d1bd9e7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3083,8 +3083,14 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } - if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) - usb_get_bos_descriptor(udev); + if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { + retval = usb_get_bos_descriptor(udev); + if (!retval) { + if (udev->bos->ext_cap && (USB_LPM_SUPPORT & + le32_to_cpu(udev->bos->ext_cap->bmAttributes))) + udev->lpm_capable = 1; + } + } retval = 0; /* notify HCD that we have a device connected and addressed */ -- cgit v1.2.2 From 65580b4321eb36f16ae8b5987bfa1bb948fc5112 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:52 -0700 Subject: xHCI: set USB2 hardware LPM If the device pass the USB2 software LPM and the host supports hardware LPM, enable hardware LPM for the device to let the host decide when to put the link into lower power state. If hardware LPM is enabled for a port and driver wants to put it into suspend, it must first disable hardware LPM, resume the port into U0, and then suspend the port. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/usb/core/hub.c') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4ffc3d1bd9e7..d6cc83249341 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2392,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } } + /* disable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(udev, 0); + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2603,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); + } else { + /* Try to enable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); } + return status; } -- cgit v1.2.2