Merge 3.8-rc4 into usb-next
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / usb / core / hub.c
index 957ed2c..dc6ebd1 100644 (file)
@@ -1351,6 +1351,8 @@ static int hub_configure(struct usb_hub *hub,
        unsigned int pipe;
        int maxp, ret, i;
        char *message = "out of memory";
+       unsigned unit_load;
+       unsigned full_load;
 
        hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
        if (!hub->buffer) {
@@ -1397,6 +1399,13 @@ static int hub_configure(struct usb_hub *hub,
        }
 
        wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+       if (hub_is_superspeed(hdev)) {
+               unit_load = 150;
+               full_load = 900;
+       } else {
+               unit_load = 100;
+               full_load = 500;
+       }
 
        /* FIXME for USB 3.0, skip for now */
        if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
@@ -1516,40 +1525,44 @@ static int hub_configure(struct usb_hub *hub,
                goto fail;
        }
        le16_to_cpus(&hubstatus);
+       hcd = bus_to_hcd(hdev->bus);
        if (hdev == hdev->bus->root_hub) {
-               if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
-                       hub->mA_per_port = 500;
+               if (hcd->power_budget > 0)
+                       hdev->bus_mA = hcd->power_budget;
+               else
+                       hdev->bus_mA = full_load * hdev->maxchild;
+               if (hdev->bus_mA >= full_load)
+                       hub->mA_per_port = full_load;
                else {
                        hub->mA_per_port = hdev->bus_mA;
                        hub->limited_power = 1;
                }
        } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
+               int remaining = hdev->bus_mA -
+                       hub->descriptor->bHubContrCurrent;
+
                dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
                        hub->descriptor->bHubContrCurrent);
                hub->limited_power = 1;
-               if (hdev->maxchild > 0) {
-                       int remaining = hdev->bus_mA -
-                                       hub->descriptor->bHubContrCurrent;
 
-                       if (remaining < hdev->maxchild * 100)
-                               dev_warn(hub_dev,
+               if (remaining < hdev->maxchild * unit_load)
+                       dev_warn(hub_dev,
                                        "insufficient power available "
                                        "to use all downstream ports\n");
-                       hub->mA_per_port = 100;         /* 7.2.1.1 */
-               }
+               hub->mA_per_port = unit_load;   /* 7.2.1 */
+
        } else {        /* Self-powered external hub */
                /* FIXME: What about battery-powered external hubs that
                 * provide less current per port? */
-               hub->mA_per_port = 500;
+               hub->mA_per_port = full_load;
        }
-       if (hub->mA_per_port < 500)
+       if (hub->mA_per_port < full_load)
                dev_dbg(hub_dev, "%umA bus power budget for each child\n",
                                hub->mA_per_port);
 
        /* Update the HCD's internal representation of this hub before khubd
         * starts getting port status changes for devices under the hub.
         */
-       hcd = bus_to_hcd(hdev->bus);
        if (hcd->driver->update_hub_device) {
                ret = hcd->driver->update_hub_device(hcd, hdev,
                                &hub->tt, GFP_KERNEL);
@@ -2535,77 +2548,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                        return ret;
 
                /* The port state is unknown until the reset completes. */
-               if ((portstatus & USB_PORT_STAT_RESET))
-                       goto delay;
-
-               /*
-                * Some buggy devices require a warm reset to be issued even
-                * when the port appears not to be connected.
-                */
-               if (!warm) {
-                       /*
-                        * Some buggy devices can cause an NEC host controller
-                        * to transition to the "Error" state after a hot port
-                        * reset.  This will show up as the port state in
-                        * "Inactive", and the port may also report a
-                        * disconnect.  Forcing a warm port reset seems to make
-                        * the device work.
-                        *
-                        * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
-                        */
-                       if (hub_port_warm_reset_required(hub, portstatus)) {
-                               int ret;
-
-                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_CONNECTION);
-                               if (portchange & USB_PORT_STAT_C_LINK_STATE)
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
-                               if (portchange & USB_PORT_STAT_C_RESET)
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_RESET);
-                               dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
-                                               port1);
-                               ret = hub_port_reset(hub, port1,
-                                               udev, HUB_BH_RESET_TIME,
-                                               true);
-                               if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                                       clear_port_feature(hub->hdev, port1,
-                                                       USB_PORT_FEAT_C_CONNECTION);
-                               return ret;
-                       }
-                       /* Device went away? */
-                       if (!(portstatus & USB_PORT_STAT_CONNECTION))
-                               return -ENOTCONN;
-
-                       /* bomb out completely if the connection bounced */
-                       if ((portchange & USB_PORT_STAT_C_CONNECTION))
-                               return -ENOTCONN;
-
-                       if ((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 (!(portstatus & USB_PORT_STAT_CONNECTION) ||
-                                       hub_port_warm_reset_required(hub,
-                                               portstatus))
-                               return -ENOTCONN;
-
-                       return 0;
-               }
+               if (!(portstatus & USB_PORT_STAT_RESET))
+                       break;
 
-delay:
                /* switch to the long delay after two short delay failures */
                if (delay_time >= 2 * HUB_SHORT_RESET_TIME)
                        delay = HUB_LONG_RESET_TIME;
@@ -2615,20 +2560,54 @@ delay:
                        port1, warm ? "warm " : "", delay);
        }
 
-       return -EBUSY;
+       if ((portstatus & USB_PORT_STAT_RESET))
+               return -EBUSY;
+
+       if (hub_port_warm_reset_required(hub, portstatus))
+               return -ENOTCONN;
+
+       /* Device went away? */
+       if (!(portstatus & USB_PORT_STAT_CONNECTION))
+               return -ENOTCONN;
+
+       /* bomb out completely if the connection bounced.  A USB 3.0
+        * connection may bounce if multiple warm resets were issued,
+        * but the device may have successfully re-connected. Ignore it.
+        */
+       if (!hub_is_superspeed(hub->hdev) &&
+                       (portchange & USB_PORT_STAT_C_CONNECTION))
+               return -ENOTCONN;
+
+       if (!(portstatus & USB_PORT_STAT_ENABLE))
+               return -EBUSY;
+
+       if (!udev)
+               return 0;
+
+       if (hub_is_wusb(hub))
+               udev->speed = USB_SPEED_WIRELESS;
+       else if (hub_is_superspeed(hub->hdev))
+               udev->speed = USB_SPEED_SUPER;
+       else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+               udev->speed = USB_SPEED_HIGH;
+       else if (portstatus & USB_PORT_STAT_LOW_SPEED)
+               udev->speed = USB_SPEED_LOW;
+       else
+               udev->speed = USB_SPEED_FULL;
+       return 0;
 }
 
 static void hub_port_finish_reset(struct usb_hub *hub, int port1,
-                       struct usb_device *udev, int *status, bool warm)
+                       struct usb_device *udev, int *status)
 {
        switch (*status) {
        case 0:
-               if (!warm) {
-                       struct usb_hcd *hcd;
-                       /* TRSTRCY = 10 ms; plus some extra */
-                       msleep(10 + 40);
+               /* TRSTRCY = 10 ms; plus some extra */
+               msleep(10 + 40);
+               if (udev) {
+                       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
                        update_devnum(udev, 0);
-                       hcd = bus_to_hcd(udev->bus);
                        /* The xHC may think the device is already reset,
                         * so ignore the status.
                         */
@@ -2640,14 +2619,15 @@ static void hub_port_finish_reset(struct usb_hub *hub, int port1,
        case -ENODEV:
                clear_port_feature(hub->hdev,
                                port1, USB_PORT_FEAT_C_RESET);
-               /* FIXME need disconnect() for NOTATTACHED device */
                if (hub_is_superspeed(hub->hdev)) {
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_BH_PORT_RESET);
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_PORT_LINK_STATE);
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_CONNECTION);
                }
-               if (!warm)
+               if (udev)
                        usb_set_device_state(udev, *status
                                        ? USB_STATE_NOTATTACHED
                                        : USB_STATE_DEFAULT);
@@ -2660,18 +2640,30 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                        struct usb_device *udev, unsigned int delay, bool warm)
 {
        int i, status;
+       u16 portchange, portstatus;
 
-       if (!warm) {
-               /* Block EHCI CF initialization during the port reset.
-                * Some companion controllers don't like it when they mix.
-                */
-               down_read(&ehci_cf_port_reset_rwsem);
-       } else {
-               if (!hub_is_superspeed(hub->hdev)) {
+       if (!hub_is_superspeed(hub->hdev)) {
+               if (warm) {
                        dev_err(hub->intfdev, "only USB3 hub support "
                                                "warm reset\n");
                        return -EINVAL;
                }
+               /* Block EHCI CF initialization during the port reset.
+                * Some companion controllers don't like it when they mix.
+                */
+               down_read(&ehci_cf_port_reset_rwsem);
+       } else if (!warm) {
+               /*
+                * If the caller hasn't explicitly requested a warm reset,
+                * double check and see if one is needed.
+                */
+               status = hub_port_status(hub, port1,
+                                       &portstatus, &portchange);
+               if (status < 0)
+                       goto done;
+
+               if (hub_port_warm_reset_required(hub, portstatus))
+                       warm = true;
        }
 
        /* Reset the port */
@@ -2692,10 +2684,33 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                                                status);
                }
 
-               /* return on disconnect or reset */
+               /* Check for disconnect or reset */
                if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
-                       hub_port_finish_reset(hub, port1, udev, &status, warm);
-                       goto done;
+                       hub_port_finish_reset(hub, port1, udev, &status);
+
+                       if (!hub_is_superspeed(hub->hdev))
+                               goto done;
+
+                       /*
+                        * If a USB 3.0 device migrates from reset to an error
+                        * state, re-issue the warm reset.
+                        */
+                       if (hub_port_status(hub, port1,
+                                       &portstatus, &portchange) < 0)
+                               goto done;
+
+                       if (!hub_port_warm_reset_required(hub, portstatus))
+                               goto done;
+
+                       /*
+                        * If the port is in SS.Inactive or Compliance Mode, the
+                        * hot or warm reset failed.  Try another warm reset.
+                        */
+                       if (!warm) {
+                               dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n",
+                                               port1);
+                               warm = true;
+                       }
                }
 
                dev_dbg (hub->intfdev,
@@ -2709,7 +2724,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
                port1);
 
 done:
-       if (!warm)
+       if (!hub_is_superspeed(hub->hdev))
                up_read(&ehci_cf_port_reset_rwsem);
 
        return status;
@@ -2945,9 +2960,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
 
        /* see 7.1.7.6 */
        if (hub_is_superspeed(hub->hdev))
-               status = set_port_feature(hub->hdev,
-                               port1 | (USB_SS_PORT_LS_U3 << 3),
-                               USB_PORT_FEAT_LINK_STATE);
+               status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U3);
        else
                status = set_port_feature(hub->hdev, port1,
                                                USB_PORT_FEAT_SUSPEND);
@@ -3123,9 +3136,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
 
        /* see 7.1.7.7; affects power usage, but not budgeting */
        if (hub_is_superspeed(hub->hdev))
-               status = set_port_feature(hub->hdev,
-                               port1 | (USB_SS_PORT_LS_U0 << 3),
-                               USB_PORT_FEAT_LINK_STATE);
+               status = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_U0);
        else
                status = clear_port_feature(hub->hdev,
                                port1, USB_PORT_FEAT_SUSPEND);
@@ -4212,16 +4223,23 @@ hub_power_remaining (struct usb_hub *hub)
        for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
                struct usb_device       *udev = hub->ports[port1 - 1]->child;
                int                     delta;
+               unsigned                unit_load;
 
                if (!udev)
                        continue;
+               if (hub_is_superspeed(udev))
+                       unit_load = 150;
+               else
+                       unit_load = 100;
 
-               /* Unconfigured devices may not use more than 100mA,
-                * or 8mA for OTG ports */
+               /*
+                * Unconfigured devices may not use more than one unit load,
+                * or 8mA for OTG ports
+                */
                if (udev->actconfig)
-                       delta = udev->actconfig->desc.bMaxPower * 2;
+                       delta = usb_get_max_power(udev, udev->actconfig);
                else if (port1 != udev->bus->otg_port || hdev->parent)
-                       delta = 100;
+                       delta = unit_load;
                else
                        delta = 8;
                if (delta > hub->mA_per_port)
@@ -4256,6 +4274,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        le16_to_cpu(hub->descriptor->wHubCharacteristics);
        struct usb_device *udev;
        int status, i;
+       unsigned unit_load;
 
        dev_dbg (hub_dev,
                "port %d, status %04x, change %04x, %s\n",
@@ -4345,6 +4364,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        goto done;
                return;
        }
+       if (hub_is_superspeed(hub->hdev))
+               unit_load = 150;
+       else
+               unit_load = 100;
 
        for (i = 0; i < SET_CONFIG_TRIES; i++) {
 
@@ -4392,7 +4415,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                 * on the parent.
                 */
                if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
-                               && udev->bus_mA <= 100) {
+                               && udev->bus_mA <= unit_load) {
                        u16     devstat;
 
                        status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
@@ -4706,12 +4729,21 @@ 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);
-                               status = hub_port_reset(hub, i, NULL,
-                                               HUB_BH_RESET_TIME, true);
-                               if (status < 0)
-                                       hub_port_disable(hub, i, 1);
+                               if (!udev) {
+                                       status = hub_port_reset(hub, i,
+                                                       NULL, HUB_BH_RESET_TIME,
+                                                       true);
+                                       if (status < 0)
+                                               hub_port_disable(hub, i, 1);
+                               } else {
+                                       usb_lock_device(udev);
+                                       status = usb_reset_device(udev);
+                                       usb_unlock_device(udev);
+                               }
                                connect_change = 0;
                        }