aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2012-06-25 15:08:08 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-07-11 07:06:48 -0400
commitf74631e3426474183389e55f703797bd965cd356 (patch)
tree5243c755fdd3604d4be4c4cf38df640a47fae80f /drivers/usb/core
parentaf3a23ef4e5fbaf33f0afdda7d26442f036ba795 (diff)
USB: Enable Latency Tolerance Messaging (LTM).
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. 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. 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. The Set and Clear Feature LTM enable command can only be sent to a configured device. The device will respond with an error if that command is sent while it is in the Default or Addressed state. Make sure to check udev->actconfig in usb_enable_ltm() and usb_disable_ltm(), and don't send those commands when the device is unconfigured. LTM should be enabled once a new configuration is installed in usb_set_configuration(). If we end up sending duplicate Set Feature LTM Enable commands on a switch from one installed configuration to another configuration, that should be harmless. Make sure that LTM is disabled before the device is unconfigured in usb_disable_device(). If no drivers are bound to the device, it doesn't make sense to allow the device to control the latency tolerance of the xHCI host controller. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c87
-rw-r--r--drivers/usb/core/message.c3
2 files changed, 84 insertions, 6 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 5c31d2c2f95a..b5bd6bd8fd12 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2610,6 +2610,57 @@ static int check_port_resume_type(struct usb_device *udev,
2610 return status; 2610 return status;
2611} 2611}
2612 2612
2613static bool usb_device_supports_ltm(struct usb_device *udev)
2614{
2615 if (udev->speed != USB_SPEED_SUPER || !udev->bos || !udev->bos->ss_cap)
2616 return false;
2617 return udev->bos->ss_cap->bmAttributes & USB_LTM_SUPPORT;
2618}
2619
2620int usb_disable_ltm(struct usb_device *udev)
2621{
2622 struct usb_hcd *hcd = bus_to_hcd(udev->bus);
2623
2624 /* Check if the roothub and device supports LTM. */
2625 if (!usb_device_supports_ltm(hcd->self.root_hub) ||
2626 !usb_device_supports_ltm(udev))
2627 return 0;
2628
2629 /* Clear Feature LTM Enable can only be sent if the device is
2630 * configured.
2631 */
2632 if (!udev->actconfig)
2633 return 0;
2634
2635 return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
2636 USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE,
2637 USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
2638 USB_CTRL_SET_TIMEOUT);
2639}
2640EXPORT_SYMBOL_GPL(usb_disable_ltm);
2641
2642void usb_enable_ltm(struct usb_device *udev)
2643{
2644 struct usb_hcd *hcd = bus_to_hcd(udev->bus);
2645
2646 /* Check if the roothub and device supports LTM. */
2647 if (!usb_device_supports_ltm(hcd->self.root_hub) ||
2648 !usb_device_supports_ltm(udev))
2649 return;
2650
2651 /* Set Feature LTM Enable can only be sent if the device is
2652 * configured.
2653 */
2654 if (!udev->actconfig)
2655 return;
2656
2657 usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
2658 USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
2659 USB_DEVICE_LTM_ENABLE, 0, NULL, 0,
2660 USB_CTRL_SET_TIMEOUT);
2661}
2662EXPORT_SYMBOL_GPL(usb_enable_ltm);
2663
2613#ifdef CONFIG_USB_SUSPEND 2664#ifdef CONFIG_USB_SUSPEND
2614 2665
2615/* 2666/*
@@ -2705,6 +2756,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2705 if (udev->usb2_hw_lpm_enabled == 1) 2756 if (udev->usb2_hw_lpm_enabled == 1)
2706 usb_set_usb2_hardware_lpm(udev, 0); 2757 usb_set_usb2_hardware_lpm(udev, 0);
2707 2758
2759 if (usb_disable_ltm(udev)) {
2760 dev_err(&udev->dev, "%s Failed to disable LTM before suspend\n.",
2761 __func__);
2762 return -ENOMEM;
2763 }
2708 if (usb_unlocked_disable_lpm(udev)) { 2764 if (usb_unlocked_disable_lpm(udev)) {
2709 dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", 2765 dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
2710 __func__); 2766 __func__);
@@ -2734,7 +2790,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
2734 if (udev->usb2_hw_lpm_capable == 1) 2790 if (udev->usb2_hw_lpm_capable == 1)
2735 usb_set_usb2_hardware_lpm(udev, 1); 2791 usb_set_usb2_hardware_lpm(udev, 1);
2736 2792
2737 /* Try to enable USB3 LPM again */ 2793 /* Try to enable USB3 LTM and LPM again */
2794 usb_enable_ltm(udev);
2738 usb_unlocked_enable_lpm(udev); 2795 usb_unlocked_enable_lpm(udev);
2739 2796
2740 /* System sleep transitions should never fail */ 2797 /* System sleep transitions should never fail */
@@ -2935,7 +2992,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
2935 if (udev->usb2_hw_lpm_capable == 1) 2992 if (udev->usb2_hw_lpm_capable == 1)
2936 usb_set_usb2_hardware_lpm(udev, 1); 2993 usb_set_usb2_hardware_lpm(udev, 1);
2937 2994
2938 /* Try to enable USB3 LPM */ 2995 /* Try to enable USB3 LTM and LPM */
2996 usb_enable_ltm(udev);
2939 usb_unlocked_enable_lpm(udev); 2997 usb_unlocked_enable_lpm(udev);
2940 } 2998 }
2941 2999
@@ -3488,6 +3546,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
3488 3546
3489void usb_unlocked_enable_lpm(struct usb_device *udev) { } 3547void usb_unlocked_enable_lpm(struct usb_device *udev) { }
3490EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); 3548EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
3549
3550int usb_disable_ltm(struct usb_device *udev)
3551{
3552 return 0;
3553}
3554EXPORT_SYMBOL_GPL(usb_disable_ltm);
3555
3556void usb_enable_ltm(struct usb_device *udev) { }
3557EXPORT_SYMBOL_GPL(usb_enable_ltm);
3491#endif 3558#endif
3492 3559
3493 3560
@@ -4673,15 +4740,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
4673 } 4740 }
4674 parent_hub = hdev_to_hub(parent_hdev); 4741 parent_hub = hdev_to_hub(parent_hdev);
4675 4742
4676 /* Disable LPM while we reset the device and reinstall the alt settings. 4743 /* Disable LPM and LTM while we reset the device and reinstall the alt
4677 * Device-initiated LPM settings, and system exit latency settings are 4744 * settings. Device-initiated LPM settings, and system exit latency
4678 * cleared when the device is reset, so we have to set them up again. 4745 * settings are cleared when the device is reset, so we have to set
4746 * them up again.
4679 */ 4747 */
4680 ret = usb_unlocked_disable_lpm(udev); 4748 ret = usb_unlocked_disable_lpm(udev);
4681 if (ret) { 4749 if (ret) {
4682 dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); 4750 dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
4683 goto re_enumerate; 4751 goto re_enumerate;
4684 } 4752 }
4753 ret = usb_disable_ltm(udev);
4754 if (ret) {
4755 dev_err(&udev->dev, "%s Failed to disable LTM\n.",
4756 __func__);
4757 goto re_enumerate;
4758 }
4685 4759
4686 set_bit(port1, parent_hub->busy_bits); 4760 set_bit(port1, parent_hub->busy_bits);
4687 for (i = 0; i < SET_CONFIG_TRIES; ++i) { 4761 for (i = 0; i < SET_CONFIG_TRIES; ++i) {
@@ -4769,8 +4843,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
4769 } 4843 }
4770 4844
4771done: 4845done:
4772 /* Now that the alt settings are re-installed, enable LPM. */ 4846 /* Now that the alt settings are re-installed, enable LTM and LPM. */
4773 usb_unlocked_enable_lpm(udev); 4847 usb_unlocked_enable_lpm(udev);
4848 usb_enable_ltm(udev);
4774 return 0; 4849 return 0;
4775 4850
4776re_enumerate: 4851re_enumerate:
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index c0877b7f505a..0ab7da2283e3 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1175,6 +1175,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
1175 dev->actconfig->interface[i] = NULL; 1175 dev->actconfig->interface[i] = NULL;
1176 } 1176 }
1177 usb_unlocked_disable_lpm(dev); 1177 usb_unlocked_disable_lpm(dev);
1178 usb_disable_ltm(dev);
1178 dev->actconfig = NULL; 1179 dev->actconfig = NULL;
1179 if (dev->state == USB_STATE_CONFIGURED) 1180 if (dev->state == USB_STATE_CONFIGURED)
1180 usb_set_device_state(dev, USB_STATE_ADDRESS); 1181 usb_set_device_state(dev, USB_STATE_ADDRESS);
@@ -1879,6 +1880,8 @@ free_interfaces:
1879 1880
1880 /* Now that the interfaces are installed, re-enable LPM. */ 1881 /* Now that the interfaces are installed, re-enable LPM. */
1881 usb_unlocked_enable_lpm(dev); 1882 usb_unlocked_enable_lpm(dev);
1883 /* Enable LTM if it was turned off by usb_disable_device. */
1884 usb_enable_ltm(dev);
1882 1885
1883 /* Now that all the interfaces are set up, register them 1886 /* Now that all the interfaces are set up, register them
1884 * to trigger binding of drivers to interfaces. probe() 1887 * to trigger binding of drivers to interfaces. probe()