commit 032f44791457d9aa50c6a194a2d475f07e311afd
Author: Felix Fietkau <nbd@openwrt.org>
Date:   Wed Jul 9 12:08:23 2014 +0200

    Revert "USB: unbind all interfaces before rebinding any"
    
    This reverts commit 59f0103d74e4a32cbaa054d5011ea287fcfb83e4.
    The commit has been found to cause USB regressions on AR933x and
    BCM4705.

--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -953,7 +953,8 @@ EXPORT_SYMBOL_GPL(usb_deregister);
  * it doesn't support pre_reset/post_reset/reset_resume or
  * because it doesn't support suspend/resume.
  *
- * The caller must hold @intf's device's lock, but not @intf's lock.
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
  */
 void usb_forced_unbind_intf(struct usb_interface *intf)
 {
@@ -966,37 +967,16 @@ void usb_forced_unbind_intf(struct usb_i
 	intf->needs_binding = 1;
 }
 
-/*
- * Unbind drivers for @udev's marked interfaces.  These interfaces have
- * the needs_binding flag set, for example by usb_resume_interface().
- *
- * The caller must hold @udev's device lock.
- */
-static void unbind_marked_interfaces(struct usb_device *udev)
-{
-	struct usb_host_config	*config;
-	int			i;
-	struct usb_interface	*intf;
-
-	config = udev->actconfig;
-	if (config) {
-		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
-			intf = config->interface[i];
-			if (intf->dev.driver && intf->needs_binding)
-				usb_forced_unbind_intf(intf);
-		}
-	}
-}
-
 /* Delayed forced unbinding of a USB interface driver and scan
  * for rebinding.
  *
- * The caller must hold @intf's device's lock, but not @intf's lock.
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
  *
  * Note: Rebinds will be skipped if a system sleep transition is in
  * progress and the PM "complete" callback hasn't occurred yet.
  */
-static void usb_rebind_intf(struct usb_interface *intf)
+void usb_rebind_intf(struct usb_interface *intf)
 {
 	int rc;
 
@@ -1013,66 +993,68 @@ static void usb_rebind_intf(struct usb_i
 	}
 }
 
-/*
- * Rebind drivers to @udev's marked interfaces.  These interfaces have
- * the needs_binding flag set.
+#ifdef CONFIG_PM
+
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
  *
  * The caller must hold @udev's device lock.
  */
-static void rebind_marked_interfaces(struct usb_device *udev)
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
 {
 	struct usb_host_config	*config;
 	int			i;
 	struct usb_interface	*intf;
+	struct usb_driver	*drv;
 
 	config = udev->actconfig;
 	if (config) {
 		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
 			intf = config->interface[i];
-			if (intf->needs_binding)
-				usb_rebind_intf(intf);
+
+			if (intf->dev.driver) {
+				drv = to_usb_driver(intf->dev.driver);
+				if (!drv->suspend || !drv->resume)
+					usb_forced_unbind_intf(intf);
+			}
 		}
 	}
 }
 
-/*
- * Unbind all of @udev's marked interfaces and then rebind all of them.
- * This ordering is necessary because some drivers claim several interfaces
- * when they are first probed.
+/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
+ * These interfaces have the needs_binding flag set by usb_resume_interface().
  *
  * The caller must hold @udev's device lock.
  */
-void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
+static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
 {
-	unbind_marked_interfaces(udev);
-	rebind_marked_interfaces(udev);
-}
+	struct usb_host_config	*config;
+	int			i;
+	struct usb_interface	*intf;
 
-#ifdef CONFIG_PM
+	config = udev->actconfig;
+	if (config) {
+		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+			intf = config->interface[i];
+			if (intf->dev.driver && intf->needs_binding)
+				usb_forced_unbind_intf(intf);
+		}
+	}
+}
 
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume
- * There is no check for reset_resume here because it can be determined
- * only during resume whether reset_resume is needed.
- *
- * The caller must hold @udev's device lock.
- */
-static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
+static void do_rebind_interfaces(struct usb_device *udev)
 {
 	struct usb_host_config	*config;
 	int			i;
 	struct usb_interface	*intf;
-	struct usb_driver	*drv;
 
 	config = udev->actconfig;
 	if (config) {
 		for (i = 0; i < config->desc.bNumInterfaces; ++i) {
 			intf = config->interface[i];
-
-			if (intf->dev.driver) {
-				drv = to_usb_driver(intf->dev.driver);
-				if (!drv->suspend || !drv->resume)
-					usb_forced_unbind_intf(intf);
-			}
+			if (intf->needs_binding)
+				usb_rebind_intf(intf);
 		}
 	}
 }
@@ -1397,7 +1379,7 @@ int usb_resume_complete(struct device *d
 	 * whose needs_binding flag is set
 	 */
 	if (udev->state != USB_STATE_NOTATTACHED)
-		rebind_marked_interfaces(udev);
+		do_rebind_interfaces(udev);
 	return 0;
 }
 
@@ -1419,7 +1401,7 @@ int usb_resume(struct device *dev, pm_me
 		pm_runtime_disable(dev);
 		pm_runtime_set_active(dev);
 		pm_runtime_enable(dev);
-		unbind_marked_interfaces(udev);
+		unbind_no_reset_resume_drivers_interfaces(udev);
 	}
 
 	/* Avoid PM error messages for devices disconnected while suspended
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -5263,11 +5263,10 @@ int usb_reset_device(struct usb_device *
 				else if (cintf->condition ==
 						USB_INTERFACE_BOUND)
 					rebind = 1;
-				if (rebind)
-					cintf->needs_binding = 1;
 			}
+			if (ret == 0 && rebind)
+				usb_rebind_intf(cintf);
 		}
-		usb_unbind_and_rebind_marked_interfaces(udev);
 	}
 
 	usb_autosuspend_device(udev);
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct
 extern int usb_match_device(struct usb_device *dev,
 			    const struct usb_device_id *id);
 extern void usb_forced_unbind_intf(struct usb_interface *intf);
-extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
+extern void usb_rebind_intf(struct usb_interface *intf);
 
 extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
 		struct dev_state *owner);
