[RFC 2/2] USB: Enable Latency Tolerance Messaging (LTM).

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux