--- /dev/null
+From sarah.a.sharp@linux.intel.com Tue Mar 12 15:00:01 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:24:19 -0800
+Subject: USB: Don't use EHCI port sempahore for USB 3.0 hubs.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002419.GA13701@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 0fe51aa5eee51db7c7ecd201d42a977ad79c58b6.
+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."]
+
+The EHCI host controller needs to prevent EHCI initialization when the
+UHCI or OHCI companion controller is in the middle of a port reset. It
+uses ehci_cf_port_reset_rwsem to do this. USB 3.0 hubs can't be under
+an EHCI host controller, so it makes no sense to down the semaphore for
+USB 3.0 hubs. It also makes the warm port reset code more complex.
+
+Don't down ehci_cf_port_reset_rwsem for USB 3.0 hubs.
+
+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 | 15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2330,17 +2330,16 @@ static int hub_port_reset(struct usb_hub
+ {
+ int i, status;
+
+- 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)) {
++ if (!hub_is_superspeed(hub->hdev)) {
++ if (warm) {
+ dev_err(hub->intfdev, "only USB3 hub support "
+ "warm reset\n");
+ return -EINVAL;
+ }
++ /* 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);
+ }
+
+ /* Reset the port */
+@@ -2378,7 +2377,7 @@ static int hub_port_reset(struct usb_hub
+ port1);
+
+ done:
+- if (!warm)
++ if (!hub_is_superspeed(hub->hdev))
+ up_read(&ehci_cf_port_reset_rwsem);
+
+ return status;
--- /dev/null
+From sarah.a.sharp@linux.intel.com Tue Mar 12 15:00:24 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:24:22 -0800
+Subject: USB: Prepare for refactoring by adding extra udev checks.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002422.GA13722@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 2d4fa940f99663c82ba55b2244638833b388e4e2.
+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."]
+
+The next patch will refactor the hub port code to rip out the recursive
+call to hub_port_reset on a failed hot reset. In preparation for that,
+make sure all code paths can deal with being called with a NULL udev.
+The usb_device will not be valid if warm reset was issued because a port
+transitioned to the Inactive or Compliance Mode on a device connect.
+
+This patch should have no effect on current behavior.
+
+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 | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2253,6 +2253,9 @@ static int hub_port_wait_reset(struct us
+ return -ENOTCONN;
+
+ if ((portstatus & USB_PORT_STAT_ENABLE)) {
++ if (!udev)
++ return 0;
++
+ if (hub_is_wusb(hub))
+ udev->speed = USB_SPEED_WIRELESS;
+ else if (hub_is_superspeed(hub->hdev))
+@@ -2296,13 +2299,15 @@ static void hub_port_finish_reset(struct
+ struct usb_hcd *hcd;
+ /* TRSTRCY = 10 ms; plus some extra */
+ msleep(10 + 40);
+- update_devnum(udev, 0);
+- hcd = bus_to_hcd(udev->bus);
+- /* The xHC may think the device is already reset,
+- * so ignore the status.
+- */
+- if (hcd->driver->reset_device)
+- hcd->driver->reset_device(hcd, udev);
++ if (udev) {
++ update_devnum(udev, 0);
++ hcd = bus_to_hcd(udev->bus);
++ /* The xHC may think the device is already
++ * reset, so ignore the status.
++ */
++ if (hcd->driver->reset_device)
++ hcd->driver->reset_device(hcd, udev);
++ }
+ }
+ /* FALL THROUGH */
+ case -ENOTCONN:
+@@ -2316,7 +2321,7 @@ static void hub_port_finish_reset(struct
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
+ }
+- if (!warm)
++ if (!warm && udev)
+ usb_set_device_state(udev, *status
+ ? USB_STATE_NOTATTACHED
+ : USB_STATE_DEFAULT);
--- /dev/null
+From sarah.a.sharp@linux.intel.com Tue Mar 12 15:02:01 2013
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Thu, 7 Mar 2013 16:24:24 -0800
+Subject: USB: Rip out recursive call on warm port reset.
+To: Greg KH <gregkh@linuxfoundation.org>
+Cc: stable@vger.kernel.org
+Message-ID: <20130308002424.GA13738@xanatos>
+
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+
+[This is upstream commit 24a6078754f28528bc91e7e7b3e6ae86bd936d8.
+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."]
+
+When a hot reset fails on a USB 3.0 port, the current port reset code
+recursively calls hub_port_reset inside hub_port_wait_reset. This isn't
+ideal, since we should avoid recursive calls in the kernel, and it also
+doesn't allow us to issue multiple warm resets on reset failures.
+
+Rip out the recursive call. Instead, add code to hub_port_reset to
+issue a warm reset if the hot reset fails, and try multiple warm resets
+before giving up on the port.
+
+In hub_port_wait_reset, remove the recursive call and re-indent. The
+code is basically the same, except:
+
+1. It bails out early if the port has transitioned to Inactive or
+Compliance Mode after the reset completed.
+
+2. It doesn't consider a connect status change to be a failed reset. If
+multiple warm resets needed to be issued, the connect status may have
+changed, so we need to ignore that and look at the port link state
+instead. hub_port_reset will now do that.
+
+3. It unconditionally sets udev->speed on all types of successful
+resets. The old recursive code would set the port speed when the second
+hub_port_reset returned.
+
+The old code did not handle connected devices needing a warm reset well.
+There were only two situations that the old code handled correctly: an
+empty port needing a warm reset, and a hot reset that migrated to a warm
+reset.
+
+When an empty port needed a warm reset, hub_port_reset was called with
+the warm variable set. The code in hub_port_finish_reset would skip
+telling the USB core and the xHC host that the device was reset, because
+otherwise that would result in a NULL pointer dereference.
+
+When a USB 3.0 device reset migrated to a warm reset, the recursive call
+made the call stack look like this:
+
+hub_port_reset(warm = false)
+ hub_wait_port_reset(warm = false)
+ hub_port_reset(warm = true)
+ hub_wait_port_reset(warm = true)
+ hub_port_finish_reset(warm = true)
+ (return up the call stack to the first wait)
+
+ hub_port_finish_reset(warm = false)
+
+The old code didn't want to notify the USB core or the xHC host of device reset
+twice, so it only did it in the second call to hub_port_finish_reset,
+when warm was set to false. This was necessary because
+before patch two ("USB: Ignore xHCI Reset Device status."), the USB core
+would pay attention to the xHC Reset Device command error status, and
+the second call would always fail.
+
+Now that we no longer have the recursive call, and warm can change from
+false to true in hub_port_reset, we need to have hub_port_finish_reset
+unconditionally notify the USB core and the xHC of the device reset.
+
+In hub_port_finish_reset, unconditionally clear the connect status
+change (CSC) bit for USB 3.0 hubs when the port reset is done. If we
+had to issue multiple warm resets for a device, that bit may have been
+set if the device went into SS.Inactive and then was successfully warm
+reset.
+
+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 | 148 ++++++++++++++++++++++---------------------------
+ 1 file changed, 67 insertions(+), 81 deletions(-)
+
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2207,73 +2207,35 @@ static int hub_port_wait_reset(struct us
+ if ((portstatus & USB_PORT_STAT_RESET))
+ goto delay;
+
+- /*
+- * Some buggy devices require a warm reset to be issued even
+- * when the port appears not to be connected.
++ if (hub_port_warm_reset_required(hub, portstatus))
++ return -ENOTCONN;
++
++ /* Device went away? */
++ if (!(portstatus & USB_PORT_STAT_CONNECTION))
++ return -ENOTCONN;
++
++ /* bomb out completely if the connection bounced. A USB 3.0
++ * connection may bounce if multiple warm resets were issued,
++ * but the device may have successfully re-connected. Ignore it.
+ */
+- 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_warm_reset_required(hub, portstatus)) {
+- int ret;
++ if (!hub_is_superspeed(hub->hdev) &&
++ (portchange & USB_PORT_STAT_C_CONNECTION))
++ return -ENOTCONN;
+
+- 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;
+-
+- /* bomb out completely if the connection bounced */
+- if ((portchange & USB_PORT_STAT_C_CONNECTION))
+- return -ENOTCONN;
+-
+- if ((portstatus & USB_PORT_STAT_ENABLE)) {
+- if (!udev)
+- return 0;
+-
+- 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;
++ if ((portstatus & USB_PORT_STAT_ENABLE)) {
++ if (!udev)
+ return 0;
+- }
+- } else {
+- if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
+- hub_port_warm_reset_required(hub,
+- portstatus))
+- return -ENOTCONN;
+
++ 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;
+ }
+
+@@ -2291,23 +2253,21 @@ delay:
+ }
+
+ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
+- struct usb_device *udev, int *status, bool warm)
++ struct usb_device *udev, int *status)
+ {
+ switch (*status) {
+ case 0:
+- if (!warm) {
+- struct usb_hcd *hcd;
+- /* TRSTRCY = 10 ms; plus some extra */
+- msleep(10 + 40);
+- if (udev) {
+- update_devnum(udev, 0);
+- hcd = bus_to_hcd(udev->bus);
+- /* The xHC may think the device is already
+- * reset, so ignore the status.
+- */
+- if (hcd->driver->reset_device)
+- hcd->driver->reset_device(hcd, udev);
+- }
++ /* TRSTRCY = 10 ms; plus some extra */
++ msleep(10 + 40);
++ if (udev) {
++ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
++
++ update_devnum(udev, 0);
++ /* The xHC may think the device is already reset,
++ * so ignore the status.
++ */
++ if (hcd->driver->reset_device)
++ hcd->driver->reset_device(hcd, udev);
+ }
+ /* FALL THROUGH */
+ case -ENOTCONN:
+@@ -2320,8 +2280,10 @@ static void hub_port_finish_reset(struct
+ USB_PORT_FEAT_C_BH_PORT_RESET);
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_PORT_LINK_STATE);
++ clear_port_feature(hub->hdev, port1,
++ USB_PORT_FEAT_C_CONNECTION);
+ }
+- if (!warm && udev)
++ if (udev)
+ usb_set_device_state(udev, *status
+ ? USB_STATE_NOTATTACHED
+ : USB_STATE_DEFAULT);
+@@ -2334,6 +2296,7 @@ static int hub_port_reset(struct usb_hub
+ struct usb_device *udev, unsigned int delay, bool warm)
+ {
+ int i, status;
++ u16 portchange, portstatus;
+
+ if (!hub_is_superspeed(hub->hdev)) {
+ if (warm) {
+@@ -2365,10 +2328,33 @@ static int hub_port_reset(struct usb_hub
+ status);
+ }
+
+- /* return on disconnect or reset */
++ /* Check for disconnect or reset */
+ if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+- hub_port_finish_reset(hub, port1, udev, &status, warm);
+- goto done;
++ hub_port_finish_reset(hub, port1, udev, &status);
++
++ if (!hub_is_superspeed(hub->hdev))
++ goto done;
++
++ /*
++ * If a USB 3.0 device migrates from reset to an error
++ * state, re-issue the warm reset.
++ */
++ if (hub_port_status(hub, port1,
++ &portstatus, &portchange) < 0)
++ goto done;
++
++ if (!hub_port_warm_reset_required(hub, portstatus))
++ goto done;
++
++ /*
++ * If the port is in SS.Inactive or Compliance Mode, the
++ * hot or warm reset failed. Try another warm reset.
++ */
++ if (!warm) {
++ dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
++ port1);
++ warm = true;
++ }
+ }
+
+ dev_dbg (hub->intfdev,