Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -142,3 +142,18 @@ Description:
		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.
+26 −0
Original line number Diff line number Diff line
@@ -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
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.
+58 −1
Original line number Diff line number Diff line
@@ -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 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,
@@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev)
{
	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);
}