Merge branch 'stable-3.2' into pandora-3.2
[pandora-kernel.git] / drivers / usb / core / hub.c
index 7978146..7c59466 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/random.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -152,7 +153,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);
 
 static inline char *portspeed(struct usb_hub *hub, int portstatus)
@@ -176,7 +177,8 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
 }
 
 /* USB 2.0 spec Section 11.24.4.5 */
-static int get_hub_descriptor(struct usb_device *hdev, void *data)
+static int get_hub_descriptor(struct usb_device *hdev,
+               struct usb_hub_descriptor *desc)
 {
        int i, ret, size;
        unsigned dtype;
@@ -192,10 +194,18 @@ static int get_hub_descriptor(struct usb_device *hdev, void *data)
        for (i = 0; i < 3; i++) {
                ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
-                       dtype << 8, 0, data, size,
+                       dtype << 8, 0, desc, size,
                        USB_CTRL_GET_TIMEOUT);
-               if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
+               if (hub_is_superspeed(hdev)) {
+                       if (ret == size)
+                               return ret;
+               } else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) {
+                       /* Make sure we have the DeviceRemovable field. */
+                       size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1;
+                       if (ret < size)
+                               return -EMSGSIZE;
                        return ret;
+               }
        }
        return -EINVAL;
 }
@@ -462,6 +472,15 @@ resubmit:
 static inline int
 hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt)
 {
+       /* Need to clear both directions for control ep */
+       if (((devinfo >> 11) & USB_ENDPOINT_XFERTYPE_MASK) ==
+                       USB_ENDPOINT_XFER_CONTROL) {
+               int status = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+                               HUB_CLEAR_TT_BUFFER, USB_RT_PORT,
+                               devinfo ^ 0x8000, tt, NULL, 0, 1000);
+               if (status)
+                       return status;
+       }
        return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
                               HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo,
                               tt, NULL, 0, 1000);
@@ -481,13 +500,16 @@ static void hub_tt_work(struct work_struct *work)
        int                     limit = 100;
 
        spin_lock_irqsave (&hub->tt.lock, flags);
-       while (--limit && !list_empty (&hub->tt.clear_list)) {
+       while (!list_empty(&hub->tt.clear_list)) {
                struct list_head        *next;
                struct usb_tt_clear     *clear;
                struct usb_device       *hdev = hub->hdev;
                const struct hc_driver  *drv;
                int                     status;
 
+               if (!hub->quiescing && --limit < 0)
+                       break;
+
                next = hub->tt.clear_list.next;
                clear = list_entry (next, struct usb_tt_clear, clear_list);
                list_del (&clear->clear_list);
@@ -615,6 +637,80 @@ static int hub_hub_status(struct usb_hub *hub,
        return ret;
 }
 
+static int hub_set_port_link_state(struct usb_hub *hub, int port1,
+                       unsigned int link_status)
+{
+       return set_port_feature(hub->hdev,
+                       port1 | (link_status << 3),
+                       USB_PORT_FEAT_LINK_STATE);
+}
+
+/*
+ * If USB 3.0 ports are placed into the Disabled state, they will no longer
+ * detect any device connects or disconnects.  This is generally not what the
+ * USB core wants, since it expects a disabled port to produce a port status
+ * change event when a new device connects.
+ *
+ * Instead, set the link state to Disabled, wait for the link to settle into
+ * that state, clear any change bits, and then put the port into the RxDetect
+ * state.
+ */
+static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
+{
+       int ret;
+       int total_time;
+       u16 portchange, portstatus;
+
+       if (!hub_is_superspeed(hub->hdev))
+               return -EINVAL;
+
+       ret = hub_port_status(hub, port1, &portstatus, &portchange);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
+        * Controller [1022:7814] will have spurious result making the following
+        * usb 3.0 device hotplugging route to the 2.0 root hub and recognized
+        * as high-speed device if we set the usb 3.0 port link state to
+        * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
+        * check the state here to avoid the bug.
+        */
+       if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                               USB_SS_PORT_LS_RX_DETECT) {
+               dev_dbg(hub->intfdev,
+                       "Not disabling port %d; link state is RxDetect\n",
+                       port1);
+               return ret;
+       }
+
+       ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
+       if (ret) {
+               dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
+                               port1, ret);
+               return ret;
+       }
+
+       /* Wait for the link to enter the disabled state. */
+       for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
+               ret = hub_port_status(hub, port1, &portstatus, &portchange);
+               if (ret < 0)
+                       return ret;
+
+               if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                               USB_SS_PORT_LS_SS_DISABLED)
+                       break;
+               if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+                       break;
+               msleep(HUB_DEBOUNCE_STEP);
+       }
+       if (total_time >= HUB_DEBOUNCE_TIMEOUT)
+               dev_warn(hub->intfdev, "Could not disable port %d after %d ms\n",
+                               port1, total_time);
+
+       return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
+}
+
 static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 {
        struct usb_device *hdev = hub->hdev;
@@ -623,8 +719,13 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
        if (hdev->children[port1-1] && set_state)
                usb_set_device_state(hdev->children[port1-1],
                                USB_STATE_NOTATTACHED);
-       if (!hub->error && !hub_is_superspeed(hub->hdev))
-               ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+       if (!hub->error) {
+               if (hub_is_superspeed(hub->hdev))
+                       ret = hub_usb3_port_disable(hub, port1);
+               else
+                       ret = clear_port_feature(hdev, port1,
+                                       USB_PORT_FEAT_ENABLE);
+       }
        if (ret)
                dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
                                port1, ret);
@@ -700,15 +801,41 @@ 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)
-               goto init3;
+       if (type == HUB_INIT2 || type == HUB_INIT3) {
+               device_lock(hub->intfdev);
 
-       /* After a resume, port power should still be on.
+               /* 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
+        * it uses to determine the downstream port number. So hub driver
+        * should send a set hub depth request to superspeed hub after
+        * the superspeed hub is set configuration in initialization or
+        * reset procedure.
+        *
+        * After a resume, port power should still be on.
         * For any other type of activation, turn it on.
         */
        if (type != HUB_RESUME) {
+               if (hdev->parent && hub_is_superspeed(hdev)) {
+                       ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+                                       HUB_SET_DEPTH, USB_RT_HUB,
+                                       hdev->level - 1, 0, NULL, 0,
+                                       USB_CTRL_SET_TIMEOUT);
+                       if (ret < 0)
+                               dev_err(hub->intfdev,
+                                               "set hub depth failed\n");
+               }
 
                /* Speed up system boot by using a delayed_work for the
                 * hub's initial power-up delays.  This is pretty awkward
@@ -766,6 +893,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 
                portstatus = portchange = 0;
                status = hub_port_status(hub, port1, &portstatus, &portchange);
+               if (status)
+                       goto abort;
+
                if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
                        dev_dbg(hub->intfdev,
                                        "port %d: status %04x change %04x\n",
@@ -812,6 +942,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_PORT_LINK_STATE);
                }
+               if (portchange & USB_PORT_STAT_C_RESET) {
+                       need_debounce_delay = true;
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_RESET);
+               }
 
                if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
                                hub_is_superspeed(hub->hdev)) {
@@ -871,6 +1006,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);
@@ -887,10 +1023,15 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 
        /* Scan all ports that need attention */
        kick_khubd(hub);
-
+ abort:
        /* 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 */
@@ -935,7 +1076,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
        if (hub->has_indicators)
                cancel_delayed_work_sync(&hub->leds);
        if (hub->tt.hub)
-               cancel_work_sync(&hub->tt.clear_work);
+               flush_work_sync(&hub->tt.clear_work);
 }
 
 /* caller has locked the hub device */
@@ -967,6 +1108,7 @@ static int hub_configure(struct usb_hub *hub,
        unsigned int pipe;
        int maxp, ret;
        char *message = "out of memory";
+       unsigned maxchild;
 
        hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
        if (!hub->buffer) {
@@ -981,33 +1123,27 @@ static int hub_configure(struct usb_hub *hub,
        }
        mutex_init(&hub->status_mutex);
 
-       hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
+       hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL);
        if (!hub->descriptor) {
                ret = -ENOMEM;
                goto fail;
        }
 
-       if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) {
-               ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
-                               HUB_SET_DEPTH, USB_RT_HUB,
-                               hdev->level - 1, 0, NULL, 0,
-                               USB_CTRL_SET_TIMEOUT);
-
-               if (ret < 0) {
-                       message = "can't set hub depth";
-                       goto fail;
-               }
-       }
-
        /* Request the entire hub descriptor.
         * hub->descriptor can handle USB_MAXCHILDREN ports,
-        * but the hub can/will return fewer bytes here.
+        * but a (non-SS) hub can/will return fewer bytes here.
         */
        ret = get_hub_descriptor(hdev, hub->descriptor);
        if (ret < 0) {
                message = "can't read hub descriptor";
                goto fail;
-       } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
+       }
+
+       maxchild = USB_MAXCHILDREN;
+       if (hub_is_superspeed(hdev))
+               maxchild = min_t(unsigned, maxchild, USB_SS_MAXPORTS);
+
+       if (hub->descriptor->bNbrPorts > maxchild) {
                message = "hub has too many ports!";
                ret = -ENODEV;
                goto fail;
@@ -1289,14 +1425,24 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
        desc = intf->cur_altsetting;
        hdev = interface_to_usbdev(intf);
 
-       /* Hubs have proper suspend/resume support.  USB 3.0 device suspend is
+       /*
+        * Hubs have proper suspend/resume support, except for root hubs
+        * where the controller driver doesn't have bus_suspend and
+        * bus_resume methods.  Also, USB 3.0 device suspend is
         * different from USB 2.0/1.1 device suspend, and unfortunately we
         * don't support it yet.  So leave autosuspend disabled for USB 3.0
         * external hubs for now.  Enable autosuspend for USB 3.0 roothubs,
         * since that isn't a "real" hub.
         */
-       if (!hub_is_superspeed(hdev) || !hdev->parent)
-               usb_enable_autosuspend(hdev);
+       if (hdev->parent) {             /* normal device */
+               if (!hub_is_superspeed(hdev))
+                       usb_enable_autosuspend(hdev);
+       } else {                        /* root hub */
+               const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
+
+               if (drv->bus_suspend && drv->bus_resume)
+                       usb_enable_autosuspend(hdev);
+       }
 
        if (hdev->level == MAX_TOPO_LEVEL) {
                dev_err(&intf->dev,
@@ -1522,8 +1668,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;
                }
@@ -1640,7 +1788,6 @@ void usb_disconnect(struct usb_device **pdev)
 {
        struct usb_device       *udev = *pdev;
        int                     i;
-       struct usb_hcd          *hcd = bus_to_hcd(udev->bus);
 
        /* mark the device as inactive, so any further urb submissions for
         * this device (and any of its children) will fail immediately.
@@ -1650,6 +1797,12 @@ void usb_disconnect(struct usb_device **pdev)
        dev_info(&udev->dev, "USB disconnect, device number %d\n",
                        udev->devnum);
 
+       /*
+        * Ensure that the pm runtime code knows that the USB device
+        * is in the process of being disconnected.
+        */
+       pm_runtime_barrier(&udev->dev);
+
        usb_lock_device(udev);
 
        /* Free up all the children before we remove this device */
@@ -1663,9 +1816,7 @@ void usb_disconnect(struct usb_device **pdev)
         * so that the hardware is now fully quiesced.
         */
        dev_dbg (&udev->dev, "unregistering device\n");
-       mutex_lock(hcd->bandwidth_mutex);
        usb_disable_device(udev, 0);
-       mutex_unlock(hcd->bandwidth_mutex);
        usb_hcd_synchronize_unlinks(udev);
 
        usb_remove_ep_devs(&udev->ep0);
@@ -1819,7 +1970,7 @@ static int usb_enumerate_device(struct usb_device *udev)
                if (err < 0) {
                        dev_err(&udev->dev, "can't read configurations, error %d\n",
                                err);
-                       goto fail;
+                       return err;
                }
        }
        if (udev->wusb == 1 && udev->authorized == 0) {
@@ -1835,8 +1986,12 @@ static int usb_enumerate_device(struct usb_device *udev)
                udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
        }
        err = usb_enumerate_device_otg(udev);
-fail:
-       return err;
+       if (err < 0)
+               return err;
+
+       usb_detect_interface_quirks(udev);
+
+       return 0;
 }
 
 
@@ -1896,6 +2051,14 @@ int usb_new_device(struct usb_device *udev)
        /* Tell the world! */
        announce_device(udev);
 
+       if (udev->serial)
+               add_device_randomness(udev->serial, strlen(udev->serial));
+       if (udev->product)
+               add_device_randomness(udev->product, strlen(udev->product));
+       if (udev->manufacturer)
+               add_device_randomness(udev->manufacturer,
+                                     strlen(udev->manufacturer));
+
        device_enable_async_suspend(&udev->dev);
        /* Register the device.  The device driver is responsible
         * for configuring the device and invoking the add-device
@@ -2033,17 +2196,18 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define HUB_SHORT_RESET_TIME   10
 #define HUB_BH_RESET_TIME      50
 #define HUB_LONG_RESET_TIME    200
-#define HUB_RESET_TIMEOUT      500
-
-static int hub_port_reset(struct usb_hub *hub, int port1,
-                       struct usb_device *udev, unsigned int delay, bool warm);
+#define HUB_RESET_TIMEOUT      800
 
-/* Is a USB 3.0 port in the Inactive state? */
-static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
+/* Is a USB 3.0 port in the Inactive or Complinance Mode state?
+ * Port worm reset is required to recover
+ */
+static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
 {
        return hub_is_superspeed(hub->hdev) &&
-               (portstatus & USB_PORT_STAT_LINK_STATE) ==
-               USB_SS_PORT_LS_SS_INACTIVE;
+               (((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                 USB_SS_PORT_LS_SS_INACTIVE) ||
+                ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                 USB_SS_PORT_LS_COMP_MOD)) ;
 }
 
 static int hub_port_wait_reset(struct usb_hub *hub, int port1,
@@ -2064,73 +2228,43 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (ret < 0)
                        return ret;
 
-               /*
-                * Some buggy devices require a warm reset to be issued even
-                * when the port appears not to be connected.
-                */
-               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_inactive(hub, portstatus)) {
-                               int ret;
-
-                               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;
+               /* The port state is unknown until the reset completes. */
+               if ((portstatus & USB_PORT_STAT_RESET))
+                       goto delay;
 
-                       /* bomb out completely if the connection bounced */
-                       if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                               return -ENOTCONN;
+               if (hub_port_warm_reset_required(hub, portstatus))
+                       return -ENOTCONN;
 
-                       /* if we`ve finished resetting, then break out of
-                        * the loop
-                        */
-                       if (!(portstatus & USB_PORT_STAT_RESET) &&
-                           (portstatus & USB_PORT_STAT_ENABLE)) {
-                               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;
-                       }
-               } else {
-                       if (portchange & USB_PORT_STAT_C_BH_RESET)
+               /* 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 (!hub_is_superspeed(hub->hdev) &&
+                               (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;
+                       return 0;
                }
 
+delay:
                /* switch to the long delay after two short delay failures */
                if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
                        delay = HUB_LONG_RESET_TIME;
@@ -2143,63 +2277,31 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
        return -EBUSY;
 }
 
-static void hub_port_finish_reset(struct usb_hub *hub, int port1,
-                       struct usb_device *udev, int *status, bool warm)
-{
-       switch (*status) {
-       case 0:
-               if (!warm) {
-                       struct usb_hcd *hcd;
-                       /* TRSTRCY = 10 ms; plus some extra */
-                       msleep(10 + 40);
-                       update_devnum(udev, 0);
-                       hcd = bus_to_hcd(udev->bus);
-                       if (hcd->driver->reset_device) {
-                               *status = hcd->driver->reset_device(hcd, udev);
-                               if (*status < 0) {
-                                       dev_err(&udev->dev, "Cannot reset "
-                                                       "HCD device state\n");
-                                       break;
-                               }
-                       }
-               }
-               /* FALL THROUGH */
-       case -ENOTCONN:
-       case -ENODEV:
-               clear_port_feature(hub->hdev,
-                               port1, USB_PORT_FEAT_C_RESET);
-               /* FIXME need disconnect() for NOTATTACHED device */
-               if (warm) {
-                       clear_port_feature(hub->hdev, port1,
-                                       USB_PORT_FEAT_C_BH_PORT_RESET);
-                       clear_port_feature(hub->hdev, port1,
-                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
-               } else {
-                       usb_set_device_state(udev, *status
-                                       ? USB_STATE_NOTATTACHED
-                                       : USB_STATE_DEFAULT);
-               }
-               break;
-       }
-}
-
 /* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */
 static int hub_port_reset(struct usb_hub *hub, int port1,
                        struct usb_device *udev, unsigned int delay, bool warm)
 {
        int i, status;
+       u16 portchange, portstatus;
 
-       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);
+       } else if (!warm) {
+               /*
+                * If the caller hasn't explicitly requested a warm reset,
+                * double check and see if one is needed.
+                */
+               if (hub_port_status(hub, port1, &portstatus, &portchange) == 0)
+                       if (hub_port_warm_reset_required(hub, portstatus))
+                               warm = true;
        }
 
        /* Reset the port */
@@ -2220,10 +2322,41 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                                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;
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_RESET);
+
+                       if (!hub_is_superspeed(hub->hdev))
+                               goto done;
+
+                       clear_port_feature(hub->hdev, port1,
+                                       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 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,
@@ -2237,7 +2370,27 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                port1);
 
 done:
-       if (!warm)
+       if (status == 0) {
+               /* 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);
+
+                       usb_set_device_state(udev, USB_STATE_DEFAULT);
+               }
+       } else {
+               if (udev)
+                       usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+       }
+
+       if (!hub_is_superspeed(hub->hdev))
                up_read(&ehci_cf_port_reset_rwsem);
 
        return status;
@@ -2419,6 +2572,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                                NULL, 0,
                                USB_CTRL_SET_TIMEOUT);
 
+               /* Try to enable USB2 hardware LPM again */
+               if (udev->usb2_hw_lpm_capable == 1)
+                       usb_set_usb2_hardware_lpm(udev, 1);
+
                /* System sleep transitions should never fail */
                if (!PMSG_IS_AUTO(msg))
                        status = 0;
@@ -2448,7 +2605,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 static int finish_port_resume(struct usb_device *udev)
 {
        int     status = 0;
-       u16     devstatus;
+       u16     devstatus = 0;
 
        /* caller owns the udev device lock */
        dev_dbg(&udev->dev, "%s\n",
@@ -2493,7 +2650,13 @@ static int finish_port_resume(struct usb_device *udev)
        if (status) {
                dev_dbg(&udev->dev, "gone after usb resume? status %d\n",
                                status);
-       } else if (udev->actconfig) {
+       /*
+        * There are a few quirky devices which violate the standard
+        * by claiming to have remote wakeup enabled after a reset,
+        * which crash if the feature is cleared, hence check for
+        * udev->reset_resume
+        */
+       } else if (udev->actconfig && !udev->reset_resume) {
                le16_to_cpus(&devstatus);
                if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) {
                        status = usb_control_msg(udev,
@@ -2513,6 +2676,43 @@ static int finish_port_resume(struct usb_device *udev)
        return status;
 }
 
+/*
+ * There are some SS USB devices which take longer time for link training.
+ * XHCI specs 4.19.4 says that when Link training is successful, port
+ * sets CSC bit to 1. So if SW reads port status before successful link
+ * training, then it will not find device to be present.
+ * USB Analyzer log with such buggy devices show that in some cases
+ * device switch on the RX termination after long delay of host enabling
+ * the VBUS. In few other cases it has been seen that device fails to
+ * negotiate link training in first attempt. It has been
+ * reported till now that few devices take as long as 2000 ms to train
+ * the link after host enabling its VBUS and termination. Following
+ * routine implements a 2000 ms timeout for link training. If in a case
+ * link trains before timeout, loop will exit earlier.
+ *
+ * FIXME: If a device was connected before suspend, but was removed
+ * while system was asleep, then the loop in the following routine will
+ * only exit at timeout.
+ *
+ * This routine should only be called when persist is enabled for a SS
+ * device.
+ */
+static int wait_for_ss_port_enable(struct usb_device *udev,
+               struct usb_hub *hub, int *port1,
+               u16 *portchange, u16 *portstatus)
+{
+       int status = 0, delay_ms = 0;
+
+       while (delay_ms < 2000) {
+               if (status || *portstatus & USB_PORT_STAT_CONNECTION)
+                       break;
+               msleep(20);
+               delay_ms += 20;
+               status = hub_port_status(hub, *port1, portstatus, portchange);
+       }
+       return status;
+}
+
 /*
  * usb_port_resume - re-activate a suspended usb device's upstream port
  * @udev: device to re-activate, not a root hub
@@ -2605,6 +2805,10 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
        clear_bit(port1, hub->busy_bits);
 
+       if (udev->persist_enabled && hub_is_superspeed(hub->hdev))
+               status = wait_for_ss_port_enable(udev, hub, &port1, &portchange,
+                               &portstatus);
+
        status = check_port_resume_type(udev,
                        hub, port1, status, portchange, portstatus);
        if (status == 0)
@@ -2854,7 +3058,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
        struct usb_device       *hdev = hub->hdev;
        struct usb_hcd          *hcd = bus_to_hcd(hdev->bus);
-       int                     i, j, retval;
+       int                     retries, operations, retval, i;
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
        const char              *speed;
@@ -2956,7 +3160,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * first 8 bytes of the device descriptor to get the ep0 maxpacket
         * value.
         */
-       for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
+       for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {
                if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
                        struct usb_device_descriptor *buf;
                        int r = 0;
@@ -2972,7 +3176,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                         * 255 is for WUSB devices, we actually need to use
                         * 512 (WUSB1.0[4.8.1]).
                         */
-                       for (j = 0; j < 3; ++j) {
+                       for (operations = 0; operations < 3; ++operations) {
                                buf->bMaxPacketSize0 = 0;
                                r = usb_control_msg(udev, usb_rcvaddr0pipe(),
                                        USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
@@ -2992,7 +3196,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                                r = -EPROTO;
                                        break;
                                }
-                               if (r == 0)
+                               /*
+                                * Some devices time out if they are powered on
+                                * when already connected. They need a second
+                                * reset. But only on the first attempt,
+                                * lest we get into a time out/reset loop
+                                */
+                               if (r == 0  || (r == -ETIMEDOUT && retries == 0))
                                        break;
                        }
                        udev->descriptor.bMaxPacketSize0 =
@@ -3024,7 +3234,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                 * authorization will assign the final address.
                 */
                if (udev->wusb == 0) {
-                       for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
+                       for (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {
                                retval = hub_set_address(udev, devnum);
                                if (retval >= 0)
                                        break;
@@ -3068,6 +3278,22 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (retval)
                goto fail;
 
+       /*
+        * Some superspeed devices have finished the link training process
+        * and attached to a superspeed hub port, but the device descriptor
+        * got from those devices show they aren't superspeed devices. Warm
+        * reset the port attached by the devices can fix them.
+        */
+       if ((udev->speed == USB_SPEED_SUPER) &&
+                       (le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
+               dev_err(&udev->dev, "got a wrong device descriptor, "
+                               "warm reset device\n");
+               hub_port_reset(hub, port1, udev,
+                               HUB_BH_RESET_TIME, true);
+               retval = -EINVAL;
+               goto fail;
+       }
+
        if (udev->descriptor.bMaxPacketSize0 == 0xff ||
                        udev->speed == USB_SPEED_SUPER)
                i = 512;
@@ -3201,6 +3427,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        le16_to_cpu(hub->descriptor->wHubCharacteristics);
        struct usb_device *udev;
        int status, i;
+       static int unreliable_port = -1;
 
        dev_dbg (hub_dev,
                "port %d, status %04x, change %04x, %s\n",
@@ -3262,10 +3489,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                                USB_PORT_STAT_C_ENABLE)) {
                status = hub_port_debounce(hub, port1);
                if (status < 0) {
-                       if (printk_ratelimit())
+                       if (port1 != unreliable_port && printk_ratelimit())
                                dev_err(hub_dev, "connect-debounce failed, "
                                                "port %d disabled\n", port1);
                        portstatus &= ~USB_PORT_STAT_CONNECTION;
+                       unreliable_port = port1;
                } else {
                        portstatus = status;
                }
@@ -3455,9 +3683,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",
@@ -3625,12 +3854,26 @@ static void hub_events(void)
                        /* Warm reset a USB3 protocol port if it's in
                         * SS.Inactive state.
                         */
-                       if (hub_is_superspeed(hub->hdev) &&
-                               (portstatus & USB_PORT_STAT_LINK_STATE)
-                                       == USB_SS_PORT_LS_SS_INACTIVE) {
+                       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);
-                               hub_port_reset(hub, i, NULL,
-                                               HUB_BH_RESET_TIME, true);
+                               if (!udev ||
+                                   !(portstatus & USB_PORT_STAT_CONNECTION) ||
+                                   udev->state == USB_STATE_NOTATTACHED) {
+                                       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;
+                               }
                        }
 
                        if (connect_change)
@@ -3678,6 +3921,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) */
@@ -3887,6 +4131,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        }
        parent_hub = hdev_to_hub(parent_hdev);
 
+       /* Disable USB2 hardware LPM.
+        * It will be re-enabled by the enumeration process.
+        */
+       if (udev->usb2_hw_lpm_enabled == 1)
+               usb_set_usb2_hardware_lpm(udev, 0);
+
        set_bit(port1, parent_hub->busy_bits);
        for (i = 0; i < SET_CONFIG_TRIES; ++i) {