Commit 17f34867 authored by Mathias Nyman's avatar Mathias Nyman Committed by Sarah Sharp

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: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
parent a558ccdc
......@@ -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)
......@@ -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 = {
......
......@@ -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;
......
......@@ -393,6 +393,22 @@ enum usb_port_connect_type {
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.
*
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment