aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic')
-rw-r--r--target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch206
1 files changed, 206 insertions, 0 deletions
diff --git a/target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch b/target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch
new file mode 100644
index 0000000000..aa1ccb31ed
--- /dev/null
+++ b/target/linux/generic/patches-3.10/140-revert_usb_unbind_interfaces.patch
@@ -0,0 +1,206 @@
+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);