USB/xhci: Enable remote wakeup for USB3 devices.
[firefly-linux-kernel-4.4.55.git] / drivers / usb / core / hub.c
index 79781461eec97ad27c4986178266a63034f4693f..50411cd0ac48a0dde8bc32345ea23c1d09b3b568 100644 (file)
@@ -84,7 +84,7 @@ struct usb_hub {
 
 static inline int hub_is_superspeed(struct usb_device *hdev)
 {
-       return (hdev->descriptor.bDeviceProtocol == 3);
+       return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
 }
 
 /* Protect struct usb_device->state and ->children members
@@ -102,7 +102,7 @@ static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
 static struct task_struct *khubd_task;
 
 /* cycle leds on hubs that aren't blinking for attention */
-static int blinkenlights = 0;
+static bool blinkenlights = 0;
 module_param (blinkenlights, bool, S_IRUGO);
 MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
 
@@ -131,12 +131,12 @@ MODULE_PARM_DESC(initial_descriptor_timeout,
  * otherwise the new scheme is used.  If that fails and "use_both_schemes"
  * is set, then the driver will make another attempt, using the other scheme.
  */
-static int old_scheme_first = 0;
+static bool old_scheme_first = 0;
 module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(old_scheme_first,
                 "start with the old device initialization scheme");
 
-static int use_both_schemes = 1;
+static bool use_both_schemes = 1;
 module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(use_both_schemes,
                "try the other device initialization scheme if the "
@@ -1041,58 +1041,58 @@ static int hub_configure(struct usb_hub *hub,
                dev_dbg(hub_dev, "standalone hub\n");
 
        switch (wHubCharacteristics & HUB_CHAR_LPSM) {
-               case 0x00:
-                       dev_dbg(hub_dev, "ganged power switching\n");
-                       break;
-               case 0x01:
-                       dev_dbg(hub_dev, "individual port power switching\n");
-                       break;
-               case 0x02:
-               case 0x03:
-                       dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
-                       break;
+       case HUB_CHAR_COMMON_LPSM:
+               dev_dbg(hub_dev, "ganged power switching\n");
+               break;
+       case HUB_CHAR_INDV_PORT_LPSM:
+               dev_dbg(hub_dev, "individual port power switching\n");
+               break;
+       case HUB_CHAR_NO_LPSM:
+       case HUB_CHAR_LPSM:
+               dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
+               break;
        }
 
        switch (wHubCharacteristics & HUB_CHAR_OCPM) {
-               case 0x00:
-                       dev_dbg(hub_dev, "global over-current protection\n");
-                       break;
-               case 0x08:
-                       dev_dbg(hub_dev, "individual port over-current protection\n");
-                       break;
-               case 0x10:
-               case 0x18:
-                       dev_dbg(hub_dev, "no over-current protection\n");
-                        break;
+       case HUB_CHAR_COMMON_OCPM:
+               dev_dbg(hub_dev, "global over-current protection\n");
+               break;
+       case HUB_CHAR_INDV_PORT_OCPM:
+               dev_dbg(hub_dev, "individual port over-current protection\n");
+               break;
+       case HUB_CHAR_NO_OCPM:
+       case HUB_CHAR_OCPM:
+               dev_dbg(hub_dev, "no over-current protection\n");
+               break;
        }
 
        spin_lock_init (&hub->tt.lock);
        INIT_LIST_HEAD (&hub->tt.clear_list);
        INIT_WORK(&hub->tt.clear_work, hub_tt_work);
        switch (hdev->descriptor.bDeviceProtocol) {
-               case 0:
-                       break;
-               case 1:
-                       dev_dbg(hub_dev, "Single TT\n");
-                       hub->tt.hub = hdev;
-                       break;
-               case 2:
-                       ret = usb_set_interface(hdev, 0, 1);
-                       if (ret == 0) {
-                               dev_dbg(hub_dev, "TT per port\n");
-                               hub->tt.multi = 1;
-                       } else
-                               dev_err(hub_dev, "Using single TT (err %d)\n",
-                                       ret);
-                       hub->tt.hub = hdev;
-                       break;
-               case 3:
-                       /* USB 3.0 hubs don't have a TT */
-                       break;
-               default:
-                       dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
-                               hdev->descriptor.bDeviceProtocol);
-                       break;
+       case USB_HUB_PR_FS:
+               break;
+       case USB_HUB_PR_HS_SINGLE_TT:
+               dev_dbg(hub_dev, "Single TT\n");
+               hub->tt.hub = hdev;
+               break;
+       case USB_HUB_PR_HS_MULTI_TT:
+               ret = usb_set_interface(hdev, 0, 1);
+               if (ret == 0) {
+                       dev_dbg(hub_dev, "TT per port\n");
+                       hub->tt.multi = 1;
+               } else
+                       dev_err(hub_dev, "Using single TT (err %d)\n",
+                               ret);
+               hub->tt.hub = hdev;
+               break;
+       case USB_HUB_PR_SS:
+               /* USB 3.0 hubs don't have a TT */
+               break;
+       default:
+               dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
+                       hdev->descriptor.bDeviceProtocol);
+               break;
        }
 
        /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
@@ -1360,7 +1360,6 @@ descriptor_error:
        return -ENODEV;
 }
 
-/* No BKL needed */
 static int
 hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
 {
@@ -1839,6 +1838,37 @@ fail:
        return err;
 }
 
+static void set_usb_port_removable(struct usb_device *udev)
+{
+       struct usb_device *hdev = udev->parent;
+       struct usb_hub *hub;
+       u8 port = udev->portnum;
+       u16 wHubCharacteristics;
+       bool removable = true;
+
+       if (!hdev)
+               return;
+
+       hub = hdev_to_hub(udev->parent);
+
+       wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+
+       if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
+               return;
+
+       if (hub_is_superspeed(hdev)) {
+               if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
+                       removable = false;
+       } else {
+               if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
+                       removable = false;
+       }
+
+       if (removable)
+               udev->removable = USB_DEVICE_REMOVABLE;
+       else
+               udev->removable = USB_DEVICE_FIXED;
+}
 
 /**
  * usb_new_device - perform initial device setup (usbcore-internal)
@@ -1897,6 +1927,15 @@ int usb_new_device(struct usb_device *udev)
        announce_device(udev);
 
        device_enable_async_suspend(&udev->dev);
+
+       /*
+        * check whether the hub marks this port as non-removable. Do it
+        * now so that platform-specific data can override it in
+        * device_add()
+        */
+       if (udev->parent)
+               set_usb_port_removable(udev);
+
        /* Register the device.  The device driver is responsible
         * for configuring the device and invoking the add-device
         * notifier chain (used by usbfs and possibly others).
@@ -2027,7 +2066,7 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
 #define SET_ADDRESS_TRIES      2
 #define GET_DESCRIPTOR_TRIES   2
 #define SET_CONFIG_TRIES       (2 * (use_both_schemes + 1))
-#define USE_NEW_SCHEME(i)      ((i) / 2 == old_scheme_first)
+#define USE_NEW_SCHEME(i)      ((i) / 2 == (int)old_scheme_first)
 
 #define HUB_ROOT_RESET_TIME    50      /* times are in msec */
 #define HUB_SHORT_RESET_TIME   10
@@ -2382,11 +2421,26 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
         * we don't explicitly enable it here.
         */
        if (udev->do_remote_wakeup) {
-               status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                               USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
-                               USB_DEVICE_REMOTE_WAKEUP, 0,
-                               NULL, 0,
-                               USB_CTRL_SET_TIMEOUT);
+               if (!hub_is_superspeed(hub->hdev)) {
+                       status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                       USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
+                                       USB_DEVICE_REMOTE_WAKEUP, 0,
+                                       NULL, 0,
+                                       USB_CTRL_SET_TIMEOUT);
+               } else {
+                       /* Assume there's only one function on the USB 3.0
+                        * device and enable remote wake for the first
+                        * interface. FIXME if the interface association
+                        * descriptor shows there's more than one function.
+                        */
+                       status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                       USB_REQ_SET_FEATURE,
+                                       USB_RECIP_INTERFACE,
+                                       USB_INTRF_FUNC_SUSPEND,
+                                       USB_INTRF_FUNC_SUSPEND_RW,
+                                       NULL, 0,
+                                       USB_CTRL_SET_TIMEOUT);
+               }
                if (status) {
                        dev_dbg(&udev->dev, "won't remote wakeup, status %d\n",
                                        status);