usbcore: add sysfs support to xHCI usb3 hardware LPM
authorKevin Strasser <kevin.strasser@linux.intel.com>
Tue, 16 Jun 2015 17:35:30 +0000 (10:35 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 22 Jul 2015 22:45:03 +0000 (15:45 -0700)
Add a sysfs node to make it easier to verify if LPM is supported and being
enabled for USB 3.0 devices.

Signed-off-by: Kevin Strasser <kevin.strasser@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

index e5cc763..e935625 100644 (file)
@@ -114,6 +114,20 @@ 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
+Contact:       Kevin Strasser <kevin.strasser@linux.intel.com>
+Description:
+               If CONFIG_PM_RUNTIME 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, 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.
+
 What:          /sys/bus/usb/devices/.../removable
 Date:          February 2012
 Contact:       Matthew Garrett <mjg@redhat.com>
index b5f8391..4a15c90 100644 (file)
@@ -521,10 +521,10 @@ enabling hardware LPM, the host can automatically put the device into
 lower power state(L1 for usb2.0 devices, or U1/U2 for usb3.0 devices),
 which state device can enter and resume very quickly.
 
-The user interface for controlling USB2 hardware LPM is located in the
+The user interface for controlling hardware LPM is located in the
 power/ subdirectory of each USB device's sysfs directory, that is, in
 /sys/bus/usb/devices/.../power/ where "..." is the device's ID. The
-relevant attribute files is usb2_hardware_lpm.
+relevant attribute files are usb2_hardware_lpm and usb3_hardware_lpm.
 
        power/usb2_hardware_lpm
 
@@ -537,6 +537,17 @@ relevant attribute files is usb2_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
+
+               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.
 
        USB Port Power Control
        ----------------------
index 43cb2f2..d9ce8f9 100644 (file)
@@ -3950,6 +3950,8 @@ 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:
@@ -4007,6 +4009,8 @@ 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 d269738..cfc68c1 100644 (file)
@@ -531,6 +531,25 @@ 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,
+                                     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_enabled)
+               p = "enabled";
+       else
+               p = "disabled";
+
+       usb_unlock_device(udev);
+
+       return sprintf(buf, "%s\n", p);
+}
+static DEVICE_ATTR_RO(usb3_hardware_lpm);
+
 static struct attribute *usb2_hardware_lpm_attr[] = {
        &dev_attr_usb2_hardware_lpm.attr,
        &dev_attr_usb2_lpm_l1_timeout.attr,
@@ -542,6 +561,15 @@ static struct attribute_group usb2_hardware_lpm_attr_group = {
        .attrs  = usb2_hardware_lpm_attr,
 };
 
+static struct attribute *usb3_hardware_lpm_attr[] = {
+       &dev_attr_usb3_hardware_lpm.attr,
+       NULL,
+};
+static struct attribute_group usb3_hardware_lpm_attr_group = {
+       .name   = power_group_name,
+       .attrs  = usb3_hardware_lpm_attr,
+};
+
 static struct attribute *power_attrs[] = {
        &dev_attr_autosuspend.attr,
        &dev_attr_level.attr,
@@ -564,6 +592,9 @@ 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)
+                       rc = sysfs_merge_group(&dev->kobj,
+                                       &usb3_hardware_lpm_attr_group);
        }
 
        return rc;