| From d9bcaf6868ca8f304dd76246251154cfd2bd0b50 Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Thu, 23 May 2013 17:14:31 +0300 |
| Subject: usb: add usb2 Link PM variables to sysfs and usb_device |
| |
| Adds abitilty to tune L1 timeout (inactivity timer for usb2 link sleep) |
| and BESL (best effort service latency)via sysfs. |
| |
| This also adds a new usb2_lpm_parameters structure with those variables to |
| struct usb_device. |
| |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| (cherry picked from commit 17f34867e98d2fb0c03918faab79efb989fa134b) |
| Signed-off-by: Benson Leung <bleung@chromium.org> |
| |
| |
| Reviewed-on: https://chromium-review.googlesource.com/169213 |
| Reviewed-by: Julius Werner <jwerner@chromium.org> |
| Commit-Queue: Benson Leung <bleung@chromium.org> |
| Tested-by: Benson Leung <bleung@chromium.org> |
| Signed-off-by: Darren Hart <dvhart@linux.intel.com> |
| --- |
| Documentation/ABI/testing/sysfs-bus-usb | 27 ++++++++++++++++ |
| drivers/usb/core/sysfs.c | 54 ++++++++++++++++++++++++++++++++ |
| drivers/usb/host/xhci.c | 6 ++- |
| include/linux/usb.h | 18 ++++++++++ |
| 4 files changed, 103 insertions(+), 2 deletions(-) |
| |
| --- a/Documentation/ABI/testing/sysfs-bus-usb |
| +++ b/Documentation/ABI/testing/sysfs-bus-usb |
| @@ -236,3 +236,30 @@ Description: |
| This attribute is to expose these information to user space. |
| The file will read "hotplug", "wired" and "not used" if the |
| information is available, and "unknown" otherwise. |
| + |
| +What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout |
| +Date: May 2013 |
| +Contact: Mathias Nyman <mathias.nyman@linux.intel.com> |
| +Description: |
| + USB 2.0 devices may support hardware link power management (LPM) |
| + L1 sleep state. The usb2_lpm_l1_timeout attribute allows |
| + tuning the timeout for L1 inactivity timer (LPM timer), e.g. |
| + needed inactivity time before host requests the device to go to L1 sleep. |
| + Useful for power management tuning. |
| + Supported values are 0 - 65535 microseconds. |
| + |
| +What: /sys/bus/usb/devices/.../power/usb2_lpm_besl |
| +Date: May 2013 |
| +Contact: Mathias Nyman <mathias.nyman@linux.intel.com> |
| +Description: |
| + USB 2.0 devices that support hardware link power management (LPM) |
| + L1 sleep state now use a best effort service latency value (BESL) to |
| + indicate the best effort to resumption of service to the device after the |
| + initiation of the resume event. |
| + If the device does not have a preferred besl value then the host can select |
| + one instead. This usb2_lpm_besl attribute allows to tune the host selected besl |
| + value in order to tune power saving and service latency. |
| + |
| + Supported values are 0 - 15. |
| + More information on how besl values map to microseconds can be found in |
| + USB 2.0 ECN Errata for Link Power Management, section 4.10) |
| --- a/drivers/usb/core/sysfs.c |
| +++ b/drivers/usb/core/sysfs.c |
| @@ -497,8 +497,62 @@ set_usb2_hardware_lpm(struct device *dev |
| static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, |
| set_usb2_hardware_lpm); |
| |
| +static ssize_t |
| +show_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, |
| + char *buf) |
| +{ |
| + struct usb_device *udev = to_usb_device(dev); |
| + return sprintf(buf, "%d\n", udev->l1_params.timeout); |
| +} |
| + |
| +static ssize_t |
| +set_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr, |
| + const char *buf, size_t count) |
| +{ |
| + struct usb_device *udev = to_usb_device(dev); |
| + u16 timeout; |
| + |
| + if (kstrtou16(buf, 0, &timeout)) |
| + return -EINVAL; |
| + |
| + udev->l1_params.timeout = timeout; |
| + |
| + return count; |
| +} |
| + |
| +static DEVICE_ATTR(usb2_lpm_l1_timeout, S_IRUGO | S_IWUSR, |
| + show_usb2_lpm_l1_timeout, set_usb2_lpm_l1_timeout); |
| + |
| +static ssize_t |
| +show_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, |
| + char *buf) |
| +{ |
| + struct usb_device *udev = to_usb_device(dev); |
| + return sprintf(buf, "%d\n", udev->l1_params.besl); |
| +} |
| + |
| +static ssize_t |
| +set_usb2_lpm_besl(struct device *dev, struct device_attribute *attr, |
| + const char *buf, size_t count) |
| +{ |
| + struct usb_device *udev = to_usb_device(dev); |
| + u8 besl; |
| + |
| + if (kstrtou8(buf, 0, &besl) || besl > 15) |
| + return -EINVAL; |
| + |
| + udev->l1_params.besl = besl; |
| + |
| + return count; |
| +} |
| + |
| +static DEVICE_ATTR(usb2_lpm_besl, S_IRUGO | S_IWUSR, |
| + show_usb2_lpm_besl, set_usb2_lpm_besl); |
| + |
| static struct attribute *usb2_hardware_lpm_attr[] = { |
| &dev_attr_usb2_hardware_lpm.attr, |
| + &dev_attr_usb2_lpm_l1_timeout.attr, |
| + &dev_attr_usb2_lpm_besl.attr, |
| NULL, |
| }; |
| static struct attribute_group usb2_hardware_lpm_attr_group = { |
| --- a/drivers/usb/host/xhci.c |
| +++ b/drivers/usb/host/xhci.c |
| @@ -3932,7 +3932,7 @@ static int xhci_calculate_usb2_hw_lpm_pa |
| field = le32_to_cpu(udev->bos->ext_cap->bmAttributes); |
| |
| /* xHCI l1 is set in steps of 256us, xHCI 1.0 section 5.4.11.2 */ |
| - l1 = XHCI_L1_TIMEOUT / 256; |
| + l1 = udev->l1_params.timeout / 256; |
| |
| /* device has preferred BESLD */ |
| if (field & USB_BESL_DEEP_VALID) { |
| @@ -4116,7 +4116,7 @@ int xhci_set_usb2_hardware_lpm(struct us |
| (field & USB_BESL_BASELINE_VALID)) |
| hird = USB_GET_BESL_BASELINE(field); |
| else |
| - hird = XHCI_DEFAULT_BESL; |
| + hird = udev->l1_params.besl; |
| |
| exit_latency = xhci_besl_encoding[hird]; |
| spin_unlock_irqrestore(&xhci->lock, flags); |
| @@ -4206,6 +4206,8 @@ int xhci_update_device(struct usb_hcd *h |
| if (xhci->hw_lpm_support == 1 && |
| xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) { |
| udev->usb2_hw_lpm_capable = 1; |
| + udev->l1_params.timeout = XHCI_L1_TIMEOUT; |
| + udev->l1_params.besl = XHCI_DEFAULT_BESL; |
| if (xhci_check_usb2_port_capability(xhci, portnum, |
| XHCI_BLC)) |
| udev->usb2_hw_lpm_besl_capable = 1; |
| --- a/include/linux/usb.h |
| +++ b/include/linux/usb.h |
| @@ -394,6 +394,22 @@ enum usb_port_connect_type { |
| }; |
| |
| /* |
| + * USB 2.0 Link Power Management (LPM) parameters. |
| + */ |
| +struct usb2_lpm_parameters { |
| + /* Best effort service latency indicate how long the host will drive |
| + * resume on an exit from L1. |
| + */ |
| + unsigned int besl; |
| + |
| + /* Timeout value in microseconds for the L1 inactivity (LPM) timer. |
| + * When the timer counts to zero, the parent hub will initiate a LPM |
| + * transition to L1. |
| + */ |
| + int timeout; |
| +}; |
| + |
| +/* |
| * USB 3.0 Link Power Management (LPM) parameters. |
| * |
| * PEL and SEL are USB 3.0 Link PM latencies for device-initiated LPM exit. |
| @@ -488,6 +504,7 @@ struct usb3_lpm_parameters { |
| * specific data for the device. |
| * @slot_id: Slot ID assigned by xHCI |
| * @removable: Device can be physically removed from this port |
| + * @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout. |
| * @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout. |
| * @u2_params: exit latencies for USB3 U2 LPM state, and hub-initiated timeout. |
| * @lpm_disable_count: Ref count used by usb_disable_lpm() and usb_enable_lpm() |
| @@ -568,6 +585,7 @@ struct usb_device { |
| struct wusb_dev *wusb_dev; |
| int slot_id; |
| enum usb_device_removable removable; |
| + struct usb2_lpm_parameters l1_params; |
| struct usb3_lpm_parameters u1_params; |
| struct usb3_lpm_parameters u2_params; |
| unsigned lpm_disable_count; |