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 <[email protected]>
---
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 e5cc763..e935625 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 <[email protected]>
+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 <[email protected]>
diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt
index b5f8391..4a15c90 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 43cb2f2..d9ce8f9 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 d269738..cfc68c1 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;
--
1.9.1