Merge tag 'v3.10.95' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / usb / core / hub.c
index ca8b4c22ba6961bcb94e46b217a841ec3dfd9ce1..67c6d0d4ba0b6721401c0b04e56cd290dcfc86e1 100644 (file)
@@ -233,6 +233,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 #define HUB_DEBOUNCE_STEP        25
 #define HUB_DEBOUNCE_STABLE     100
 
+static void hub_release(struct kref *kref);
 static int usb_reset_and_verify_device(struct usb_device *udev);
 
 #define usb_sndaddr0pipe()     (PIPE_CONTROL << 30)
@@ -261,6 +262,10 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
 
 static int usb_device_supports_lpm(struct usb_device *udev)
 {
+       /* Some devices have trouble with LPM */
+       if (udev->quirks & USB_QUIRK_NO_LPM)
+               return 0;
+
        /* USB 2.1 (and greater) devices indicate LPM support through
         * their USB 2.0 Extended Capabilities BOS descriptor.
         */
@@ -1145,10 +1150,21 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
        unsigned delay;
 
        /* Continue a partial initialization */
-       if (type == HUB_INIT2)
-               goto init2;
-       if (type == HUB_INIT3)
+       if (type == HUB_INIT2 || type == HUB_INIT3) {
+               device_lock(hub->intfdev);
+
+               /* Was the hub disconnected while we were waiting? */
+               if (hub->disconnected) {
+                       device_unlock(hub->intfdev);
+                       kref_put(&hub->kref, hub_release);
+                       return;
+               }
+               if (type == HUB_INIT2)
+                       goto init2;
+
                goto init3;
+       }
+       kref_get(&hub->kref);
 
        /* The superspeed hub except for root hub has to use Hub Depth
         * value as an offset into the route string to locate the bits
@@ -1290,7 +1306,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        /* Tell khubd to disconnect the device or
                         * check for a new connection
                         */
-                       if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+                       if (udev || (portstatus & USB_PORT_STAT_CONNECTION) ||
+                           (portstatus & USB_PORT_STAT_OVERCURRENT))
                                set_bit(port1, hub->change_bits);
 
                } else if (portstatus & USB_PORT_STAT_ENABLE) {
@@ -1344,6 +1361,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);
                        schedule_delayed_work(&hub->init_work,
                                        msecs_to_jiffies(delay));
+                       device_unlock(hub->intfdev);
                        return;         /* Continues at init3: below */
                } else {
                        msleep(delay);
@@ -1364,6 +1382,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
        /* Allow autosuspend if it was suppressed */
        if (type <= HUB_INIT3)
                usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+
+       if (type == HUB_INIT2 || type == HUB_INIT3)
+               device_unlock(hub->intfdev);
+
+       kref_put(&hub->kref, hub_release);
 }
 
 /* Implement the continuations for the delays above */
@@ -2086,8 +2109,10 @@ void usb_set_device_state(struct usb_device *udev,
                                        || new_state == USB_STATE_SUSPENDED)
                                ;       /* No change to wakeup settings */
                        else if (new_state == USB_STATE_CONFIGURED)
-                               wakeup = udev->actconfig->desc.bmAttributes
-                                        & USB_CONFIG_ATT_WAKEUP;
+                               wakeup = (udev->quirks &
+                                       USB_QUIRK_IGNORE_REMOTE_WAKEUP) ? 0 :
+                                       udev->actconfig->desc.bmAttributes &
+                                       USB_CONFIG_ATT_WAKEUP;
                        else
                                wakeup = 0;
                }
@@ -2291,7 +2316,7 @@ void usb_disconnect(struct usb_device **pdev)
        put_device(&udev->dev);
 
 #ifdef CONFIG_MTK_ICUSB_SUPPORT
-       if(is_icusb_rh)
+       if (is_icusb_rh)
        {
                set_icusb_sts_disconnect_done();
                MYDBG("ICUSB Disconnect\n");
@@ -3510,10 +3535,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
                dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
                                port1, status);
        } else {
-               /* drive resume for at least 20 msec */
+               /* drive resume for USB_RESUME_TIMEOUT msec */
                dev_dbg(&udev->dev, "usb %sresume\n",
                                (PMSG_IS_AUTO(msg) ? "auto-" : ""));
-               msleep(25);
+               msleep(USB_RESUME_TIMEOUT);
 
                /* Virtual root hubs can trigger on GET_PORT_STATUS to
                 * stop resume signaling.  Then finish the resume
@@ -4527,6 +4552,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                goto fail;
        }
 
+       usb_detect_quirks(udev);
+
        if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
                retval = usb_get_bos_descriptor(udev);
                if (!retval) {
@@ -4777,7 +4804,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
                MYDBG("");
 
-               usb_detect_quirks(udev);
                if (udev->quirks & USB_QUIRK_DELAY_INIT)
                        msleep(1000);
 
@@ -4956,9 +4982,10 @@ static void hub_events(void)
 
                hub = list_entry(tmp, struct usb_hub, event_list);
                kref_get(&hub->kref);
+               hdev = hub->hdev;
+               usb_get_dev(hdev);
                spin_unlock_irq(&hub_event_lock);
 
-               hdev = hub->hdev;
                hub_dev = hub->intfdev;
                intf = to_usb_interface(hub_dev);
                dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
@@ -5174,6 +5201,7 @@ static void hub_events(void)
                usb_autopm_put_interface(intf);
  loop_disconnected:
                usb_unlock_device(hdev);
+               usb_put_dev(hdev);
                kref_put(&hub->kref, hub_release);
 
         } /* end while (1) */