diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-bus-usb | 27 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 54 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 6 | ||||
-rw-r--r-- | include/linux/usb.h | 18 |
4 files changed, 103 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index f093e59cbe5f..9759b8c91332 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb | |||
@@ -236,3 +236,30 @@ Description: | |||
236 | This attribute is to expose these information to user space. | 236 | This attribute is to expose these information to user space. |
237 | The file will read "hotplug", "wired" and "not used" if the | 237 | The file will read "hotplug", "wired" and "not used" if the |
238 | information is available, and "unknown" otherwise. | 238 | information is available, and "unknown" otherwise. |
239 | |||
240 | What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout | ||
241 | Date: May 2013 | ||
242 | Contact: Mathias Nyman <mathias.nyman@linux.intel.com> | ||
243 | Description: | ||
244 | USB 2.0 devices may support hardware link power management (LPM) | ||
245 | L1 sleep state. The usb2_lpm_l1_timeout attribute allows | ||
246 | tuning the timeout for L1 inactivity timer (LPM timer), e.g. | ||
247 | needed inactivity time before host requests the device to go to L1 sleep. | ||
248 | Useful for power management tuning. | ||
249 | Supported values are 0 - 65535 microseconds. | ||
250 | |||
251 | What: /sys/bus/usb/devices/.../power/usb2_lpm_besl | ||
252 | Date: May 2013 | ||
253 | Contact: Mathias Nyman <mathias.nyman@linux.intel.com> | ||
254 | Description: | ||
255 | USB 2.0 devices that support hardware link power management (LPM) | ||
256 | L1 sleep state now use a best effort service latency value (BESL) to | ||
257 | indicate the best effort to resumption of service to the device after the | ||
258 | initiation of the resume event. | ||
259 | If the device does not have a preferred besl value then the host can select | ||
260 | one instead. This usb2_lpm_besl attribute allows to tune the host selected besl | ||
261 | value in order to tune power saving and service latency. | ||
262 | |||
263 | Supported values are 0 - 15. | ||
264 | More information on how besl values map to microseconds can be found in | ||
265 | USB 2.0 ECN Errata for Link Power Management, section 4.10) | ||
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index aa38db44818a..d9284b998bd7 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -497,8 +497,62 @@ set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, | |||
497 | static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, | 497 | static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, |
498 | set_usb2_hardware_lpm); | 498 | set_usb2_hardware_lpm); |
499 | 499 | ||
500 | static ssize_t | ||
501 | show_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, | ||
502 | char *buf) | ||
503 | { | ||
504 | struct usb_device *udev = to_usb_device(dev); | ||
505 | return sprintf(buf, "%d\n", udev->l1_params.timeout); | ||
506 | } | ||
507 | |||
508 | static ssize_t | ||
509 | set_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, | ||
510 | const char *buf, size_t count) | ||
511 | { | ||
512 | struct usb_device *udev = to_usb_device(dev); | ||
513 | u16 timeout; | ||
514 | |||
515 | if (kstrtou16(buf, 0, &timeout)) | ||
516 | return -EINVAL; | ||
517 | |||
518 | udev->l1_params.timeout = timeout; | ||
519 | |||
520 | return count; | ||
521 | } | ||
522 | |||
523 | static DEVICE_ATTR(usb2_lpm_l1_timeout, S_IRUGO | S_IWUSR, | ||
524 | show_usb2_lpm_l1_timeout, set_usb2_lpm_l1_timeout); | ||
525 | |||
526 | static ssize_t | ||
527 | show_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, | ||
528 | char *buf) | ||
529 | { | ||
530 | struct usb_device *udev = to_usb_device(dev); | ||
531 | return sprintf(buf, "%d\n", udev->l1_params.besl); | ||
532 | } | ||
533 | |||
534 | static ssize_t | ||
535 | set_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, | ||
536 | const char *buf, size_t count) | ||
537 | { | ||
538 | struct usb_device *udev = to_usb_device(dev); | ||
539 | u8 besl; | ||
540 | |||
541 | if (kstrtou8(buf, 0, &besl) || besl > 15) | ||
542 | return -EINVAL; | ||
543 | |||
544 | udev->l1_params.besl = besl; | ||
545 | |||
546 | return count; | ||
547 | } | ||
548 | |||
549 | static DEVICE_ATTR(usb2_lpm_besl, S_IRUGO | S_IWUSR, | ||
550 | show_usb2_lpm_besl, set_usb2_lpm_besl); | ||
551 | |||
500 | static struct attribute *usb2_hardware_lpm_attr[] = { | 552 | static struct attribute *usb2_hardware_lpm_attr[] = { |
501 | &dev_attr_usb2_hardware_lpm.attr, | 553 | &dev_attr_usb2_hardware_lpm.attr, |
554 | &dev_attr_usb2_lpm_l1_timeout.attr, | ||
555 | &dev_attr_usb2_lpm_besl.attr, | ||
502 | NULL, | 556 | NULL, |
503 | }; | 557 | }; |
504 | static struct attribute_group usb2_hardware_lpm_attr_group = { | 558 | static struct attribute_group usb2_hardware_lpm_attr_group = { |
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3d34a0eed088..8be34f838bd4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c | |||
@@ -3917,7 +3917,7 @@ static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev) | |||
3917 | field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); | 3917 | field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); |
3918 | 3918 | ||
3919 | /* xHCI l1 is set in steps of 256us, xHCI 1.0 section 5.4.11.2 */ | 3919 | /* xHCI l1 is set in steps of 256us, xHCI 1.0 section 5.4.11.2 */ |
3920 | l1 = XHCI_L1_TIMEOUT / 256; | 3920 | l1 = udev->l1_params.timeout / 256; |
3921 | 3921 | ||
3922 | /* device has preferred BESLD */ | 3922 | /* device has preferred BESLD */ |
3923 | if (field & USB_BESL_DEEP_VALID) { | 3923 | if (field & USB_BESL_DEEP_VALID) { |
@@ -4101,7 +4101,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, | |||
4101 | (field & USB_BESL_BASELINE_VALID)) | 4101 | (field & USB_BESL_BASELINE_VALID)) |
4102 | hird = USB_GET_BESL_BASELINE(field); | 4102 | hird = USB_GET_BESL_BASELINE(field); |
4103 | else | 4103 | else |
4104 | hird = XHCI_DEFAULT_BESL; | 4104 | hird = udev->l1_params.besl; |
4105 | 4105 | ||
4106 | exit_latency = xhci_besl_encoding[hird]; | 4106 | exit_latency = xhci_besl_encoding[hird]; |
4107 | spin_unlock_irqrestore(&xhci->lock, flags); | 4107 | spin_unlock_irqrestore(&xhci->lock, flags); |
@@ -4191,6 +4191,8 @@ int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) | |||
4191 | if (xhci->hw_lpm_support == 1 && | 4191 | if (xhci->hw_lpm_support == 1 && |
4192 | xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) { | 4192 | xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) { |
4193 | udev->usb2_hw_lpm_capable = 1; | 4193 | udev->usb2_hw_lpm_capable = 1; |
4194 | udev->l1_params.timeout = XHCI_L1_TIMEOUT; | ||
4195 | udev->l1_params.besl = XHCI_DEFAULT_BESL; | ||
4194 | if (xhci_check_usb2_port_capability(xhci, portnum, | 4196 | if (xhci_check_usb2_port_capability(xhci, portnum, |
4195 | XHCI_BLC)) | 4197 | XHCI_BLC)) |
4196 | udev->usb2_hw_lpm_besl_capable = 1; | 4198 | udev->usb2_hw_lpm_besl_capable = 1; |
diff --git a/include/linux/usb.h b/include/linux/usb.h index fe444989668a..a232b7ece1f6 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -394,6 +394,22 @@ enum usb_port_connect_type { | |||
394 | }; | 394 | }; |
395 | 395 | ||
396 | /* | 396 | /* |
397 | * USB 2.0 Link Power Management (LPM) parameters. | ||
398 | */ | ||
399 | struct usb2_lpm_parameters { | ||
400 | /* Best effort service latency indicate how long the host will drive | ||
401 | * resume on an exit from L1. | ||
402 | */ | ||
403 | unsigned int besl; | ||
404 | |||
405 | /* Timeout value in microseconds for the L1 inactivity (LPM) timer. | ||
406 | * When the timer counts to zero, the parent hub will initiate a LPM | ||
407 | * transition to L1. | ||
408 | */ | ||
409 | int timeout; | ||
410 | }; | ||
411 | |||
412 | /* | ||
397 | * USB 3.0 Link Power Management (LPM) parameters. | 413 | * USB 3.0 Link Power Management (LPM) parameters. |
398 | * | 414 | * |
399 | * PEL and SEL are USB 3.0 Link PM latencies for device-initiated LPM exit. | 415 | * PEL and SEL are USB 3.0 Link PM latencies for device-initiated LPM exit. |
@@ -488,6 +504,7 @@ struct usb3_lpm_parameters { | |||
488 | * specific data for the device. | 504 | * specific data for the device. |
489 | * @slot_id: Slot ID assigned by xHCI | 505 | * @slot_id: Slot ID assigned by xHCI |
490 | * @removable: Device can be physically removed from this port | 506 | * @removable: Device can be physically removed from this port |
507 | * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout. | ||
491 | * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout. | 508 | * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout. |
492 | * @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout. | 509 | * @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout. |
493 | * @lpm_disable_count: Ref count used by usb_disable_lpm() and usb_enable_lpm() | 510 | * @lpm_disable_count: Ref count used by usb_disable_lpm() and usb_enable_lpm() |
@@ -568,6 +585,7 @@ struct usb_device { | |||
568 | struct wusb_dev *wusb_dev; | 585 | struct wusb_dev *wusb_dev; |
569 | int slot_id; | 586 | int slot_id; |
570 | enum usb_device_removable removable; | 587 | enum usb_device_removable removable; |
588 | struct usb2_lpm_parameters l1_params; | ||
571 | struct usb3_lpm_parameters u1_params; | 589 | struct usb3_lpm_parameters u1_params; |
572 | struct usb3_lpm_parameters u2_params; | 590 | struct usb3_lpm_parameters u2_params; |
573 | unsigned lpm_disable_count; | 591 | unsigned lpm_disable_count; |