From 655fe4effe0f1f40e4f6ca6b3cc64a7fe0032183 Mon Sep 17 00:00:00 2001 From: Kevin Strasser Date: Tue, 16 Jun 2015 10:35:30 -0700 Subject: [PATCH] usbcore: add sysfs support to xHCI usb3 hardware LPM 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 Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-usb | 14 +++++++++++ Documentation/usb/power-management.txt | 15 ++++++++++-- drivers/usb/core/hub.c | 4 ++++ drivers/usb/core/sysfs.c | 31 +++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index e5cc7633d013..e935625f5995 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -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 +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 diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index b5f83911732a..4a15c90bc11d 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -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 ---------------------- diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 43cb2f2e3b43..d9ce8f9d9acc 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index d26973844a4d..cfc68c11c3f5 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -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; -- 2.34.1