Merge tag 'backport/v3.14.24-ltsi-rc1/phy-rcar-gen2-usb-to-v3.15' into backport/v3...
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / usb / core / hub.c
index 64ea219..adcbe18 100644 (file)
@@ -404,30 +404,35 @@ static int set_port_feature(struct usb_device *hdev, int port1, int feature)
                NULL, 0, 1000);
 }
 
+static char *to_led_name(int selector)
+{
+       switch (selector) {
+       case HUB_LED_AMBER:
+               return "amber";
+       case HUB_LED_GREEN:
+               return "green";
+       case HUB_LED_OFF:
+               return "off";
+       case HUB_LED_AUTO:
+               return "auto";
+       default:
+               return "??";
+       }
+}
+
 /*
  * USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7
  * for info about using port indicators
  */
-static void set_port_led(
-       struct usb_hub *hub,
-       int port1,
-       int selector
-)
+static void set_port_led(struct usb_hub *hub, int port1, int selector)
 {
-       int status = set_port_feature(hub->hdev, (selector << 8) | port1,
+       struct usb_port *port_dev = hub->ports[port1 - 1];
+       int status;
+
+       status = set_port_feature(hub->hdev, (selector << 8) | port1,
                        USB_PORT_FEAT_INDICATOR);
-       if (status < 0)
-               dev_dbg (hub->intfdev,
-                       "port %d indicator %s status %d\n",
-                       port1,
-                       ({ char *s; switch (selector) {
-                       case HUB_LED_AMBER: s = "amber"; break;
-                       case HUB_LED_GREEN: s = "green"; break;
-                       case HUB_LED_OFF: s = "off"; break;
-                       case HUB_LED_AUTO: s = "auto"; break;
-                       default: s = "??"; break;
-                       } s; }),
-                       status);
+       dev_dbg(&port_dev->dev, "indicator %s status %d\n",
+               to_led_name(selector), status);
 }
 
 #define        LED_CYCLE_PERIOD        ((2*HZ)/3)
@@ -809,8 +814,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
        int port1;
        unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
        unsigned delay;
-       u16 wHubCharacteristics =
-                       le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
        /* Enable power on each port.  Some hubs have reserved values
         * of LPSM (> 2) in their descriptors, even though they are
@@ -818,7 +821,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
         * but only emulate it.  In all cases, the ports won't work
         * unless we send these messages to the hub.
         */
-       if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
+       if (hub_is_port_power_switchable(hub))
                dev_dbg(hub->intfdev, "enabling power on all ports\n");
        else
                dev_dbg(hub->intfdev, "trying to enable port power on "
@@ -884,6 +887,25 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
        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->ports[port1 - 1]->dev,
+                        "Not disabling port; link state is RxDetect\n");
+               return ret;
+       }
+
        ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
        if (ret)
                return ret;
@@ -902,20 +924,20 @@ static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
                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);
+               dev_warn(&hub->ports[port1 - 1]->dev,
+                               "Could not disable after %d ms\n", 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_port *port_dev = hub->ports[port1 - 1];
        struct usb_device *hdev = hub->hdev;
        int ret = 0;
 
-       if (hub->ports[port1 - 1]->child && set_state)
-               usb_set_device_state(hub->ports[port1 - 1]->child,
-                               USB_STATE_NOTATTACHED);
+       if (port_dev->child && set_state)
+               usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
        if (!hub->error) {
                if (hub_is_superspeed(hub->hdev))
                        ret = hub_usb3_port_disable(hub, port1);
@@ -924,8 +946,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
                                        USB_PORT_FEAT_ENABLE);
        }
        if (ret && ret != -ENODEV)
-               dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
-                               port1, ret);
+               dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
        return ret;
 }
 
@@ -936,7 +957,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
  */
 static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
 {
-       dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1);
+       dev_dbg(&hub->ports[port1 - 1]->dev, "logical disconnect\n");
        hub_port_disable(hub, port1, 1);
 
        /* FIXME let caller ask to power down the port:
@@ -1073,21 +1094,23 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
        }
  init2:
 
-       /* Check each port and set hub->change_bits to let khubd know
+       /*
+        * Check each port and set hub->change_bits to let khubd know
         * which ports need attention.
         */
        for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
-               struct usb_device *udev = hub->ports[port1 - 1]->child;
+               struct usb_port *port_dev = hub->ports[port1 - 1];
+               struct usb_device *udev = port_dev->child;
                u16 portstatus, portchange;
 
                portstatus = portchange = 0;
                status = hub_port_status(hub, port1, &portstatus, &portchange);
                if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
-                       dev_dbg(hub->intfdev,
-                                       "port %d: status %04x change %04x\n",
-                                       port1, portstatus, portchange);
+                       dev_dbg(&port_dev->dev, "status %04x change %04x\n",
+                                       portstatus, portchange);
 
-               /* After anything other than HUB_RESUME (i.e., initialization
+               /*
+                * After anything other than HUB_RESUME (i.e., initialization
                 * or any sort of reset), every port should be disabled.
                 * Unconnected ports should likewise be disabled (paranoia),
                 * and so should ports for which we have no usb_device.
@@ -1676,11 +1699,28 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
         * - Change autosuspend delay of hub can avoid unnecessary auto
         *   suspend timer for hub, also may decrease power consumption
         *   of USB bus.
+        *
+        * - If user has indicated to prevent autosuspend by passing
+        *   usbcore.autosuspend = -1 then keep autosuspend disabled.
         */
-       pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
+#ifdef CONFIG_PM_RUNTIME
+       if (hdev->dev.power.autosuspend_delay >= 0)
+               pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
+#endif
+
+       /*
+        * Hubs have proper suspend/resume support, except for root hubs
+        * where the controller driver doesn't have bus_suspend and
+        * bus_resume methods.
+        */
+       if (hdev->parent) {             /* normal device */
+               usb_enable_autosuspend(hdev);
+       } else {                        /* root hub */
+               const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
 
-       /* Hubs have proper suspend/resume support. */
-       usb_enable_autosuspend(hdev);
+               if (drv->bus_suspend && drv->bus_resume)
+                       usb_enable_autosuspend(hdev);
+       }
 
        if (hdev->level == MAX_TOPO_LEVEL) {
                dev_err(&intf->dev,
@@ -1912,8 +1952,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;
                }
@@ -2547,9 +2589,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
                        delay = HUB_LONG_RESET_TIME;
 
-               dev_dbg (hub->intfdev,
-                       "port %d not %sreset yet, waiting %dms\n",
-                       port1, warm ? "warm " : "", delay);
+               dev_dbg(&hub->ports[port1 - 1]->dev,
+                               "not %sreset yet, waiting %dms\n",
+                               warm ? "warm " : "", delay);
        }
 
        if ((portstatus & USB_PORT_STAT_RESET))
@@ -2633,6 +2675,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
 {
        int i, status;
        u16 portchange, portstatus;
+       struct usb_port *port_dev = hub->ports[port1 - 1];
 
        if (!hub_is_superspeed(hub->hdev)) {
                if (warm) {
@@ -2666,9 +2709,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                if (status == -ENODEV) {
                        ;       /* The hub is gone */
                } else if (status) {
-                       dev_err(hub->intfdev,
-                                       "cannot %sreset port %d (err = %d)\n",
-                                       warm ? "warm " : "", port1, status);
+                       dev_err(&port_dev->dev,
+                                       "cannot %sreset (err = %d)\n",
+                                       warm ? "warm " : "", status);
                } else {
                        status = hub_port_wait_reset(hub, port1, udev, delay,
                                                                warm);
@@ -2701,21 +2744,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                         * hot or warm reset failed.  Try another warm reset.
                         */
                        if (!warm) {
-                               dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
-                                               port1);
+                               dev_dbg(&port_dev->dev,
+                                               "hot reset failed, warm reset\n");
                                warm = true;
                        }
                }
 
-               dev_dbg (hub->intfdev,
-                       "port %d not enabled, trying %sreset again...\n",
-                       port1, warm ? "warm " : "");
+               dev_dbg(&port_dev->dev,
+                               "not enabled, trying %sreset again...\n",
+                               warm ? "warm " : "");
                delay = HUB_LONG_RESET_TIME;
        }
 
-       dev_err (hub->intfdev,
-               "Cannot enable port %i.  Maybe the USB cable is bad?\n",
-               port1);
+       dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");
 
 done:
        if (!hub_is_superspeed(hub->hdev))
@@ -2766,6 +2807,8 @@ static int check_port_resume_type(struct usb_device *udev,
                struct usb_hub *hub, int port1,
                int status, unsigned portchange, unsigned portstatus)
 {
+       struct usb_port *port_dev = hub->ports[port1 - 1];
+
        /* Is the device still present? */
        if (status || port_is_suspended(hub, portstatus) ||
                        !port_is_power_on(hub, portstatus) ||
@@ -2785,9 +2828,8 @@ static int check_port_resume_type(struct usb_device *udev,
        }
 
        if (status) {
-               dev_dbg(hub->intfdev,
-                               "port %d status %04x.%04x after resume, %d\n",
-                               port1, portchange, portstatus, status);
+               dev_dbg(&port_dev->dev, "status %04x.%04x after resume, %d\n",
+                               portchange, portstatus, status);
        } else if (udev->reset_resume) {
 
                /* Late port handoff can set status-change bits */
@@ -3018,8 +3060,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                status = 0;
        }
        if (status) {
-               dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n",
-                               port1, status);
+               dev_dbg(&port_dev->dev, "can't suspend, status %d\n", status);
 
                /* Try to enable USB3 LPM and LTM again */
                usb_unlocked_enable_lpm(udev);
@@ -3144,6 +3185,43 @@ static int finish_port_resume(struct usb_device *udev)
 }
 
 /*
+ * 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
  * Context: must be able to sleep; device not locked; pm locks held
@@ -3200,8 +3278,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        if (status == 0 && !port_is_suspended(hub, portstatus))
                goto SuspendCleared;
 
-       /* dev_dbg(hub->intfdev, "resume port %d\n", port1); */
-
        set_bit(port1, hub->busy_bits);
 
        /* see 7.1.7.7; affects power usage, but not budgeting */
@@ -3211,8 +3287,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
                status = usb_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);
+               dev_dbg(&port_dev->dev, "can't resume, status %d\n", status);
        } else {
                /* drive resume for at least 20 msec */
                dev_dbg(&udev->dev, "usb %sresume\n",
@@ -3245,6 +3320,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)
@@ -3313,12 +3392,11 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
         */
        hub->wakeup_enabled_descendants = 0;
        for (port1 = 1; port1 <= hdev->maxchild; port1++) {
-               struct usb_device       *udev;
+               struct usb_port *port_dev = hub->ports[port1 - 1];
+               struct usb_device *udev = port_dev->child;
 
-               udev = hub->ports[port1 - 1]->child;
                if (udev && udev->can_submit) {
-                       dev_warn(&intf->dev, "port %d not suspended yet\n",
-                                       port1);
+                       dev_warn(&port_dev->dev, "not suspended yet\n");
                        if (PMSG_IS_AUTO(msg))
                                return -EBUSY;
                }
@@ -3858,9 +3936,10 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
 int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
 {
        int ret;
-       int total_time, stable_time = 0;
        u16 portchange, portstatus;
        unsigned connection = 0xffff;
+       int total_time, stable_time = 0;
+       struct usb_port *port_dev = hub->ports[port1 - 1];
 
        for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
                ret = hub_port_status(hub, port1, &portstatus, &portchange);
@@ -3889,9 +3968,8 @@ int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected)
                msleep(HUB_DEBOUNCE_STEP);
        }
 
-       dev_dbg (hub->intfdev,
-               "debounce: port %d: total %dms stable %dms status 0x%x\n",
-               port1, total_time, stable_time, portstatus);
+       dev_dbg(&port_dev->dev, "debounce total %dms stable %dms status 0x%x\n",
+                       total_time, stable_time, portstatus);
 
        if (stable_time < HUB_DEBOUNCE_STABLE)
                return -ETIMEDOUT;
@@ -3950,13 +4028,14 @@ static int hub_set_address(struct usb_device *udev, int devnum)
  */
 static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
 {
-       int connect_type;
+       struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
+       int connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
 
        if (!udev->usb2_hw_lpm_capable)
                return;
 
-       connect_type = usb_get_hub_port_connect_type(udev->parent,
-                       udev->portnum);
+       if (hub)
+               connect_type = hub->ports[udev->portnum - 1]->connect_type;
 
        if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
                        connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
@@ -4222,8 +4301,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (retval)
                goto fail;
 
-       if (hcd->phy && !hdev->parent)
-               usb_phy_notify_connect(hcd->phy, udev->speed);
+       if (hcd->usb_phy && !hdev->parent)
+               usb_phy_notify_connect(hcd->usb_phy, udev->speed);
 
        /*
         * Some superspeed devices have finished the link training process
@@ -4299,6 +4378,9 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
        struct usb_qualifier_descriptor *qual;
        int                             status;
 
+       if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER)
+               return;
+
        qual = kmalloc (sizeof *qual, GFP_KERNEL);
        if (qual == NULL)
                return;
@@ -4329,9 +4411,10 @@ hub_power_remaining (struct usb_hub *hub)
 
        remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
        for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
-               struct usb_device       *udev = hub->ports[port1 - 1]->child;
-               int                     delta;
-               unsigned                unit_load;
+               struct usb_port *port_dev = hub->ports[port1 - 1];
+               struct usb_device *udev = port_dev->child;
+               unsigned unit_load;
+               int delta;
 
                if (!udev)
                        continue;
@@ -4351,9 +4434,8 @@ hub_power_remaining (struct usb_hub *hub)
                else
                        delta = 8;
                if (delta > hub->mA_per_port)
-                       dev_warn(&udev->dev,
-                                "%dmA is over %umA budget for port %d!\n",
-                                delta, hub->mA_per_port, port1);
+                       dev_warn(&port_dev->dev, "%dmA is over %umA budget!\n",
+                                       delta, hub->mA_per_port);
                remaining -= delta;
        }
        if (remaining < 0) {
@@ -4376,17 +4458,14 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                                        u16 portstatus, u16 portchange)
 {
        struct usb_device *hdev = hub->hdev;
-       struct device *hub_dev = hub->intfdev;
        struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
-       unsigned wHubCharacteristics =
-                       le16_to_cpu(hub->descriptor->wHubCharacteristics);
+       struct usb_port *port_dev = hub->ports[port1 - 1];
        struct usb_device *udev;
        int status, i;
        unsigned unit_load;
 
-       dev_dbg (hub_dev,
-               "port %d, status %04x, change %04x, %s\n",
-               port1, portstatus, portchange, portspeed(hub, portstatus));
+       dev_dbg(&port_dev->dev, "status %04x, change %04x, %s\n",
+                       portstatus, portchange, portspeed(hub, portstatus));
 
        if (hub->has_indicators) {
                set_port_led(hub, port1, HUB_LED_AUTO);
@@ -4401,7 +4480,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 #endif
 
        /* Try to resuscitate an existing device */
-       udev = hub->ports[port1 - 1]->child;
+       udev = port_dev->child;
        if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
                        udev->state != USB_STATE_NOTATTACHED) {
                usb_lock_device(udev);
@@ -4430,10 +4509,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 
        /* Disconnect any existing devices under this port */
        if (udev) {
-               if (hcd->phy && !hdev->parent &&
+               if (hcd->usb_phy && !hdev->parent &&
                                !(portstatus & USB_PORT_STAT_CONNECTION))
-                       usb_phy_notify_disconnect(hcd->phy, udev->speed);
-               usb_disconnect(&hub->ports[port1 - 1]->child);
+                       usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
+               usb_disconnect(&port_dev->child);
        }
        clear_bit(port1, hub->change_bits);
 
@@ -4449,8 +4528,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                status = hub_port_debounce_be_stable(hub, port1);
                if (status < 0) {
                        if (status != -ENODEV && printk_ratelimit())
-                               dev_err(hub_dev, "connect-debounce failed, "
-                                               "port %d disabled\n", port1);
+                               dev_err(&port_dev->dev,
+                                               "connect-debounce failed\n");
                        portstatus &= ~USB_PORT_STAT_CONNECTION;
                } else {
                        portstatus = status;
@@ -4464,7 +4543,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        test_bit(port1, hub->removed_bits)) {
 
                /* maybe switch power back on (e.g. root hub was reset) */
-               if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
+               if (hub_is_port_power_switchable(hub)
                                && !port_is_power_on(hub, portstatus))
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
@@ -4485,9 +4564,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                 */
                udev = usb_alloc_dev(hdev, hdev->bus, port1);
                if (!udev) {
-                       dev_err (hub_dev,
-                               "couldn't allocate port %d usb_device\n",
-                               port1);
+                       dev_err(&port_dev->dev,
+                                       "couldn't allocate usb_device\n");
                        goto done;
                }
 
@@ -4567,7 +4645,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                if (hdev->state == USB_STATE_NOTATTACHED)
                        status = -ENOTCONN;
                else
-                       hub->ports[port1 - 1]->child = udev;
+                       port_dev->child = udev;
                spin_unlock_irq(&device_state_lock);
 
                /* Run it through the hoops (find a driver, etc) */
@@ -4575,7 +4653,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        status = usb_new_device(udev);
                        if (status) {
                                spin_lock_irq(&device_state_lock);
-                               hub->ports[port1 - 1]->child = NULL;
+                               port_dev->child = NULL;
                                spin_unlock_irq(&device_state_lock);
                        }
                }
@@ -4585,7 +4663,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 
                status = hub_power_remaining(hub);
                if (status)
-                       dev_dbg(hub_dev, "%dmA power budget left\n", status);
+                       dev_dbg(hub->intfdev, "%dmA power budget left\n", status);
 
                return;
 
@@ -4603,8 +4681,8 @@ loop:
                        !hcd->driver->port_handed_over ||
                        !(hcd->driver->port_handed_over)(hcd, port1)) {
                if (status != -ENOTCONN && status != -ENODEV)
-                       dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
-                                       port1);
+                       dev_err(&port_dev->dev,
+                                       "unable to enumerate USB device\n");
        }
 
 done:
@@ -4617,13 +4695,14 @@ done:
 static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
                u16 portstatus, u16 portchange)
 {
+       struct usb_port *port_dev = hub->ports[port - 1];
        struct usb_device *hdev;
        struct usb_device *udev;
        int connect_change = 0;
        int ret;
 
        hdev = hub->hdev;
-       udev = hub->ports[port - 1]->child;
+       udev = port_dev->child;
        if (!hub_is_superspeed(hdev)) {
                if (!(portchange & USB_PORT_STAT_C_SUSPEND))
                        return 0;
@@ -4648,8 +4727,7 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
                ret = -ENODEV;
                hub_port_disable(hub, port, 1);
        }
-       dev_dbg(hub->intfdev, "resume on port %d, status %d\n",
-                       port, ret);
+       dev_dbg(&port_dev->dev, "resume, status %d\n", ret);
        return connect_change;
 }
 
@@ -4687,9 +4765,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",
@@ -4739,6 +4818,9 @@ static void hub_events(void)
 
                /* deal with port status changes */
                for (i = 1; i <= hdev->maxchild; i++) {
+                       struct usb_port *port_dev = hub->ports[i - 1];
+                       struct usb_device *udev = port_dev->child;
+
                        if (test_bit(i, hub->busy_bits))
                                continue;
                        connect_change = test_bit(i, hub->change_bits);
@@ -4760,10 +4842,9 @@ static void hub_events(void)
 
                        if (portchange & USB_PORT_STAT_C_ENABLE) {
                                if (!connect_change)
-                                       dev_dbg (hub_dev,
-                                               "port %d enable change, "
-                                               "status %08x\n",
-                                               i, portstatus);
+                                       dev_dbg(&port_dev->dev,
+                                                       "enable change, status %08x\n",
+                                                        portstatus);
                                usb_clear_port_feature(hdev, i,
                                        USB_PORT_FEAT_C_ENABLE);
 
@@ -4774,13 +4855,9 @@ static void hub_events(void)
                                 * Works at least with mouse driver.
                                 */
                                if (!(portstatus & USB_PORT_STAT_ENABLE)
-                                   && !connect_change
-                                   && hub->ports[i - 1]->child) {
-                                       dev_err (hub_dev,
-                                           "port %i "
-                                           "disabled by hub (EMI?), "
-                                           "re-enabling...\n",
-                                               i);
+                                   && !connect_change && udev) {
+                                       dev_err(&port_dev->dev,
+                                                       "disabled by hub (EMI?), re-enabling...\n");
                                        connect_change = 1;
                                }
                        }
@@ -4793,30 +4870,25 @@ static void hub_events(void)
                                u16 status = 0;
                                u16 unused;
 
-                               dev_dbg(hub_dev, "over-current change on port "
-                                       "%d\n", i);
+                               dev_dbg(&port_dev->dev, "over-current change\n");
                                usb_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);
+                                       dev_err(&port_dev->dev,
+                                                       "over-current condition\n");
                        }
 
                        if (portchange & USB_PORT_STAT_C_RESET) {
-                               dev_dbg (hub_dev,
-                                       "reset change on port %d\n",
-                                       i);
+                               dev_dbg(&port_dev->dev, "reset change\n");
                                usb_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);
+                               dev_dbg(&port_dev->dev, "warm reset change\n");
                                usb_clear_port_feature(hdev, i,
                                        USB_PORT_FEAT_C_BH_PORT_RESET);
                        }
@@ -4825,9 +4897,7 @@ static void hub_events(void)
                                                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);
+                               dev_warn(&port_dev->dev, "config error\n");
                                usb_clear_port_feature(hub->hdev, i,
                                                USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
                        }
@@ -4837,10 +4907,8 @@ static void hub_events(void)
                         */
                        if (hub_port_warm_reset_required(hub, portstatus)) {
                                int status;
-                               struct usb_device *udev =
-                                       hub->ports[i - 1]->child;
 
-                               dev_dbg(hub_dev, "warm reset port %d\n", i);
+                               dev_dbg(&port_dev->dev, "warm reset\n");
                                if (!udev ||
                                    !(portstatus & USB_PORT_STAT_CONNECTION) ||
                                    udev->state == USB_STATE_NOTATTACHED) {
@@ -4855,6 +4923,24 @@ static void hub_events(void)
                                        usb_unlock_device(udev);
                                        connect_change = 0;
                                }
+                       /*
+                        * On disconnect USB3 protocol ports transit from U0 to
+                        * SS.Inactive to Rx.Detect. If this happens a warm-
+                        * reset is not needed, but a (re)connect may happen
+                        * before khubd runs and sees the disconnect, and the
+                        * device may be an unknown state.
+                        *
+                        * If the port went through SS.Inactive without khubd
+                        * seeing it the C_LINK_STATE change flag will be set,
+                        * and we reset the dev to put it in a known state.
+                        */
+                       } else if (udev && hub_is_superspeed(hub->hdev) &&
+                                  (portchange & USB_PORT_STAT_C_LINK_STATE) &&
+                                  (portstatus & USB_PORT_STAT_CONNECTION)) {
+                               usb_lock_device(udev);
+                               usb_reset_device(udev);
+                               usb_unlock_device(udev);
+                               connect_change = 0;
                        }
 
                        if (connect_change)
@@ -4902,6 +4988,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) */
@@ -5340,10 +5427,11 @@ int usb_reset_device(struct usb_device *udev)
                                else if (cintf->condition ==
                                                USB_INTERFACE_BOUND)
                                        rebind = 1;
+                               if (rebind)
+                                       cintf->needs_binding = 1;
                        }
-                       if (ret == 0 && rebind)
-                               usb_rebind_intf(cintf);
                }
+               usb_unbind_and_rebind_marked_interfaces(udev);
        }
 
        usb_autosuspend_device(udev);
@@ -5419,56 +5507,26 @@ struct usb_device *usb_hub_find_child(struct usb_device *hdev,
 }
 EXPORT_SYMBOL_GPL(usb_hub_find_child);
 
-/**
- * usb_set_hub_port_connect_type - set hub port connect type.
- * @hdev: USB device belonging to the usb hub
- * @port1: port num of the port
- * @type: connect type of the port
- */
-void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
-       enum usb_port_connect_type type)
-{
-       struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
-
-       if (hub)
-               hub->ports[port1 - 1]->connect_type = type;
-}
-
-/**
- * usb_get_hub_port_connect_type - Get the port's connect type
- * @hdev: USB device belonging to the usb hub
- * @port1: port num of the port
- *
- * Return: The connect type of the port if successful. Or
- * USB_PORT_CONNECT_TYPE_UNKNOWN if input params are invalid.
- */
-enum usb_port_connect_type
-usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
-{
-       struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
-
-       if (!hub)
-               return USB_PORT_CONNECT_TYPE_UNKNOWN;
-
-       return hub->ports[port1 - 1]->connect_type;
-}
-
 void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
                struct usb_hub_descriptor *desc)
 {
+       struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
        enum usb_port_connect_type connect_type;
        int i;
 
+       if (!hub)
+               return;
+
        if (!hub_is_superspeed(hdev)) {
                for (i = 1; i <= hdev->maxchild; i++) {
-                       connect_type = usb_get_hub_port_connect_type(hdev, i);
+                       struct usb_port *port_dev = hub->ports[i - 1];
 
+                       connect_type = port_dev->connect_type;
                        if (connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
                                u8 mask = 1 << (i%8);
 
                                if (!(desc->u.hs.DeviceRemovable[i/8] & mask)) {
-                                       dev_dbg(&hdev->dev, "usb port%d's DeviceRemovable is changed to 1 according to platform information.\n",
-                                               i);
+                                       dev_dbg(&port_dev->dev, "DeviceRemovable is changed to 1 according to platform information.\n");
                                        desc->u.hs.DeviceRemovable[i/8] |= mask;
                                }
                        }
@@ -5477,14 +5535,14 @@ void usb_hub_adjust_deviceremovable(struct usb_device *hdev,
                u16 port_removable = le16_to_cpu(desc->u.ss.DeviceRemovable);
 
                for (i = 1; i <= hdev->maxchild; i++) {
-                       connect_type = usb_get_hub_port_connect_type(hdev, i);
+                       struct usb_port *port_dev = hub->ports[i - 1];
 
+                       connect_type = port_dev->connect_type;
                        if (connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
                                u16 mask = 1 << i;
 
                                if (!(port_removable & mask)) {
-                                       dev_dbg(&hdev->dev, "usb port%d's DeviceRemovable is changed to 1 according to platform information.\n",
-                                               i);
+                                       dev_dbg(&port_dev->dev, "DeviceRemovable is changed to 1 according to platform information.\n");
                                        port_removable |= mask;
                                }
                        }