| From bf5ce5bf3cc7136fd7fe5e8999a580bc93a9c8f6 Mon Sep 17 00:00:00 2001 |
| From: Lu Baolu <baolu.lu@linux.intel.com> |
| Date: Sat, 14 Nov 2015 16:26:32 +0800 |
| Subject: usb: core: lpm: fix usb3_hardware_lpm sysfs node |
| |
| From: Lu Baolu <baolu.lu@linux.intel.com> |
| |
| commit bf5ce5bf3cc7136fd7fe5e8999a580bc93a9c8f6 upstream. |
| |
| Commit 655fe4effe0f ("usbcore: add sysfs support to xHCI usb3 |
| hardware LPM") introduced usb3_hardware_lpm sysfs node. This |
| doesn't show the correct status of USB3 U1 and U2 LPM status. |
| |
| This patch fixes this by replacing usb3_hardware_lpm with two |
| nodes, usb3_hardware_lpm_u1 (for U1) and usb3_hardware_lpm_u2 |
| (for U2), and recording the U1/U2 LPM status in right places. |
| |
| This patch should be back-ported to kernels as old as 4.3, |
| that contains Commit 655fe4effe0f ("usbcore: add sysfs support |
| to xHCI usb3 hardware LPM"). |
| |
| Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| Documentation/ABI/testing/sysfs-bus-usb | 16 +++++++------ |
| Documentation/usb/power-management.txt | 11 ++++----- |
| drivers/usb/core/hub.c | 39 ++++++++++++++++++++++++-------- |
| drivers/usb/core/sysfs.c | 31 +++++++++++++++++++++---- |
| include/linux/usb.h | 4 +++ |
| 5 files changed, 75 insertions(+), 26 deletions(-) |
| |
| --- a/Documentation/ABI/testing/sysfs-bus-usb |
| +++ b/Documentation/ABI/testing/sysfs-bus-usb |
| @@ -114,19 +114,21 @@ Description: |
| enabled for the device. Developer can write y/Y/1 or n/N/0 to |
| the file to enable/disable the feature. |
| |
| -What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm |
| -Date: June 2015 |
| +What: /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u1 |
| + /sys/bus/usb/devices/.../power/usb3_hardware_lpm_u2 |
| +Date: November 2015 |
| Contact: Kevin Strasser <kevin.strasser@linux.intel.com> |
| + Lu Baolu <baolu.lu@linux.intel.com> |
| Description: |
| If CONFIG_PM is set and a USB 3.0 lpm-capable device is plugged |
| in to a xHCI host which supports link PM, it will check if U1 |
| and U2 exit latencies have been set in the BOS descriptor; if |
| - the check is is passed and the host supports USB3 hardware LPM, |
| + the check is passed and the host supports USB3 hardware LPM, |
| USB3 hardware LPM will be enabled for the device and the USB |
| - device directory will contain a file named |
| - power/usb3_hardware_lpm. The file holds a string value (enable |
| - or disable) indicating whether or not USB3 hardware LPM is |
| - enabled for the device. |
| + device directory will contain two files named |
| + power/usb3_hardware_lpm_u1 and power/usb3_hardware_lpm_u2. These |
| + files hold a string value (enable or disable) indicating whether |
| + or not USB3 hardware LPM U1 or U2 is enabled for the device. |
| |
| What: /sys/bus/usb/devices/.../removable |
| Date: February 2012 |
| --- a/Documentation/usb/power-management.txt |
| +++ b/Documentation/usb/power-management.txt |
| @@ -537,17 +537,18 @@ relevant attribute files are usb2_hardwa |
| can write y/Y/1 or n/N/0 to the file to enable/disable |
| USB2 hardware LPM manually. This is for test purpose mainly. |
| |
| - power/usb3_hardware_lpm |
| + power/usb3_hardware_lpm_u1 |
| + power/usb3_hardware_lpm_u2 |
| |
| When a USB 3.0 lpm-capable device is plugged in to a |
| xHCI host which supports link PM, it will check if U1 |
| and U2 exit latencies have been set in the BOS |
| descriptor; if the check is is passed and the host |
| supports USB3 hardware LPM, USB3 hardware LPM will be |
| - enabled for the device and this file will be created. |
| - The file holds a string value (enable or disable) |
| - indicating whether or not USB3 hardware LPM is |
| - enabled for the device. |
| + enabled for the device and these files will be created. |
| + The files hold a string value (enable or disable) |
| + indicating whether or not USB3 hardware LPM U1 or U2 |
| + is enabled for the device. |
| |
| USB Port Power Control |
| ---------------------- |
| --- a/drivers/usb/core/hub.c |
| +++ b/drivers/usb/core/hub.c |
| @@ -3886,17 +3886,30 @@ static void usb_enable_link_state(struct |
| return; |
| } |
| |
| - if (usb_set_lpm_timeout(udev, state, timeout)) |
| + if (usb_set_lpm_timeout(udev, state, timeout)) { |
| /* If we can't set the parent hub U1/U2 timeout, |
| * device-initiated LPM won't be allowed either, so let the xHCI |
| * host know that this link state won't be enabled. |
| */ |
| hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state); |
| + } else { |
| + /* Only a configured device will accept the Set Feature |
| + * U1/U2_ENABLE |
| + */ |
| + if (udev->actconfig) |
| + usb_set_device_initiated_lpm(udev, state, true); |
| |
| - /* Only a configured device will accept the Set Feature U1/U2_ENABLE */ |
| - else if (udev->actconfig) |
| - usb_set_device_initiated_lpm(udev, state, true); |
| - |
| + /* As soon as usb_set_lpm_timeout(timeout) returns 0, the |
| + * hub-initiated LPM is enabled. Thus, LPM is enabled no |
| + * matter the result of usb_set_device_initiated_lpm(). |
| + * The only difference is whether device is able to initiate |
| + * LPM. |
| + */ |
| + if (state == USB3_LPM_U1) |
| + udev->usb3_lpm_u1_enabled = 1; |
| + else if (state == USB3_LPM_U2) |
| + udev->usb3_lpm_u2_enabled = 1; |
| + } |
| } |
| |
| /* |
| @@ -3936,6 +3949,18 @@ static int usb_disable_link_state(struct |
| dev_warn(&udev->dev, "Could not disable xHCI %s timeout, " |
| "bus schedule bandwidth may be impacted.\n", |
| usb3_lpm_names[state]); |
| + |
| + /* As soon as usb_set_lpm_timeout(0) return 0, hub initiated LPM |
| + * is disabled. Hub will disallows link to enter U1/U2 as well, |
| + * even device is initiating LPM. Hence LPM is disabled if hub LPM |
| + * timeout set to 0, no matter device-initiated LPM is disabled or |
| + * not. |
| + */ |
| + if (state == USB3_LPM_U1) |
| + udev->usb3_lpm_u1_enabled = 0; |
| + else if (state == USB3_LPM_U2) |
| + udev->usb3_lpm_u2_enabled = 0; |
| + |
| return 0; |
| } |
| |
| @@ -3970,8 +3995,6 @@ int usb_disable_lpm(struct usb_device *u |
| if (usb_disable_link_state(hcd, udev, USB3_LPM_U2)) |
| goto enable_lpm; |
| |
| - udev->usb3_lpm_enabled = 0; |
| - |
| return 0; |
| |
| enable_lpm: |
| @@ -4029,8 +4052,6 @@ void usb_enable_lpm(struct usb_device *u |
| |
| usb_enable_link_state(hcd, udev, USB3_LPM_U1); |
| usb_enable_link_state(hcd, udev, USB3_LPM_U2); |
| - |
| - udev->usb3_lpm_enabled = 1; |
| } |
| EXPORT_SYMBOL_GPL(usb_enable_lpm); |
| |
| --- a/drivers/usb/core/sysfs.c |
| +++ b/drivers/usb/core/sysfs.c |
| @@ -531,7 +531,7 @@ static ssize_t usb2_lpm_besl_store(struc |
| } |
| static DEVICE_ATTR_RW(usb2_lpm_besl); |
| |
| -static ssize_t usb3_hardware_lpm_show(struct device *dev, |
| +static ssize_t usb3_hardware_lpm_u1_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct usb_device *udev = to_usb_device(dev); |
| @@ -539,7 +539,7 @@ static ssize_t usb3_hardware_lpm_show(st |
| |
| usb_lock_device(udev); |
| |
| - if (udev->usb3_lpm_enabled) |
| + if (udev->usb3_lpm_u1_enabled) |
| p = "enabled"; |
| else |
| p = "disabled"; |
| @@ -548,7 +548,26 @@ static ssize_t usb3_hardware_lpm_show(st |
| |
| return sprintf(buf, "%s\n", p); |
| } |
| -static DEVICE_ATTR_RO(usb3_hardware_lpm); |
| +static DEVICE_ATTR_RO(usb3_hardware_lpm_u1); |
| + |
| +static ssize_t usb3_hardware_lpm_u2_show(struct device *dev, |
| + struct device_attribute *attr, char *buf) |
| +{ |
| + struct usb_device *udev = to_usb_device(dev); |
| + const char *p; |
| + |
| + usb_lock_device(udev); |
| + |
| + if (udev->usb3_lpm_u2_enabled) |
| + p = "enabled"; |
| + else |
| + p = "disabled"; |
| + |
| + usb_unlock_device(udev); |
| + |
| + return sprintf(buf, "%s\n", p); |
| +} |
| +static DEVICE_ATTR_RO(usb3_hardware_lpm_u2); |
| |
| static struct attribute *usb2_hardware_lpm_attr[] = { |
| &dev_attr_usb2_hardware_lpm.attr, |
| @@ -562,7 +581,8 @@ static struct attribute_group usb2_hardw |
| }; |
| |
| static struct attribute *usb3_hardware_lpm_attr[] = { |
| - &dev_attr_usb3_hardware_lpm.attr, |
| + &dev_attr_usb3_hardware_lpm_u1.attr, |
| + &dev_attr_usb3_hardware_lpm_u2.attr, |
| NULL, |
| }; |
| static struct attribute_group usb3_hardware_lpm_attr_group = { |
| @@ -592,7 +612,8 @@ static int add_power_attributes(struct d |
| if (udev->usb2_hw_lpm_capable == 1) |
| rc = sysfs_merge_group(&dev->kobj, |
| &usb2_hardware_lpm_attr_group); |
| - if (udev->lpm_capable == 1) |
| + if (udev->speed == USB_SPEED_SUPER && |
| + udev->lpm_capable == 1) |
| rc = sysfs_merge_group(&dev->kobj, |
| &usb3_hardware_lpm_attr_group); |
| } |
| --- a/include/linux/usb.h |
| +++ b/include/linux/usb.h |
| @@ -507,6 +507,8 @@ struct usb3_lpm_parameters { |
| * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled |
| * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled |
| * @usb3_lpm_enabled: USB3 hardware LPM enabled |
| + * @usb3_lpm_u1_enabled: USB3 hardware U1 LPM enabled |
| + * @usb3_lpm_u2_enabled: USB3 hardware U2 LPM enabled |
| * @string_langid: language ID for strings |
| * @product: iProduct string, if present (static) |
| * @manufacturer: iManufacturer string, if present (static) |
| @@ -580,6 +582,8 @@ struct usb_device { |
| unsigned usb2_hw_lpm_enabled:1; |
| unsigned usb2_hw_lpm_allowed:1; |
| unsigned usb3_lpm_enabled:1; |
| + unsigned usb3_lpm_u1_enabled:1; |
| + unsigned usb3_lpm_u2_enabled:1; |
| int string_langid; |
| |
| /* static strings from the device */ |