usbcore: refine warm reset logic
[linux-flexiantxendom0.git] / drivers / usb / core / hub.c
index 5ac27ed..aa336f7 100644 (file)
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
-#include <linux/pm_runtime.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -82,6 +82,10 @@ struct usb_hub {
        void                    **port_owners;
 };
 
+static inline int hub_is_superspeed(struct usb_device *hdev)
+{
+       return (hdev->descriptor.bDeviceProtocol == 3);
+}
 
 /* Protect struct usb_device->state and ->children members
  * Note: Both are also protected by ->dev.sem, except that ->state can
@@ -151,14 +155,14 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
 
 static int usb_reset_and_verify_device(struct usb_device *udev);
 
-static inline char *portspeed(int portstatus)
+static inline char *portspeed(struct usb_hub *hub, int portstatus)
 {
+       if (hub_is_superspeed(hub->hdev))
+               return "5.0 Gb/s";
        if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                return "480 Mb/s";
        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                return "1.5 Mb/s";
-       else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
-               return "5.0 Gb/s";
        else
                return "12 Mb/s";
 }
@@ -172,14 +176,23 @@ 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, int size)
+static int get_hub_descriptor(struct usb_device *hdev, void *data)
 {
-       int i, ret;
+       int i, ret, size;
+       unsigned dtype;
+
+       if (hub_is_superspeed(hdev)) {
+               dtype = USB_DT_SS_HUB;
+               size = USB_DT_SS_HUB_SIZE;
+       } else {
+               dtype = USB_DT_HUB;
+               size = sizeof(struct usb_hub_descriptor);
+       }
 
        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,
-                       USB_DT_HUB << 8, 0, data, size,
+                       dtype << 8, 0, data, size,
                        USB_CTRL_GET_TIMEOUT);
                if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
                        return ret;
@@ -326,7 +339,8 @@ static int get_hub_status(struct usb_device *hdev,
 {
        int i, status = -ETIMEDOUT;
 
-       for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+       for (i = 0; i < USB_STS_RETRIES &&
+                       (status == -ETIMEDOUT || status == -EPIPE); i++) {
                status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
                        data, sizeof(*data), USB_STS_TIMEOUT);
@@ -342,7 +356,8 @@ static int get_port_status(struct usb_device *hdev, int port1,
 {
        int i, status = -ETIMEDOUT;
 
-       for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+       for (i = 0; i < USB_STS_RETRIES &&
+                       (status == -ETIMEDOUT || status == -EPIPE); i++) {
                status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
                        data, sizeof(*data), USB_STS_TIMEOUT);
@@ -365,6 +380,7 @@ static int hub_port_status(struct usb_hub *hub, int port1,
        } else {
                *status = le16_to_cpu(hub->status->port.wPortStatus);
                *change = le16_to_cpu(hub->status->port.wPortChange);
+
                ret = 0;
        }
        mutex_unlock(&hub->status_mutex);
@@ -607,7 +623,7 @@ 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)
+       if (!hub->error && !hub_is_superspeed(hub->hdev))
                ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
        if (ret)
                dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
@@ -616,7 +632,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 }
 
 /*
- * Disable a port and mark a logical connnect-change event, so that some
+ * Disable a port and mark a logical connect-change event, so that some
  * time later khubd will disconnect() any existing usb_device on the port
  * and will re-enumerate if there actually is a device attached.
  */
@@ -676,6 +692,8 @@ static void hub_init_func3(struct work_struct *ws);
 static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 {
        struct usb_device *hdev = hub->hdev;
+       struct usb_hcd *hcd;
+       int ret;
        int port1;
        int status;
        bool need_debounce_delay = false;
@@ -714,6 +732,25 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        usb_autopm_get_interface_no_resume(
                                        to_usb_interface(hub->intfdev));
                        return;         /* Continues at init2: below */
+               } else if (type == HUB_RESET_RESUME) {
+                       /* The internal host controller state for the hub device
+                        * may be gone after a host power loss on system resume.
+                        * Update the device's info so the HW knows it's a hub.
+                        */
+                       hcd = bus_to_hcd(hdev->bus);
+                       if (hcd->driver->update_hub_device) {
+                               ret = hcd->driver->update_hub_device(hcd, hdev,
+                                               &hub->tt, GFP_NOIO);
+                               if (ret < 0) {
+                                       dev_err(hub->intfdev, "Host not "
+                                                       "accepting hub info "
+                                                       "update.\n");
+                                       dev_err(hub->intfdev, "LS/FS devices "
+                                                       "and hubs may not work "
+                                                       "under this hub\n.");
+                               }
+                       }
+                       hub_power_on(hub, true);
                } else {
                        hub_power_on(hub, true);
                }
@@ -744,8 +781,19 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                                !(portstatus & USB_PORT_STAT_CONNECTION) ||
                                !udev ||
                                udev->state == USB_STATE_NOTATTACHED)) {
-                       clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
-                       portstatus &= ~USB_PORT_STAT_ENABLE;
+                       /*
+                        * USB3 protocol ports will automatically transition
+                        * to Enabled state when detect an USB3.0 device attach.
+                        * Do not disable USB3 protocol ports.
+                        */
+                       if (!hub_is_superspeed(hdev)) {
+                               clear_port_feature(hdev, port1,
+                                                  USB_PORT_FEAT_ENABLE);
+                               portstatus &= ~USB_PORT_STAT_ENABLE;
+                       } else {
+                               /* Pretend that power was lost for USB3 devs */
+                               portstatus &= ~USB_PORT_STAT_ENABLE;
+                       }
                }
 
                /* Clear status-change flags; we'll debounce later */
@@ -759,6 +807,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_ENABLE);
                }
+               if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+                       need_debounce_delay = true;
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+               }
 
                /* We can forget about a "removed" device when there's a
                 * physical disconnect or the connect status changes.
@@ -928,12 +981,23 @@ static int hub_configure(struct usb_hub *hub,
                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.
         */
-       ret = get_hub_descriptor(hdev, hub->descriptor,
-                       sizeof(*hub->descriptor));
+       ret = get_hub_descriptor(hdev, hub->descriptor);
        if (ret < 0) {
                message = "can't read hub descriptor";
                goto fail;
@@ -955,12 +1019,14 @@ static int hub_configure(struct usb_hub *hub,
 
        wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
-       if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
+       /* FIXME for USB 3.0, skip for now */
+       if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
+                       !(hub_is_superspeed(hdev))) {
                int     i;
                char    portstr [USB_MAXCHILDREN + 1];
 
                for (i = 0; i < hdev->maxchild; i++)
-                       portstr[i] = hub->descriptor->DeviceRemovable
+                       portstr[i] = hub->descriptor->u.hs.DeviceRemovable
                                    [((i + 1) / 8)] & (1 << ((i + 1) % 8))
                                ? 'F' : 'R';
                portstr[hdev->maxchild] = 0;
@@ -1217,8 +1283,14 @@ 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_enable_autosuspend(hdev);
+       /* Hubs have proper suspend/resume support.  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->level == MAX_TOPO_LEVEL) {
                dev_err(&intf->dev,
@@ -1282,6 +1354,7 @@ descriptor_error:
        return -ENODEV;
 }
 
+/* No BKL needed */
 static int
 hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
 {
@@ -1428,6 +1501,7 @@ void usb_set_device_state(struct usb_device *udev,
                enum usb_device_state new_state)
 {
        unsigned long flags;
+       int wakeup = -1;
 
        spin_lock_irqsave(&device_state_lock, flags);
        if (udev->state == USB_STATE_NOTATTACHED)
@@ -1442,11 +1516,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)
-                               device_set_wakeup_capable(&udev->dev,
-                                       (udev->actconfig->desc.bmAttributes
-                                        & USB_CONFIG_ATT_WAKEUP));
+                               wakeup = udev->actconfig->desc.bmAttributes
+                                        & USB_CONFIG_ATT_WAKEUP;
                        else
-                               device_set_wakeup_capable(&udev->dev, 0);
+                               wakeup = 0;
                }
                if (udev->state == USB_STATE_SUSPENDED &&
                        new_state != USB_STATE_SUSPENDED)
@@ -1458,10 +1531,19 @@ void usb_set_device_state(struct usb_device *udev,
        } else
                recursively_mark_NOTATTACHED(udev);
        spin_unlock_irqrestore(&device_state_lock, flags);
+       if (wakeup >= 0)
+               device_set_wakeup_capable(&udev->dev, wakeup);
 }
 EXPORT_SYMBOL_GPL(usb_set_device_state);
 
 /*
+ * Choose a device number.
+ *
+ * Device numbers are used as filenames in usbfs.  On USB-1.1 and
+ * USB-2.0 buses they are also used as device addresses, however on
+ * USB-3.0 buses the address is assigned by the controller hardware
+ * and it usually is not the same as the device number.
+ *
  * WUSB devices are simple: they have no hubs behind, so the mapping
  * device <-> virtual port number becomes 1:1. Why? to simplify the
  * life of the device connection logic in
@@ -1483,7 +1565,7 @@ EXPORT_SYMBOL_GPL(usb_set_device_state);
  * the HCD must setup data structures before issuing a set address
  * command to the hardware.
  */
-static void choose_address(struct usb_device *udev)
+static void choose_devnum(struct usb_device *udev)
 {
        int             devnum;
        struct usb_bus  *bus = udev->bus;
@@ -1508,7 +1590,7 @@ static void choose_address(struct usb_device *udev)
        }
 }
 
-static void release_address(struct usb_device *udev)
+static void release_devnum(struct usb_device *udev)
 {
        if (udev->devnum > 0) {
                clear_bit(udev->devnum, udev->bus->devmap.devicemap);
@@ -1516,7 +1598,7 @@ static void release_address(struct usb_device *udev)
        }
 }
 
-static void update_address(struct usb_device *udev, int devnum)
+static void update_devnum(struct usb_device *udev, int devnum)
 {
        /* The address for a WUSB device is managed by wusbcore. */
        if (!udev->wusb)
@@ -1552,18 +1634,15 @@ void usb_disconnect(struct usb_device **pdev)
 {
        struct usb_device       *udev = *pdev;
        int                     i;
-
-       if (!udev) {
-               pr_debug ("%s nodev\n", __func__);
-               return;
-       }
+       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.
-        * this quiesces everyting except pending urbs.
+        * this quiesces everything except pending urbs.
         */
        usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-       dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum);
+       dev_info(&udev->dev, "USB disconnect, device number %d\n",
+                       udev->devnum);
 
        usb_lock_device(udev);
 
@@ -1578,7 +1657,9 @@ 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);
@@ -1593,7 +1674,7 @@ void usb_disconnect(struct usb_device **pdev)
        /* Free the device number and delete the parent's children[]
         * (or root_hub) pointer.
         */
-       release_address(udev);
+       release_devnum(udev);
 
        /* Avoid races with recursively_mark_NOTATTACHED() */
        spin_lock_irq(&device_state_lock);
@@ -1783,14 +1864,19 @@ int usb_new_device(struct usb_device *udev)
                 * sysfs power/wakeup controls wakeup enabled/disabled
                 */
                device_init_wakeup(&udev->dev, 0);
-               device_set_wakeup_enable(&udev->dev, 1);
        }
 
        /* Tell the runtime-PM framework the device is active */
        pm_runtime_set_active(&udev->dev);
+       pm_runtime_get_noresume(&udev->dev);
+       pm_runtime_use_autosuspend(&udev->dev);
        pm_runtime_enable(&udev->dev);
 
-       usb_detect_quirks(udev);
+       /* By default, forbid autosuspend for all devices.  It will be
+        * allowed for hubs during binding.
+        */
+       usb_disable_autosuspend(udev);
+
        err = usb_enumerate_device(udev);       /* Read descriptors */
        if (err < 0)
                goto fail;
@@ -1816,6 +1902,8 @@ int usb_new_device(struct usb_device *udev)
        }
 
        (void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
+       usb_mark_last_busy(udev);
+       pm_runtime_put_sync_autosuspend(&udev->dev);
        return err;
 
 fail:
@@ -1937,11 +2025,12 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 
 #define HUB_ROOT_RESET_TIME    50      /* times are in msec */
 #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_wait_reset(struct usb_hub *hub, int port1,
-                               struct usb_device *udev, unsigned int delay)
+                       struct usb_device *udev, unsigned int delay, bool warm)
 {
        int delay_time, ret;
        u16 portstatus;
@@ -1958,26 +2047,39 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (ret < 0)
                        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 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 (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;
+               /*
+                * Some buggy devices require a warm reset to be issued even
+                * when the port appears not to be connected.
+                */
+               if (!warm) {
+                       /* 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 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)
+                               return 0;
                }
 
                /* switch to the long delay after two short delay failures */
@@ -1985,35 +2087,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                        delay = HUB_LONG_RESET_TIME;
 
                dev_dbg (hub->intfdev,
-                       "port %d not reset yet, waiting %dms\n",
-                       port1, delay);
+                       "port %d not %sreset yet, waiting %dms\n",
+                       port1, warm ? "warm " : "", delay);
        }
 
        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)
+                       struct usb_device *udev, unsigned int delay, bool warm)
 {
        int i, status;
-       struct usb_hcd *hcd;
 
-       hcd = bus_to_hcd(udev->bus);
-       /* 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);
+       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)) {
+                       dev_err(hub->intfdev, "only USB3 hub support "
+                                               "warm reset\n");
+                       return -EINVAL;
+               }
+       }
 
        /* Reset the port */
        for (i = 0; i < PORT_RESET_TRIES; i++) {
-               status = set_port_feature(hub->hdev,
-                               port1, USB_PORT_FEAT_RESET);
-               if (status)
+               status = set_port_feature(hub->hdev, port1, (warm ?
+                                       USB_PORT_FEAT_BH_PORT_RESET :
+                                       USB_PORT_FEAT_RESET));
+               if (status) {
                        dev_err(hub->intfdev,
-                                       "cannot reset port %d (err = %d)\n",
-                                       port1, status);
-               else {
-                       status = hub_port_wait_reset(hub, port1, udev, delay);
+                                       "cannot %sreset port %d (err = %d)\n",
+                                       warm ? "warm " : "", port1, status);
+               } else {
+                       status = hub_port_wait_reset(hub, port1, udev, delay,
+                                                               warm);
                        if (status && status != -ENOTCONN)
                                dev_dbg(hub->intfdev,
                                                "port_wait_reset: err = %d\n",
@@ -2021,34 +2172,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                }
 
                /* return on disconnect or reset */
-               switch (status) {
-               case 0:
-                       /* TRSTRCY = 10 ms; plus some extra */
-                       msleep(10 + 40);
-                       update_address(udev, 0);
-                       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 */
-                       usb_set_device_state(udev, status
-                                       ? USB_STATE_NOTATTACHED
-                                       : USB_STATE_DEFAULT);
+               if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
+                       hub_port_finish_reset(hub, port1, udev, &status, warm);
                        goto done;
                }
 
                dev_dbg (hub->intfdev,
-                       "port %d not enabled, trying reset again...\n",
-                       port1);
+                       "port %d not enabled, trying %sreset again...\n",
+                       port1, warm ? "warm " : "");
                delay = HUB_LONG_RESET_TIME;
        }
 
@@ -2056,16 +2187,47 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                "Cannot enable port %i.  Maybe the USB cable is bad?\n",
                port1);
 
- done:
-       up_read(&ehci_cf_port_reset_rwsem);
+done:
+       if (!warm)
+               up_read(&ehci_cf_port_reset_rwsem);
+
        return status;
 }
 
+/* Check if a port is power on */
+static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
+{
+       int ret = 0;
+
+       if (hub_is_superspeed(hub->hdev)) {
+               if (portstatus & USB_SS_PORT_STAT_POWER)
+                       ret = 1;
+       } else {
+               if (portstatus & USB_PORT_STAT_POWER)
+                       ret = 1;
+       }
+
+       return ret;
+}
+
 #ifdef CONFIG_PM
 
-#define MASK_BITS      (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \
-                               USB_PORT_STAT_SUSPEND)
-#define WANT_BITS      (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION)
+/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
+static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
+{
+       int ret = 0;
+
+       if (hub_is_superspeed(hub->hdev)) {
+               if ((portstatus & USB_PORT_STAT_LINK_STATE)
+                               == USB_SS_PORT_LS_U3)
+                       ret = 1;
+       } else {
+               if (portstatus & USB_PORT_STAT_SUSPEND)
+                       ret = 1;
+       }
+
+       return ret;
+}
 
 /* Determine whether the device on a port is ready for a normal resume,
  * is ready for a reset-resume, or should be disconnected.
@@ -2075,7 +2237,9 @@ static int check_port_resume_type(struct usb_device *udev,
                int status, unsigned portchange, unsigned portstatus)
 {
        /* Is the device still present? */
-       if (status || (portstatus & MASK_BITS) != WANT_BITS) {
+       if (status || port_is_suspended(hub, portstatus) ||
+                       !port_is_power_on(hub, portstatus) ||
+                       !(portstatus & USB_PORT_STAT_CONNECTION)) {
                if (status >= 0)
                        status = -ENODEV;
        }
@@ -2186,7 +2350,13 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
        }
 
        /* see 7.1.7.6 */
-       status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND);
+       if (hub_is_superspeed(hub->hdev))
+               status = set_port_feature(hub->hdev,
+                               port1 | (USB_SS_PORT_LS_U3 << 3),
+                               USB_PORT_FEAT_LINK_STATE);
+       else
+               status = set_port_feature(hub->hdev, port1,
+                                               USB_PORT_FEAT_SUSPEND);
        if (status) {
                dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
                                port1, status);
@@ -2197,6 +2367,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                                USB_DEVICE_REMOTE_WAKEUP, 0,
                                NULL, 0,
                                USB_CTRL_SET_TIMEOUT);
+
+               /* System sleep transitions should never fail */
+               if (!(msg.event & PM_EVENT_AUTO))
+                       status = 0;
        } else {
                /* device has up to 10 msec to fully suspend */
                dev_dbg(&udev->dev, "usb %ssuspend\n",
@@ -2204,6 +2378,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                usb_set_device_state(udev, USB_STATE_SUSPENDED);
                msleep(10);
        }
+       usb_mark_last_busy(hub->hdev);
        return status;
 }
 
@@ -2329,7 +2504,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
        /* Skip the initial Clear-Suspend step for a remote wakeup */
        status = hub_port_status(hub, port1, &portstatus, &portchange);
-       if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND))
+       if (status == 0 && !port_is_suspended(hub, portstatus))
                goto SuspendCleared;
 
        // dev_dbg(hub->intfdev, "resume port %d\n", port1);
@@ -2337,8 +2512,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        set_bit(port1, hub->busy_bits);
 
        /* see 7.1.7.7; affects power usage, but not budgeting */
-       status = clear_port_feature(hub->hdev,
-                       port1, USB_PORT_FEAT_SUSPEND);
+       if (hub_is_superspeed(hub->hdev))
+               status = set_port_feature(hub->hdev,
+                               port1 | (USB_SS_PORT_LS_U0 << 3),
+                               USB_PORT_FEAT_LINK_STATE);
+       else
+               status = clear_port_feature(hub->hdev,
+                               port1, USB_PORT_FEAT_SUSPEND);
        if (status) {
                dev_dbg(hub->intfdev, "can't resume port %d, status %d\n",
                                port1, status);
@@ -2360,9 +2540,15 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
  SuspendCleared:
        if (status == 0) {
-               if (portchange & USB_PORT_STAT_C_SUSPEND)
-                       clear_port_feature(hub->hdev, port1,
-                                       USB_PORT_FEAT_C_SUSPEND);
+               if (hub_is_superspeed(hub->hdev)) {
+                       if (portchange & USB_PORT_STAT_C_LINK_STATE)
+                               clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+               } else {
+                       if (portchange & USB_PORT_STAT_C_SUSPEND)
+                               clear_port_feature(hub->hdev, port1,
+                                               USB_PORT_FEAT_C_SUSPEND);
+               }
        }
 
        clear_bit(port1, hub->busy_bits);
@@ -2434,16 +2620,15 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
        struct usb_device       *hdev = hub->hdev;
        unsigned                port1;
 
-       /* fail if children aren't already suspended */
+       /* Warn if children aren't already suspended */
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
                struct usb_device       *udev;
 
                udev = hdev->children [port1-1];
                if (udev && udev->can_submit) {
-                       if (!(msg.event & PM_EVENT_AUTO))
-                               dev_dbg(&intf->dev, "port %d nyet suspended\n",
-                                               port1);
-                       return -EBUSY;
+                       dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
+                       if (msg.event & PM_EVENT_AUTO)
+                               return -EBUSY;
                }
        }
 
@@ -2580,16 +2765,14 @@ static int hub_set_address(struct usb_device *udev, int devnum)
                return 0;
        if (udev->state != USB_STATE_DEFAULT)
                return -EINVAL;
-       if (hcd->driver->address_device) {
+       if (hcd->driver->address_device)
                retval = hcd->driver->address_device(hcd, udev);
-       } else {
+       else
                retval = usb_control_msg(udev, usb_sndaddr0pipe(),
                                USB_REQ_SET_ADDRESS, 0, devnum, 0,
                                NULL, 0, USB_CTRL_SET_TIMEOUT);
-               if (retval == 0)
-                       update_address(udev, devnum);
-       }
        if (retval == 0) {
+               update_devnum(udev, devnum);
                /* Device now using proper address. */
                usb_set_device_state(udev, USB_STATE_ADDRESS);
                usb_ep0_reinit(udev);
@@ -2617,7 +2800,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        int                     i, j, retval;
        unsigned                delay = HUB_SHORT_RESET_TIME;
        enum usb_device_speed   oldspeed = udev->speed;
-       char                    *speed, *type;
+       const char              *speed;
        int                     devnum = udev->devnum;
 
        /* root hub ports have a slightly longer reset period
@@ -2636,17 +2819,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
        mutex_lock(&usb_address0_mutex);
 
-       if (!udev->config && oldspeed == USB_SPEED_SUPER) {
-               /* Don't reset USB 3.0 devices during an initial setup */
-               usb_set_device_state(udev, USB_STATE_DEFAULT);
-       } else {
-               /* Reset the device; full speed may morph to high speed */
-               /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
-               retval = hub_port_reset(hub, port1, udev, delay);
-               if (retval < 0)         /* error or disconnect */
-                       goto fail;
-               /* success, speed is known */
-       }
+       /* Reset the device; full speed may morph to high speed */
+       /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
+       retval = hub_port_reset(hub, port1, udev, delay, false);
+       if (retval < 0)         /* error or disconnect */
+               goto fail;
+       /* success, speed is known */
+
        retval = -ENODEV;
 
        if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
@@ -2681,26 +2860,17 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        default:
                goto fail;
        }
-       type = "";
-       switch (udev->speed) {
-       case USB_SPEED_LOW:     speed = "low";  break;
-       case USB_SPEED_FULL:    speed = "full"; break;
-       case USB_SPEED_HIGH:    speed = "high"; break;
-       case USB_SPEED_SUPER:
-                               speed = "super";
-                               break;
-       case USB_SPEED_WIRELESS:
-                               speed = "variable";
-                               type = "Wireless ";
-                               break;
-       default:                speed = "?";    break;
-       }
+
+       if (udev->speed == USB_SPEED_WIRELESS)
+               speed = "variable speed Wireless";
+       else
+               speed = usb_speed_string(udev->speed);
+
        if (udev->speed != USB_SPEED_SUPER)
                dev_info(&udev->dev,
-                               "%s %s speed %sUSB device using %s and address %d\n",
-                               (udev->config) ? "reset" : "new", speed, type,
-                               udev->bus->controller->driver->name, devnum);
+                               "%s %s USB device number %d using %s\n",
+                               (udev->config) ? "reset" : "new", speed,
+                               devnum, udev->bus->controller->driver->name);
 
        /* Set up TT records, if needed  */
        if (hdev->tt) {
@@ -2708,6 +2878,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                udev->ttport = hdev->ttport;
        } else if (udev->speed != USB_SPEED_HIGH
                        && hdev->speed == USB_SPEED_HIGH) {
+               if (!hub->tt.hub) {
+                       dev_err(&udev->dev, "parent hub has no TT\n");
+                       retval = -EINVAL;
+                       goto fail;
+               }
                udev->tt = &hub->tt;
                udev->ttport = port1;
        }
@@ -2725,10 +2900,6 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
         * value.
         */
        for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
-               /*
-                * An xHCI controller cannot send any packets to a device until
-                * a set address command successfully completes.
-                */
                if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
                        struct usb_device_descriptor *buf;
                        int r = 0;
@@ -2771,7 +2942,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                                        buf->bMaxPacketSize0;
                        kfree(buf);
 
-                       retval = hub_port_reset(hub, port1, udev, delay);
+                       retval = hub_port_reset(hub, port1, udev, delay, false);
                        if (retval < 0)         /* error or disconnect */
                                goto fail;
                        if (oldspeed != udev->speed) {
@@ -2811,9 +2982,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                        if (udev->speed == USB_SPEED_SUPER) {
                                devnum = udev->devnum;
                                dev_info(&udev->dev,
-                                               "%s SuperSpeed USB device using %s and address %d\n",
+                                               "%s SuperSpeed USB device number %d using %s\n",
                                                (udev->config) ? "reset" : "new",
-                                               udev->bus->controller->driver->name, devnum);
+                                               devnum, udev->bus->controller->driver->name);
                        }
 
                        /* cope with hardware quirkiness:
@@ -2845,14 +3016,17 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                i = 512;
        else
                i = udev->descriptor.bMaxPacketSize0;
-       if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
-               if (udev->speed != USB_SPEED_FULL ||
+       if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
+               if (udev->speed == USB_SPEED_LOW ||
                                !(i == 8 || i == 16 || i == 32 || i == 64)) {
-                       dev_err(&udev->dev, "ep0 maxpacket = %d\n", i);
+                       dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
                        retval = -EMSGSIZE;
                        goto fail;
                }
-               dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
+               if (udev->speed == USB_SPEED_FULL)
+                       dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
+               else
+                       dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
                usb_ep0_reinit(udev);
        }
@@ -2867,11 +3041,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        }
 
        retval = 0;
-
+       /* notify HCD that we have a device connected and addressed */
+       if (hcd->driver->update_device)
+               hcd->driver->update_device(hcd, udev);
 fail:
        if (retval) {
                hub_port_disable(hub, port1, 0);
-               update_address(udev, devnum);   /* for disconnect processing */
+               update_devnum(udev, devnum);    /* for disconnect processing */
        }
        mutex_unlock(&usb_address0_mutex);
        return retval;
@@ -2962,7 +3138,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 
        dev_dbg (hub_dev,
                "port %d, status %04x, change %04x, %s\n",
-               port1, portstatus, portchange, portspeed (portstatus));
+               port1, portstatus, portchange, portspeed(hub, portstatus));
 
        if (hub->has_indicators) {
                set_port_led(hub, port1, HUB_LED_AUTO);
@@ -3037,7 +3213,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 
                /* maybe switch power back on (e.g. root hub was reset) */
                if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
-                               && !(portstatus & (1 << USB_PORT_FEAT_POWER)))
+                               && !port_is_power_on(hub, portstatus))
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
                if (portstatus & USB_PORT_STAT_ENABLE)
@@ -3063,34 +3239,16 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                udev->level = hdev->level + 1;
                udev->wusb = hub_is_wusb(hub);
 
-               /*
-                * USB 3.0 devices are reset automatically before the connect
-                * port status change appears, and the root hub port status
-                * shows the correct speed.  We also get port change
-                * notifications for USB 3.0 devices from the USB 3.0 portion of
-                * an external USB 3.0 hub, but this isn't handled correctly yet
-                * FIXME.
-                */
-
-               if (!(hcd->driver->flags & HCD_USB3))
-                       udev->speed = USB_SPEED_UNKNOWN;
-               else if ((hdev->parent == NULL) &&
-                               (portstatus & USB_PORT_STAT_SUPER_SPEED))
+               /* Only USB 3.0 devices are connected to SuperSpeed hubs. */
+               if (hub_is_superspeed(hub->hdev))
                        udev->speed = USB_SPEED_SUPER;
                else
                        udev->speed = USB_SPEED_UNKNOWN;
 
-               /*
-                * xHCI needs to issue an address device command later
-                * in the hub_port_init sequence for SS/HS/FS/LS devices.
-                */
-               if (!(hcd->driver->flags & HCD_USB3)) {
-                       /* set the address */
-                       choose_address(udev);
-                       if (udev->devnum <= 0) {
-                               status = -ENOTCONN;     /* Don't retry */
-                               goto loop;
-                       }
+               choose_devnum(udev);
+               if (udev->devnum <= 0) {
+                       status = -ENOTCONN;     /* Don't retry */
+                       goto loop;
                }
 
                /* reset (non-USB 3.0 devices) and get descriptor */
@@ -3098,6 +3256,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                if (status < 0)
                        goto loop;
 
+               usb_detect_quirks(udev);
+               if (udev->quirks & USB_QUIRK_DELAY_INIT)
+                       msleep(1000);
+
                /* consecutive bus-powered hubs aren't reliable; they can
                 * violate the voltage drop budget.  if the new child has
                 * a "powered" LED, users should notice we didn't enable it
@@ -3175,7 +3337,7 @@ loop_disable:
                hub_port_disable(hub, port1, 1);
 loop:
                usb_ep0_reinit(udev);
-               release_address(udev);
+               release_devnum(udev);
                hub_free_dev(udev);
                usb_put_dev(udev);
                if ((status == -ENOTCONN) || (status == -ENOTSUPP))
@@ -3352,12 +3514,19 @@ static void hub_events(void)
                        }
                        
                        if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
-                               dev_err (hub_dev,
-                                       "over-current change on port %d\n",
-                                       i);
+                               u16 status = 0;
+                               u16 unused;
+
+                               dev_dbg(hub_dev, "over-current change on port "
+                                       "%d\n", i);
                                clear_port_feature(hdev, i,
                                        USB_PORT_FEAT_C_OVER_CURRENT);
+                               msleep(100);    /* Cool down */
                                hub_power_on(hub, true);
+                               hub_port_status(hub, i, &status, &unused);
+                               if (status & USB_PORT_STAT_OVERCURRENT)
+                                       dev_err(hub_dev, "over-current "
+                                               "condition on port %d\n", i);
                        }
 
                        if (portchange & USB_PORT_STAT_C_RESET) {
@@ -3367,6 +3536,36 @@ static void hub_events(void)
                                clear_port_feature(hdev, i,
                                        USB_PORT_FEAT_C_RESET);
                        }
+                       if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
+                                       hub_is_superspeed(hub->hdev)) {
+                               dev_dbg(hub_dev,
+                                       "warm reset change on port %d\n",
+                                       i);
+                               clear_port_feature(hdev, i,
+                                       USB_PORT_FEAT_C_BH_PORT_RESET);
+                       }
+                       if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+                               clear_port_feature(hub->hdev, i,
+                                               USB_PORT_FEAT_C_PORT_LINK_STATE);
+                       }
+                       if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
+                               dev_warn(hub_dev,
+                                       "config error on port %d\n",
+                                       i);
+                               clear_port_feature(hub->hdev, i,
+                                               USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
+                       }
+
+                       /* 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) {
+                               dev_dbg(hub_dev, "warm reset port %d\n", i);
+                               hub_port_reset(hub, i, NULL,
+                                               HUB_BH_RESET_TIME, true);
+                       }
 
                        if (connect_change)
                                hub_port_connect_change(hub, i,
@@ -3389,10 +3588,17 @@ static void hub_events(void)
                                        hub->limited_power = 0;
                        }
                        if (hubchange & HUB_CHANGE_OVERCURRENT) {
-                               dev_dbg (hub_dev, "overcurrent change\n");
-                               msleep(500);    /* Cool down */
+                               u16 status = 0;
+                               u16 unused;
+
+                               dev_dbg(hub_dev, "over-current change\n");
                                clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
+                               msleep(500);    /* Cool down */
                                hub_power_on(hub, true);
+                               hub_hub_status(hub, &status, &unused);
+                               if (status & HUB_STATUS_OVERCURRENT)
+                                       dev_err(hub_dev, "over-current "
+                                               "condition\n");
                        }
                }
 
@@ -3450,7 +3656,7 @@ static struct usb_driver hub_driver = {
        .reset_resume = hub_reset_resume,
        .pre_reset =    hub_pre_reset,
        .post_reset =   hub_post_reset,
-       .ioctl =        hub_ioctl,
+       .unlocked_ioctl = hub_ioctl,
        .id_table =     hub_id_table,
        .supports_autosuspend = 1,
 };
@@ -3609,7 +3815,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        }
 
        if (!parent_hdev) {
-               /* this requires hcd-specific logic; see OHCI hc_restart() */
+               /* this requires hcd-specific logic; see ohci_restart() */
                dev_dbg(&udev->dev, "%s for root hub!\n", __func__);
                return -EISDIR;
        }
@@ -3641,13 +3847,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        if (!udev->actconfig)
                goto done;
 
-       mutex_lock(&hcd->bandwidth_mutex);
+       mutex_lock(hcd->bandwidth_mutex);
        ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
        if (ret < 0) {
                dev_warn(&udev->dev,
                                "Busted HC?  Not enough HCD resources for "
                                "old configuration.\n");
-               mutex_unlock(&hcd->bandwidth_mutex);
+               mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
        ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
@@ -3658,10 +3864,10 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                dev_err(&udev->dev,
                        "can't restore configuration #%d (error=%d)\n",
                        udev->actconfig->desc.bConfigurationValue, ret);
-               mutex_unlock(&hcd->bandwidth_mutex);
+               mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
-       mutex_unlock(&hcd->bandwidth_mutex);
+       mutex_unlock(hcd->bandwidth_mutex);
        usb_set_device_state(udev, USB_STATE_CONFIGURED);
 
        /* Put interfaces back into the same altsettings as before.