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: 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) 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, 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 = { 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) 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) { @@ -4101,7 +4101,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, (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); @@ -4191,6 +4191,8 @@ int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) 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; 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 { }; /* + * 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; |