--- /dev/null
+From sarah.a.sharp@linux.intel.com Wed Mar 13 11:27:06 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 13 Mar 2013 10:59:21 -0700
+Subject: USB: Fix connected device switch to Inactive state.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org, sarah.a.sharp@linux.intel.com
+Message-ID: <20130313175801.GA5604@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit d3b9d7a9051d7024a93c76a84b2f84b3b66ad6d5.
+It needs to be backported to kernels as old as 3.2, because it fixes the
+buggy commit 9dbcaec830cd97f44a0b91b315844e0d7144746b "USB: Handle warm
+reset failure on empty port."]
+
+A USB 3.0 device can transition to the Inactive state if a U1 or U2 exit
+transition fails. The current code in hub_events simply issues a warm
+reset, but does not call any pre-reset or post-reset driver methods (or
+unbind/rebind drivers without them). Therefore the drivers won't know
+their device has just been reset.
+
+hub_events should instead call usb_reset_device. This means
+hub_port_reset now needs to figure out whether it should issue a warm
+reset or a hot reset.
+
+Remove the FIXME note about needing disconnect() for a NOTATTACHED
+device. This patch fixes that.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/core/hub.c | 30 +++++++++++++++++++++++++-----
+ 1 file changed, 25 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2274,7 +2274,6 @@ static void hub_port_finish_reset(struct
+ case -ENODEV:
+ clear_port_feature(hub->hdev,
+ port1, USB_PORT_FEAT_C_RESET);
+- /* FIXME need disconnect() for NOTATTACHED device */
+ if (hub_is_superspeed(hub->hdev)) {
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_BH_PORT_RESET);
+@@ -2308,6 +2307,18 @@ static int hub_port_reset(struct usb_hub
+ * Some companion controllers don't like it when they mix.
+ */
+ down_read(&ehci_cf_port_reset_rwsem);
++ } else if (!warm) {
++ /*
++ * If the caller hasn't explicitly requested a warm reset,
++ * double check and see if one is needed.
++ */
++ status = hub_port_status(hub, port1,
++ &portstatus, &portchange);
++ if (status < 0)
++ goto done;
++
++ if (hub_port_warm_reset_required(hub, portstatus))
++ warm = true;
+ }
+
+ /* Reset the port */
+@@ -3866,12 +3877,21 @@ static void hub_events(void)
+ */
+ if (hub_port_warm_reset_required(hub, portstatus)) {
+ int status;
++ struct usb_device *udev =
++ hub->hdev->children[i - 1];
+
+ dev_dbg(hub_dev, "warm reset port %d\n", i);
+- status = hub_port_reset(hub, i, NULL,
+- HUB_BH_RESET_TIME, true);
+- if (status < 0)
+- hub_port_disable(hub, i, 1);
++ if (!udev) {
++ status = hub_port_reset(hub, i,
++ NULL, HUB_BH_RESET_TIME,
++ true);
++ if (status < 0)
++ hub_port_disable(hub, i, 1);
++ } else {
++ usb_lock_device(udev);
++ status = usb_reset_device(udev);
++ usb_unlock_device(udev);
++ }
+ connect_change = 0;
+ }
+