Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Andiry Xu and committed by
Greg Kroah-Hartman
c1045e87 65580b43

+99 -1
+15
Documentation/ABI/testing/sysfs-bus-usb
··· 142 142 such devices. 143 143 Users: 144 144 usb_modeswitch 145 + 146 + What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm 147 + Date: September 2011 148 + Contact: Andiry Xu <andiry.xu@amd.com> 149 + Description: 150 + If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device 151 + is plugged in to a xHCI host which support link PM, it will 152 + perform a LPM test; if the test is passed and host supports 153 + USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will 154 + be enabled for the device and the USB device directory will 155 + contain a file named power/usb2_hardware_lpm. The file holds 156 + a string value (enable or disable) indicating whether or not 157 + USB2 hardware LPM is enabled for the device. Developer can 158 + write y/Y/1 or n/N/0 to the file to enable/disable the 159 + feature.
+26
Documentation/usb/power-management.txt
··· 487 487 resume as soon as the system suspend is complete. Or the remote 488 488 wakeup may fail and get lost. Which outcome occurs depends on timing 489 489 and on the hardware and firmware design. 490 + 491 + 492 + xHCI hardware link PM 493 + --------------------- 494 + 495 + xHCI host controller provides hardware link power management to usb2.0 496 + (xHCI 1.0 feature) and usb3.0 devices which support link PM. By 497 + enabling hardware LPM, the host can automatically put the device into 498 + lower power state(L1 for usb2.0 devices, or U1/U2 for usb3.0 devices), 499 + which state device can enter and resume very quickly. 500 + 501 + The user interface for controlling USB2 hardware LPM is located in the 502 + power/ subdirectory of each USB device's sysfs directory, that is, in 503 + /sys/bus/usb/devices/.../power/ where "..." is the device's ID. The 504 + relevant attribute files is usb2_hardware_lpm. 505 + 506 + power/usb2_hardware_lpm 507 + 508 + When a USB2 device which support LPM is plugged to a 509 + xHCI host root hub which support software LPM, the 510 + host will run a software LPM test for it; if the device 511 + enters L1 state and resume successfully and the host 512 + supports USB2 hardware LPM, this file will show up and 513 + driver will enable hardware LPM for the device. You 514 + can write y/Y/1 or n/N/0 to the file to enable/disable 515 + USB2 hardware LPM manually. This is for test purpose mainly.
+58 -1
drivers/usb/core/sysfs.c
··· 412 412 413 413 static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); 414 414 415 + static ssize_t 416 + show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, 417 + char *buf) 418 + { 419 + struct usb_device *udev = to_usb_device(dev); 420 + const char *p; 421 + 422 + if (udev->usb2_hw_lpm_enabled == 1) 423 + p = "enabled"; 424 + else 425 + p = "disabled"; 426 + 427 + return sprintf(buf, "%s\n", p); 428 + } 429 + 430 + static ssize_t 431 + set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, 432 + const char *buf, size_t count) 433 + { 434 + struct usb_device *udev = to_usb_device(dev); 435 + bool value; 436 + int ret; 437 + 438 + usb_lock_device(udev); 439 + 440 + ret = strtobool(buf, &value); 441 + 442 + if (!ret) 443 + ret = usb_set_usb2_hardware_lpm(udev, value); 444 + 445 + usb_unlock_device(udev); 446 + 447 + if (!ret) 448 + return count; 449 + 450 + return ret; 451 + } 452 + 453 + static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, 454 + set_usb2_hardware_lpm); 455 + 456 + static struct attribute *usb2_hardware_lpm_attr[] = { 457 + &dev_attr_usb2_hardware_lpm.attr, 458 + NULL, 459 + }; 460 + static struct attribute_group usb2_hardware_lpm_attr_group = { 461 + .name = power_group_name, 462 + .attrs = usb2_hardware_lpm_attr, 463 + }; 464 + 415 465 static struct attribute *power_attrs[] = { 416 466 &dev_attr_autosuspend.attr, 417 467 &dev_attr_level.attr, ··· 478 428 { 479 429 int rc = 0; 480 430 481 - if (is_usb_device(dev)) 431 + if (is_usb_device(dev)) { 432 + struct usb_device *udev = to_usb_device(dev); 482 433 rc = sysfs_merge_group(&dev->kobj, &power_attr_group); 434 + if (udev->usb2_hw_lpm_capable == 1) 435 + rc = sysfs_merge_group(&dev->kobj, 436 + &usb2_hardware_lpm_attr_group); 437 + } 438 + 483 439 return rc; 484 440 } 485 441 486 442 static void remove_power_attributes(struct device *dev) 487 443 { 444 + sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group); 488 445 sysfs_unmerge_group(&dev->kobj, &power_attr_group); 489 446 } 490 447