usb: add usb2 Link PM variables to sysfs and usb_device
authorMathias Nyman <mathias.nyman@linux.intel.com>
Thu, 23 May 2013 14:14:31 +0000 (17:14 +0300)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Wed, 5 Jun 2013 23:48:40 +0000 (16:48 -0700)
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>
Documentation/ABI/testing/sysfs-bus-usb
drivers/usb/core/sysfs.c
drivers/usb/host/xhci.c
include/linux/usb.h

index f093e59cbe5f017531b74ce45dcc3d130ddfd44d..9759b8c913329cde110754b84438133a4738dcd7 100644 (file)
@@ -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.
                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)
index aa38db44818a99ab9331c11d2c670669d014498f..d9284b998bd7238d13918c27094150ff80fd0c6c 100644 (file)
@@ -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 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,
 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 = {
        NULL,
 };
 static struct attribute_group usb2_hardware_lpm_attr_group = {
index 3d34a0eed0880fe8254989f107224e94f98b071a..8be34f838bd45e6a854c73d4e94923bf73bb928e 100644 (file)
@@ -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 */
        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) {
 
        /* 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
                            (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);
 
                        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;
                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;
                        if (xhci_check_usb2_port_capability(xhci, portnum,
                                                            XHCI_BLC))
                                udev->usb2_hw_lpm_besl_capable = 1;
index fe444989668a4306feae2149c4376089ddbf2881..a232b7ece1f63c687a2f50da506c9e70ec33555e 100644 (file)
@@ -393,6 +393,22 @@ enum usb_port_connect_type {
        USB_PORT_NOT_USED,
 };
 
        USB_PORT_NOT_USED,
 };
 
+/*
+ * 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.
  *
 /*
  * USB 3.0 Link Power Management (LPM) parameters.
  *
@@ -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
  *     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()
  * @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 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;
        struct usb3_lpm_parameters u1_params;
        struct usb3_lpm_parameters u2_params;
        unsigned lpm_disable_count;