Commit c1045e87 authored by Andiry Xu's avatar Andiry Xu Committed by Greg Kroah-Hartman

usbcore: add sysfs support to xHCI usb2 hardware LPM

This patch adds sysfs support to xHCI usb2 hardware LPM, so developer can
enable and disable usb2 hardware LPM manually for test purpose.
Signed-off-by: default avatarAndiry Xu <andiry.xu@amd.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 65580b43
...@@ -142,3 +142,18 @@ Description: ...@@ -142,3 +142,18 @@ Description:
such devices. such devices.
Users: Users:
usb_modeswitch usb_modeswitch
What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm
Date: September 2011
Contact: Andiry Xu <andiry.xu@amd.com>
Description:
If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device
is plugged in to a xHCI host which support link PM, it will
perform a LPM test; if the test is passed and host supports
USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will
be enabled for the device and the USB device directory will
contain a file named power/usb2_hardware_lpm. The file holds
a string value (enable or disable) indicating whether or not
USB2 hardware LPM is enabled for the device. Developer can
write y/Y/1 or n/N/0 to the file to enable/disable the
feature.
...@@ -487,3 +487,29 @@ succeed, it may still remain active and thus cause the system to ...@@ -487,3 +487,29 @@ succeed, it may still remain active and thus cause the system to
resume as soon as the system suspend is complete. Or the remote resume as soon as the system suspend is complete. Or the remote
wakeup may fail and get lost. Which outcome occurs depends on timing wakeup may fail and get lost. Which outcome occurs depends on timing
and on the hardware and firmware design. and on the hardware and firmware design.
xHCI hardware link PM
---------------------
xHCI host controller provides hardware link power management to usb2.0
(xHCI 1.0 feature) and usb3.0 devices which support link PM. By
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
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.
power/usb2_hardware_lpm
When a USB2 device which support LPM is plugged to a
xHCI host root hub which support software LPM, the
host will run a software LPM test for it; if the device
enters L1 state and resume successfully and the host
supports USB2 hardware LPM, this file will show up and
driver will enable hardware LPM for the device. You
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.
...@@ -412,6 +412,56 @@ set_level(struct device *dev, struct device_attribute *attr, ...@@ -412,6 +412,56 @@ set_level(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
static ssize_t
show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_device *udev = to_usb_device(dev);
const char *p;
if (udev->usb2_hw_lpm_enabled == 1)
p = "enabled";
else
p = "disabled";
return sprintf(buf, "%s\n", p);
}
static ssize_t
set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
bool value;
int ret;
usb_lock_device(udev);
ret = strtobool(buf, &value);
if (!ret)
ret = usb_set_usb2_hardware_lpm(udev, value);
usb_unlock_device(udev);
if (!ret)
return count;
return ret;
}
static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm,
set_usb2_hardware_lpm);
static struct attribute *usb2_hardware_lpm_attr[] = {
&dev_attr_usb2_hardware_lpm.attr,
NULL,
};
static struct attribute_group usb2_hardware_lpm_attr_group = {
.name = power_group_name,
.attrs = usb2_hardware_lpm_attr,
};
static struct attribute *power_attrs[] = { static struct attribute *power_attrs[] = {
&dev_attr_autosuspend.attr, &dev_attr_autosuspend.attr,
&dev_attr_level.attr, &dev_attr_level.attr,
...@@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev) ...@@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev)
{ {
int rc = 0; int rc = 0;
if (is_usb_device(dev)) if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev);
rc = sysfs_merge_group(&dev->kobj, &power_attr_group); rc = sysfs_merge_group(&dev->kobj, &power_attr_group);
if (udev->usb2_hw_lpm_capable == 1)
rc = sysfs_merge_group(&dev->kobj,
&usb2_hardware_lpm_attr_group);
}
return rc; return rc;
} }
static void remove_power_attributes(struct device *dev) static void remove_power_attributes(struct device *dev)
{ {
sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group);
sysfs_unmerge_group(&dev->kobj, &power_attr_group); sysfs_unmerge_group(&dev->kobj, &power_attr_group);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment