USB 3.0 devices may optionally support a new feature called Latency Tolerance Messaging. If both the xHCI host controller and the device support LTM, it should be turned on in order to give the system hardware a better clue about the latency tolerance values of its PCI devices. LTM is disabled by default on enumeration or after device reset. Once a Set Feature request to enable LTM is received, the USB 3.0 device will begin to send LTM updates as its buffers fill or empty, and it can tolerate more or less latency. Make the USB core enable LTM after the device is reset. The USB 3.0 spec, section C.4.2 says that LTM should be disabled just before the device is placed into suspend. Then the device will send an updated LTM notification, so that the system doesn't think it should remain in an active state in order to satisfy the latency requirements of the suspended device. Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> --- drivers/usb/core/hub.c | 66 +++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 63 insertions(+), 3 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 25a7422..b64d11a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2607,6 +2607,9 @@ static int check_port_resume_type(struct usb_device *udev, return status; } +static int usb_disable_ltm(struct usb_device *udev); +static void usb_enable_ltm(struct usb_device *udev); + #ifdef CONFIG_USB_SUSPEND /* @@ -2702,6 +2705,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); + if (usb_disable_ltm(udev)) { + dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.", + __func__); + return -ENOMEM; + } if (usb_unlocked_disable_lpm(udev)) { dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", __func__); @@ -2731,7 +2739,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM again */ + /* Try to enable USB3 LTM and LPM again */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); /* System sleep transitions should never fail */ @@ -2932,7 +2941,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (udev->usb2_hw_lpm_capable == 1) usb_set_usb2_hardware_lpm(udev, 1); - /* Try to enable USB3 LPM */ + /* Try to enable USB3 LTM and LPM */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); } @@ -3461,6 +3471,43 @@ void usb_unlocked_enable_lpm(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); +static bool usb_device_supports_ltm(struct usb_device *udev) +{ + if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap) + return false; + return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT; +} + +static int usb_disable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!hcd || !usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return 0; + + /* Double check whether the device supports LTM. */ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} + +static void usb_enable_ltm(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Check if the roothub and device supports LTM. */ + if (!hcd || !usb_device_supports_ltm(hcd->self.root_hub) || + !usb_device_supports_ltm(udev)) + return; + + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_LTM_ENABLE, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} #else /* CONFIG_PM */ @@ -3485,6 +3532,13 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); void usb_unlocked_enable_lpm(struct usb_device *udev) { } EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); + +static int usb_disable_ltm(struct usb_device *udev) +{ + return 0; +} + +static void usb_enable_ltm(struct usb_device *udev) { } #endif @@ -4705,6 +4759,8 @@ static int usb_reset_and_verify_device(struct usb_device *udev) if (ret) { dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); mutex_unlock(hcd->bandwidth_mutex); + /* Re-enable LTM, since the default is disabled after reset. */ + usb_enable_ltm(udev); goto done; } ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); @@ -4714,6 +4770,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) "old configuration.\n"); usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); + usb_enable_ltm(udev); goto re_enumerate; } ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -4726,6 +4783,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) udev->actconfig->desc.bConfigurationValue, ret); usb_enable_lpm(udev); mutex_unlock(hcd->bandwidth_mutex); + usb_enable_ltm(udev); goto re_enumerate; } mutex_unlock(hcd->bandwidth_mutex); @@ -4763,12 +4821,14 @@ static int usb_reset_and_verify_device(struct usb_device *udev) desc->bInterfaceNumber, desc->bAlternateSetting, ret); + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); goto re_enumerate; } } - /* Now that the alt settings are re-installed, enable LPM. */ + /* Now that the alt settings are re-installed, enable LTM and LPM. */ + usb_enable_ltm(udev); usb_unlocked_enable_lpm(udev); done: return 0; -- 1.7.9 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html