aboutsummaryrefslogtreecommitdiffstats
path: root/usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch
diff options
Diffstat (limited to 'usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch')
-rw-r--r--usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch97
1 files changed, 97 insertions, 0 deletions
diff --git a/usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch b/usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch
new file mode 100644
index 00000000000000..59451e9ac658ae
--- /dev/null
+++ b/usb/usb-fix-issue-with-usb-3.0-devices-after-system-resume.patch
@@ -0,0 +1,97 @@
+From sarah.a.sharp@linux.intel.com Thu Oct 14 11:49:31 2010
+Date: Thu, 14 Oct 2010 07:22:54 -0700
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, "He, Alex" <Alex.He@amd.com>,
+ "Su, Henry" <Henry.Su@amd.com>, Andiry Xu <andiry.xu@amd.com>
+Subject: usb: Fix issue with USB 3.0 devices after system resume
+Message-ID: <20101014142254.GA2928@xanatos>
+Content-Disposition: inline
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+When the system suspends and a host controller's power is lost, the USB
+core attempts to revive any USB devices that had the persist_enabled flag
+set. For non-SuperSpeed devices, it will disable the port, and then set
+the udev->reset_resume flag. This will cause the USB core to reset the
+device, verify the device descriptors to make sure it's the same device,
+and re-install any non-default configurations or alternate interface
+settings.
+
+However, we can't disable SuperSpeed root hub ports because that turns off
+SuperSpeed terminations, which will inhibit any devices connecting at USB
+3.0 speeds. (Plus external hubs don't allow SuperSpeed ports to be
+disabled.)
+
+Because of this logic in hub_activate():
+ /* We can forget about a "removed" device when there's a
+ * physical disconnect or the connect status changes.
+ */
+ if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+ (portchange & USB_PORT_STAT_C_CONNECTION))
+ clear_bit(port1, hub->removed_bits);
+
+ if (!udev || udev->state == USB_STATE_NOTATTACHED) {
+ /* Tell khubd to disconnect the device or
+ * check for a new connection
+ */
+ if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+ set_bit(port1, hub->change_bits);
+
+ } else if (portstatus & USB_PORT_STAT_ENABLE) {
+ /* The power session apparently survived the resume.
+ * If there was an overcurrent or suspend change
+ * (i.e., remote wakeup request), have khubd
+ * take care of it.
+ */
+ if (portchange)
+ set_bit(port1, hub->change_bits);
+
+ } else if (udev->persist_enabled) {
+ udev->reset_resume = 1;
+ set_bit(port1, hub->change_bits);
+
+ } else {
+ /* The power session is gone; tell khubd */
+ usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+ set_bit(port1, hub->change_bits);
+ }
+
+a SuperSpeed device after a resume with a loss of power will never get the
+reset_resume flag set. Instead the core will assume the power session
+survived and that the device still has the same address, configuration,
+and alternate interface settings. The xHCI host controller will have no
+knowledge of the device (since all xhci_virt_devices were destroyed when
+power loss was discovered, and xhci_discover_or_reset_device() has not
+been called), and all URBs to the device will fail.
+
+If the device driver responds by resetting the device, everything will
+continue smoothly. However, if lsusb is used before the device driver
+resets the device (or there is no driver), then all lsusb descriptor
+fetches will fail.
+
+The quick fix is to pretend the port is disabled in hub_activate(), by
+clearing the local variable. But I'm not sure what other parts of the hub
+driver need to be changed because they have assumptions about when ports
+will be disabled.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Andiry Xu <andiry.xu@amd.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/hub.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -758,6 +758,9 @@ static void hub_activate(struct usb_hub
+ clear_port_feature(hdev, port1,
+ USB_PORT_FEAT_ENABLE);
+ portstatus &= ~USB_PORT_STAT_ENABLE;
++ } else {
++ /* Pretend that power was lost for USB3 devs */
++ portstatus &= ~USB_PORT_STAT_ENABLE;
+ }
+ }
+