Merge tag 'random_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[pandora-kernel.git] / drivers / usb / core / hub.c
index 7f380ff..128a804 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
 #include <linux/usb/quirks.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
@@ -82,7 +83,7 @@ struct usb_hub {
        u8                      indicator[USB_MAXCHILDREN];
        struct delayed_work     leds;
        struct delayed_work     init_work;
-       void                    **port_owners;
+       struct dev_state        **port_owners;
 };
 
 static inline int hub_is_superspeed(struct usb_device *hdev)
@@ -1272,7 +1273,8 @@ static int hub_configure(struct usb_hub *hub,
 
        hdev->children = kzalloc(hdev->maxchild *
                                sizeof(struct usb_device *), GFP_KERNEL);
-       hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
+       hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
+                               GFP_KERNEL);
        if (!hdev->children || !hub->port_owners) {
                ret = -ENOMEM;
                goto fail;
@@ -1650,7 +1652,7 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
  * to one of these "claimed" ports, the program will "own" the device.
  */
 static int find_port_owner(struct usb_device *hdev, unsigned port1,
-               void ***ppowner)
+               struct dev_state ***ppowner)
 {
        if (hdev->state == USB_STATE_NOTATTACHED)
                return -ENODEV;
@@ -1665,10 +1667,11 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
 }
 
 /* In the following three functions, the caller must hold hdev's lock */
-int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_claim_port(struct usb_device *hdev, unsigned port1,
+                      struct dev_state *owner)
 {
        int rc;
-       void **powner;
+       struct dev_state **powner;
 
        rc = find_port_owner(hdev, port1, &powner);
        if (rc)
@@ -1679,10 +1682,11 @@ int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
        return rc;
 }
 
-int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
+int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
+                        struct dev_state *owner)
 {
        int rc;
-       void **powner;
+       struct dev_state **powner;
 
        rc = find_port_owner(hdev, port1, &powner);
        if (rc)
@@ -1693,10 +1697,10 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
        return rc;
 }
 
-void usb_hub_release_all_ports(struct usb_device *hdev, void *owner)
+void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
 {
        int n;
-       void **powner;
+       struct dev_state **powner;
 
        n = find_port_owner(hdev, 1, &powner);
        if (n == 0) {
@@ -2066,7 +2070,7 @@ static int usb_enumerate_device(struct usb_device *udev)
                if (err < 0) {
                        dev_err(&udev->dev, "can't read configurations, error %d\n",
                                err);
-                       goto fail;
+                       return err;
                }
        }
        if (udev->wusb == 1 && udev->authorized == 0) {
@@ -2082,8 +2086,12 @@ static int usb_enumerate_device(struct usb_device *udev)
                udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
        }
        err = usb_enumerate_device_otg(udev);
-fail:
-       return err;
+       if (err < 0)
+               return err;
+
+       usb_detect_interface_quirks(udev);
+
+       return 0;
 }
 
 static void set_usb_port_removable(struct usb_device *udev)
@@ -2333,12 +2341,16 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 static int hub_port_reset(struct usb_hub *hub, int port1,
                        struct usb_device *udev, unsigned int delay, bool warm);
 
-/* Is a USB 3.0 port in the Inactive state? */
-static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
+/* Is a USB 3.0 port in the Inactive or Complinance Mode state?
+ * Port worm reset is required to recover
+ */
+static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
 {
        return hub_is_superspeed(hub->hdev) &&
-               (portstatus & USB_PORT_STAT_LINK_STATE) ==
-               USB_SS_PORT_LS_SS_INACTIVE;
+               (((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                 USB_SS_PORT_LS_SS_INACTIVE) ||
+                ((portstatus & USB_PORT_STAT_LINK_STATE) ==
+                 USB_SS_PORT_LS_COMP_MOD)) ;
 }
 
 static int hub_port_wait_reset(struct usb_hub *hub, int port1,
@@ -2374,7 +2386,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                         *
                         * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
                         */
-                       if (hub_port_inactive(hub, portstatus)) {
+                       if (hub_port_warm_reset_required(hub, portstatus)) {
                                int ret;
 
                                if ((portchange & USB_PORT_STAT_C_CONNECTION))
@@ -2616,6 +2628,50 @@ static int check_port_resume_type(struct usb_device *udev,
        return status;
 }
 
+int usb_disable_ltm(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       /* Check if the roothub and device supports LTM. */
+       if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+                       !usb_device_supports_ltm(udev))
+               return 0;
+
+       /* Clear Feature LTM Enable can only be sent if the device is
+        * configured.
+        */
+       if (!udev->actconfig)
+               return 0;
+
+       return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                       USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
+                       USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+                       USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       /* Check if the roothub and device supports LTM. */
+       if (!usb_device_supports_ltm(hcd->self.root_hub) ||
+                       !usb_device_supports_ltm(udev))
+               return;
+
+       /* Set Feature LTM Enable can only be sent if the device is
+        * configured.
+        */
+       if (!udev->actconfig)
+               return;
+
+       usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                       USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+                       USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
+                       USB_CTRL_SET_TIMEOUT);
+}
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
+
 #ifdef CONFIG_USB_SUSPEND
 
 /*
@@ -2711,6 +2767,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
        if (udev->usb2_hw_lpm_enabled == 1)
                usb_set_usb2_hardware_lpm(udev, 0);
 
+       if (usb_disable_ltm(udev)) {
+               dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.",
+                               __func__);
+               return -ENOMEM;
+       }
        if (usb_unlocked_disable_lpm(udev)) {
                dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
                                __func__);
@@ -2740,7 +2801,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
                if (udev->usb2_hw_lpm_capable == 1)
                        usb_set_usb2_hardware_lpm(udev, 1);
 
-               /* Try to enable USB3 LPM again */
+               /* Try to enable USB3 LTM and LPM again */
+               usb_enable_ltm(udev);
                usb_unlocked_enable_lpm(udev);
 
                /* System sleep transitions should never fail */
@@ -2941,7 +3003,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
                if (udev->usb2_hw_lpm_capable == 1)
                        usb_set_usb2_hardware_lpm(udev, 1);
 
-               /* Try to enable USB3 LPM */
+               /* Try to enable USB3 LTM and LPM */
+               usb_enable_ltm(udev);
                usb_unlocked_enable_lpm(udev);
        }
 
@@ -3494,6 +3557,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
 
 void usb_unlocked_enable_lpm(struct usb_device *udev) { }
 EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+
+int usb_disable_ltm(struct usb_device *udev)
+{
+       return 0;
+}
+EXPORT_SYMBOL_GPL(usb_disable_ltm);
+
+void usb_enable_ltm(struct usb_device *udev) { }
+EXPORT_SYMBOL_GPL(usb_enable_ltm);
 #endif
 
 
@@ -4043,6 +4115,13 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
        }
 
+       if (hcd->phy && !hdev->parent) {
+               if (portstatus & USB_PORT_STAT_CONNECTION)
+                       usb_phy_notify_connect(hcd->phy, port1);
+               else
+                       usb_phy_notify_disconnect(hcd->phy, port1);
+       }
+
        /* Return now if debouncing failed or nothing is connected or
         * the device was "removed".
         */
@@ -4417,9 +4496,7 @@ static void hub_events(void)
                        /* Warm reset a USB3 protocol port if it's in
                         * SS.Inactive state.
                         */
-                       if (hub_is_superspeed(hub->hdev) &&
-                               (portstatus & USB_PORT_STAT_LINK_STATE)
-                                       == USB_SS_PORT_LS_SS_INACTIVE) {
+                       if (hub_port_warm_reset_required(hub, portstatus)) {
                                dev_dbg(hub_dev, "warm reset port %d\n", i);
                                hub_port_reset(hub, i, NULL,
                                                HUB_BH_RESET_TIME, true);
@@ -4679,6 +4756,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        }
        parent_hub = hdev_to_hub(parent_hdev);
 
+       /* Disable LPM and LTM while we reset the device and reinstall the alt
+        * settings.  Device-initiated LPM settings, and system exit latency
+        * settings are cleared when the device is reset, so we have to set
+        * them up again.
+        */
+       ret = usb_unlocked_disable_lpm(udev);
+       if (ret) {
+               dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
+               goto re_enumerate;
+       }
+       ret = usb_disable_ltm(udev);
+       if (ret) {
+               dev_err(&udev->dev, "%s Failed to disable LTM\n.",
+                               __func__);
+               goto re_enumerate;
+       }
+
        set_bit(port1, parent_hub->busy_bits);
        for (i = 0; i < SET_CONFIG_TRIES; ++i) {
 
@@ -4706,22 +4800,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                goto done;
 
        mutex_lock(hcd->bandwidth_mutex);
-       /* Disable LPM while we reset the device and reinstall the alt settings.
-        * Device-initiated LPM settings, and system exit latency settings are
-        * cleared when the device is reset, so we have to set them up again.
-        */
-       ret = usb_disable_lpm(udev);
-       if (ret) {
-               dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
-               mutex_unlock(hcd->bandwidth_mutex);
-               goto done;
-       }
        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");
-               usb_enable_lpm(udev);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
@@ -4733,7 +4816,6 @@ 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);
-               usb_enable_lpm(udev);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
        }
@@ -4772,17 +4854,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                                desc->bInterfaceNumber,
                                desc->bAlternateSetting,
                                ret);
-                       usb_unlocked_enable_lpm(udev);
                        goto re_enumerate;
                }
        }
 
-       /* Now that the alt settings are re-installed, enable LPM. */
-       usb_unlocked_enable_lpm(udev);
 done:
+       /* Now that the alt settings are re-installed, enable LTM and LPM. */
+       usb_unlocked_enable_lpm(udev);
+       usb_enable_ltm(udev);
        return 0;
  
 re_enumerate:
+       /* LPM state doesn't matter when we're about to destroy the device. */
        hub_port_logical_disconnect(parent_hub, port1);
        return -ENODEV;
 }