usb: core: lpm: fix usb3_hardware_lpm sysfs node
authorLu Baolu <baolu.lu@linux.intel.com>
Sat, 14 Nov 2015 08:26:32 +0000 (16:26 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 31 Jan 2016 19:28:58 +0000 (11:28 -0800)
commit bf5ce5bf3cc7136fd7fe5e8999a580bc93a9c8f6 upstream.

Commit 655fe4effe0f ("usbcore: add sysfs support to xHCI usb3
hardware LPM") introduced usb3_hardware_lpm sysfs node. This
doesn't show the correct status of USB3 U1 and U2 LPM status.

This patch fixes this by replacing usb3_hardware_lpm with two
nodes, usb3_hardware_lpm_u1 (for U1) and usb3_hardware_lpm_u2
(for U2), and recording the U1/U2 LPM status in right places.

This patch should be back-ported to kernels as old as 4.3,
that contains Commit 655fe4effe0f ("usbcore: add sysfs support
to xHCI usb3 hardware LPM").

Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/sysfs-bus-usb
Documentation/usb/power-management.txt
drivers/usb/core/hub.c
drivers/usb/core/sysfs.c
include/linux/usb.h

index 3a4abfc44f5e0d8a9e358626c1ca8251d756003d..136ba17d2da0558ad8ac129bc193e98486df0c0e 100644 (file)
@@ -134,19 +134,21 @@ Description:
                enabled for the device. Developer can write y/Y/1 or n/N/0 to
                the file to enable/disable the feature.
 
-What:          /sys/bus/usb/devices/.../power/usb3_hardware_lpm
-Date:          June 2015
+What:          /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u1
+               /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u2
+Date:          November 2015
 Contact:       Kevin Strasser <kevin.strasser@linux.intel.com>
+               Lu Baolu <baolu.lu@linux.intel.com>
 Description:
                If CONFIG_PM is set and a USB 3.0 lpm-capable device is plugged
                in to a xHCI host which supports link PM, it will check if U1
                and U2 exit latencies have been set in the BOS descriptor; if
-               the check is is passed and the host supports USB3 hardware LPM,
+               the check is passed and the host supports USB3 hardware LPM,
                USB3 hardware LPM will be enabled for the device and the USB
-               device directory will contain a file named
-               power/usb3_hardware_lpm. The file holds a string value (enable
-               or disable) indicating whether or not USB3 hardware LPM is
-               enabled for the device.
+               device directory will contain two files named
+               power/usb3_hardware_lpm_u1 and power/usb3_hardware_lpm_u2. These
+               files hold a string value (enable or disable) indicating whether
+               or not USB3 hardware LPM U1 or U2 is enabled for the device.
 
 What:          /sys/bus/usb/devices/.../removable
 Date:          February 2012
index 4a15c90bc11d93d362b0397899091d048992a545..0a94ffe17ab6f1d3b11774924411431da08a3385 100644 (file)
@@ -537,17 +537,18 @@ relevant attribute files are usb2_hardware_lpm and usb3_hardware_lpm.
                can write y/Y/1 or n/N/0 to the file to enable/disable
                USB2 hardware LPM manually. This is for test purpose mainly.
 
-       power/usb3_hardware_lpm
+       power/usb3_hardware_lpm_u1
+       power/usb3_hardware_lpm_u2
 
                When a USB 3.0 lpm-capable device is plugged in to a
                xHCI host which supports link PM, it will check if U1
                and U2 exit latencies have been set in the BOS
                descriptor; if the check is is passed and the host
                supports USB3 hardware LPM, USB3 hardware LPM will be
-               enabled for the device and this file will be created.
-               The file holds a string value (enable or disable)
-               indicating whether or not USB3 hardware LPM is
-               enabled for the device.
+               enabled for the device and these files will be created.
+               The files hold a string value (enable or disable)
+               indicating whether or not USB3 hardware LPM U1 or U2
+               is enabled for the device.
 
        USB Port Power Control
        ----------------------
index ddbf32d599cba4e51aeb81eea8519500a8bbea01..8683436788c30fde0d142872ee3a516996da9b9a 100644 (file)
@@ -3895,17 +3895,30 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
                return;
        }
 
-       if (usb_set_lpm_timeout(udev, state, timeout))
+       if (usb_set_lpm_timeout(udev, state, timeout)) {
                /* If we can't set the parent hub U1/U2 timeout,
                 * device-initiated LPM won't be allowed either, so let the xHCI
                 * host know that this link state won't be enabled.
                 */
                hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
+       } else {
+               /* Only a configured device will accept the Set Feature
+                * U1/U2_ENABLE
+                */
+               if (udev->actconfig)
+                       usb_set_device_initiated_lpm(udev, state, true);
 
-       /* Only a configured device will accept the Set Feature U1/U2_ENABLE */
-       else if (udev->actconfig)
-               usb_set_device_initiated_lpm(udev, state, true);
-
+               /* As soon as usb_set_lpm_timeout(timeout) returns 0, the
+                * hub-initiated LPM is enabled. Thus, LPM is enabled no
+                * matter the result of usb_set_device_initiated_lpm().
+                * The only difference is whether device is able to initiate
+                * LPM.
+                */
+               if (state == USB3_LPM_U1)
+                       udev->usb3_lpm_u1_enabled = 1;
+               else if (state == USB3_LPM_U2)
+                       udev->usb3_lpm_u2_enabled = 1;
+       }
 }
 
 /*
@@ -3945,6 +3958,18 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
                dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
                                "bus schedule bandwidth may be impacted.\n",
                                usb3_lpm_names[state]);
+
+       /* As soon as usb_set_lpm_timeout(0) return 0, hub initiated LPM
+        * is disabled. Hub will disallows link to enter U1/U2 as well,
+        * even device is initiating LPM. Hence LPM is disabled if hub LPM
+        * timeout set to 0, no matter device-initiated LPM is disabled or
+        * not.
+        */
+       if (state == USB3_LPM_U1)
+               udev->usb3_lpm_u1_enabled = 0;
+       else if (state == USB3_LPM_U2)
+               udev->usb3_lpm_u2_enabled = 0;
+
        return 0;
 }
 
@@ -3979,8 +4004,6 @@ int usb_disable_lpm(struct usb_device *udev)
        if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
                goto enable_lpm;
 
-       udev->usb3_lpm_enabled = 0;
-
        return 0;
 
 enable_lpm:
@@ -4038,8 +4061,6 @@ void usb_enable_lpm(struct usb_device *udev)
 
        usb_enable_link_state(hcd, udev, USB3_LPM_U1);
        usb_enable_link_state(hcd, udev, USB3_LPM_U2);
-
-       udev->usb3_lpm_enabled = 1;
 }
 EXPORT_SYMBOL_GPL(usb_enable_lpm);
 
index d9ec2de6c4cf8dd38bb7afb85399448f88992961..65b6e6b840431a7b91184fccd110390678e3bb8e 100644 (file)
@@ -531,7 +531,7 @@ static ssize_t usb2_lpm_besl_store(struct device *dev,
 }
 static DEVICE_ATTR_RW(usb2_lpm_besl);
 
-static ssize_t usb3_hardware_lpm_show(struct device *dev,
+static ssize_t usb3_hardware_lpm_u1_show(struct device *dev,
                                      struct device_attribute *attr, char *buf)
 {
        struct usb_device *udev = to_usb_device(dev);
@@ -539,7 +539,7 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
 
        usb_lock_device(udev);
 
-       if (udev->usb3_lpm_enabled)
+       if (udev->usb3_lpm_u1_enabled)
                p = "enabled";
        else
                p = "disabled";
@@ -548,7 +548,26 @@ static ssize_t usb3_hardware_lpm_show(struct device *dev,
 
        return sprintf(buf, "%s\n", p);
 }
-static DEVICE_ATTR_RO(usb3_hardware_lpm);
+static DEVICE_ATTR_RO(usb3_hardware_lpm_u1);
+
+static ssize_t usb3_hardware_lpm_u2_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct usb_device *udev = to_usb_device(dev);
+       const char *p;
+
+       usb_lock_device(udev);
+
+       if (udev->usb3_lpm_u2_enabled)
+               p = "enabled";
+       else
+               p = "disabled";
+
+       usb_unlock_device(udev);
+
+       return sprintf(buf, "%s\n", p);
+}
+static DEVICE_ATTR_RO(usb3_hardware_lpm_u2);
 
 static struct attribute *usb2_hardware_lpm_attr[] = {
        &dev_attr_usb2_hardware_lpm.attr,
@@ -562,7 +581,8 @@ static struct attribute_group usb2_hardware_lpm_attr_group = {
 };
 
 static struct attribute *usb3_hardware_lpm_attr[] = {
-       &dev_attr_usb3_hardware_lpm.attr,
+       &dev_attr_usb3_hardware_lpm_u1.attr,
+       &dev_attr_usb3_hardware_lpm_u2.attr,
        NULL,
 };
 static struct attribute_group usb3_hardware_lpm_attr_group = {
@@ -592,7 +612,8 @@ static int add_power_attributes(struct device *dev)
                if (udev->usb2_hw_lpm_capable == 1)
                        rc = sysfs_merge_group(&dev->kobj,
                                        &usb2_hardware_lpm_attr_group);
-               if (udev->lpm_capable == 1)
+               if (udev->speed == USB_SPEED_SUPER &&
+                               udev->lpm_capable == 1)
                        rc = sysfs_merge_group(&dev->kobj,
                                        &usb3_hardware_lpm_attr_group);
        }
index b9a28074210f218e05383099b8c043d4ca9fff93..b79925dd2b41165edf9282d1628fe7628d1bde4b 100644 (file)
@@ -511,6 +511,8 @@ struct usb3_lpm_parameters {
  * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
  * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
  * @usb3_lpm_enabled: USB3 hardware LPM enabled
+ * @usb3_lpm_u1_enabled: USB3 hardware U1 LPM enabled
+ * @usb3_lpm_u2_enabled: USB3 hardware U2 LPM enabled
  * @string_langid: language ID for strings
  * @product: iProduct string, if present (static)
  * @manufacturer: iManufacturer string, if present (static)
@@ -584,6 +586,8 @@ struct usb_device {
        unsigned usb2_hw_lpm_enabled:1;
        unsigned usb2_hw_lpm_allowed:1;
        unsigned usb3_lpm_enabled:1;
+       unsigned usb3_lpm_u1_enabled:1;
+       unsigned usb3_lpm_u2_enabled:1;
        int string_langid;
 
        /* static strings from the device */