such devices.
Users:
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.
resume as soon as the system suspend is complete. Or the remote
wakeup may fail and get lost. Which outcome occurs depends on timing
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.
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[] = {
&dev_attr_autosuspend.attr,
&dev_attr_level.attr,
{
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);
+ if (udev->usb2_hw_lpm_capable == 1)
+ rc = sysfs_merge_group(&dev->kobj,
+ &usb2_hardware_lpm_attr_group);
+ }
+
return rc;
}
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);
}