USB: Link PM fixes and Latency Tolerance Messaging
Hi Greg, Here's four bug fix patches for Link PM (LPM), which are marked for 3.5-stable. There's also three patches that turn on Latency Tolerance Messaging (LTM) for xHCI host controllers and USB 3.0 devices that support this low power feature. Please queue for 3.6. Sarah Sharp -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJP/WCWAAoJEBMGWMLi1Gc5AjEQAIYHfWi3rhkoTpyhTyYhmzqM /ZhAaeJXDe5VE1isfWK0mnzBz/X05MgpIxCck9CKMkcKZySJNkQFmK7iz6puGPVh GnMQ3QkDo+9JSa7TKEX77ZG+bMkEHcAO2XbZjQs2IDfTuz+BJmQ8gFdjQGfAt/l3 KOU3k83Ci1gdtNgxqifQPBuo3o2l0L5Hn2E7XqFWQ8WUYYu1LWd2bZa/5dznq0hD 4n+ylcK0gDPa9pl7vRsLT79misdLTsJoBfjvooOE2Ms/5QXeFWRppsYRFOla8V4K P2MjiXOCtZHN7GuxdLW776s5dZZyGZnbYNtTOSu0cOjheTC25KpmCm5XW/h5xMt3 saM6mhkSq5ZweDaLXvqV5O+WTQ2ePnubBpqR7/tVWkeUxJoK06ENKZ10dhsqfZw9 Wqcs4ze667Y3wjbBmAaF4b1bmEbhsJR/iAO2z3TLrfiAfYW3S5/4xCUCYujAXuff n1gD75pnJJK1g4hfwFKDDNxWJtXIVqOaLMvD1x5AcGCnCG43mteruFfcS9q66LfI uckf/PnQFAuqEx/J3dIava+yzZhNr0TMQp6aPtSCQUKgBukNf+/RRyB/pDGmYbC1 XBwqIYtv7yQ4w0DVPKd4edPP8zB8E5XMlAY/a1xvxcIa/tFBBDbVh8efXKeKrJEL +smfSepLPwPzETe8YMNR =G1bL -----END PGP SIGNATURE----- Merge tag 'for-usb-next-2012-07-11' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-next USB: Link PM fixes and Latency Tolerance Messaging Hi Greg, Here's four bug fix patches for Link PM (LPM), which are marked for 3.5-stable. There's also three patches that turn on Latency Tolerance Messaging (LTM) for xHCI host controllers and USB 3.0 devices that support this low power feature. Please queue for 3.6. Sarah Sharphifive-unleashed-5.1
commit
6470cbc486
|
@ -208,3 +208,15 @@ Description:
|
|||
such as ACPI. This file will read either "removable" or
|
||||
"fixed" if the information is available, and "unknown"
|
||||
otherwise.
|
||||
|
||||
What: /sys/bus/usb/devices/.../ltm_capable
|
||||
Date: July 2012
|
||||
Contact: Sarah Sharp <sarah.a.sharp@linux.intel.com>
|
||||
Description:
|
||||
USB 3.0 devices may optionally support Latency Tolerance
|
||||
Messaging (LTM). They indicate their support by setting a bit
|
||||
in the bmAttributes field of their SuperSpeed BOS descriptors.
|
||||
If that bit is set for the device, ltm_capable will read "yes".
|
||||
If the device doesn't support LTM, the file will read "no".
|
||||
The file will be present for all speeds of USB devices, and will
|
||||
always read "no" for USB 1.1 and USB 2.0 devices.
|
||||
|
|
|
@ -2614,6 +2614,50 @@ static int check_port_resume_type(struct usb_device *udev,
|
|||
return status;
|
||||
}
|
||||
|
||||
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 (!usb_device_supports_ltm(hcd->self.root_hub) ||
|
||||
!usb_device_supports_ltm(udev))
|
||||
return 0;
|
||||
|
||||
/* Clear Feature LTM Enable can only be sent if the device is
|
||||
* configured.
|
||||
*/
|
||||
if (!udev->actconfig)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_ltm);
|
||||
|
||||
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 (!usb_device_supports_ltm(hcd->self.root_hub) ||
|
||||
!usb_device_supports_ltm(udev))
|
||||
return;
|
||||
|
||||
/* Set Feature LTM Enable can only be sent if the device is
|
||||
* configured.
|
||||
*/
|
||||
if (!udev->actconfig)
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_enable_ltm);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
|
||||
/*
|
||||
|
@ -2709,6 +2753,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__);
|
||||
|
@ -2738,7 +2787,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 */
|
||||
|
@ -2939,7 +2989,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);
|
||||
}
|
||||
|
||||
|
@ -3492,6 +3543,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
|
|||
|
||||
void usb_unlocked_enable_lpm(struct usb_device *udev) { }
|
||||
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
|
||||
|
||||
int usb_disable_ltm(struct usb_device *udev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_disable_ltm);
|
||||
|
||||
void usb_enable_ltm(struct usb_device *udev) { }
|
||||
EXPORT_SYMBOL_GPL(usb_enable_ltm);
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -4675,6 +4735,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
}
|
||||
parent_hub = hdev_to_hub(parent_hdev);
|
||||
|
||||
/* Disable LPM and LTM while we reset the device and reinstall the alt
|
||||
* settings. Device-initiated LPM settings, and system exit latency
|
||||
* settings are cleared when the device is reset, so we have to set
|
||||
* them up again.
|
||||
*/
|
||||
ret = usb_unlocked_disable_lpm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
goto re_enumerate;
|
||||
}
|
||||
ret = usb_disable_ltm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LTM\n.",
|
||||
__func__);
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
||||
set_bit(port1, parent_hub->busy_bits);
|
||||
for (i = 0; i < SET_CONFIG_TRIES; ++i) {
|
||||
|
||||
|
@ -4702,22 +4779,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
goto done;
|
||||
|
||||
mutex_lock(hcd->bandwidth_mutex);
|
||||
/* Disable LPM while we reset the device and reinstall the alt settings.
|
||||
* Device-initiated LPM settings, and system exit latency settings are
|
||||
* cleared when the device is reset, so we have to set them up again.
|
||||
*/
|
||||
ret = usb_disable_lpm(udev);
|
||||
if (ret) {
|
||||
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto done;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
dev_warn(&udev->dev,
|
||||
"Busted HC? Not enough HCD resources for "
|
||||
"old configuration.\n");
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
@ -4729,7 +4795,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
dev_err(&udev->dev,
|
||||
"can't restore configuration #%d (error=%d)\n",
|
||||
udev->actconfig->desc.bConfigurationValue, ret);
|
||||
usb_enable_lpm(udev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
goto re_enumerate;
|
||||
}
|
||||
|
@ -4768,17 +4833,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
|
|||
desc->bInterfaceNumber,
|
||||
desc->bAlternateSetting,
|
||||
ret);
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
goto re_enumerate;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that the alt settings are re-installed, enable LPM. */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
done:
|
||||
/* Now that the alt settings are re-installed, enable LTM and LPM. */
|
||||
usb_unlocked_enable_lpm(udev);
|
||||
usb_enable_ltm(udev);
|
||||
return 0;
|
||||
|
||||
re_enumerate:
|
||||
/* LPM state doesn't matter when we're about to destroy the device. */
|
||||
hub_port_logical_disconnect(parent_hub, port1);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -1174,6 +1174,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
|
|||
put_device(&dev->actconfig->interface[i]->dev);
|
||||
dev->actconfig->interface[i] = NULL;
|
||||
}
|
||||
usb_unlocked_disable_lpm(dev);
|
||||
usb_disable_ltm(dev);
|
||||
dev->actconfig = NULL;
|
||||
if (dev->state == USB_STATE_CONFIGURED)
|
||||
usb_set_device_state(dev, USB_STATE_ADDRESS);
|
||||
|
@ -1792,14 +1794,15 @@ free_interfaces:
|
|||
* installed, so that the xHCI driver can recalculate the U1/U2
|
||||
* timeouts.
|
||||
*/
|
||||
if (usb_disable_lpm(dev)) {
|
||||
if (dev->actconfig && usb_disable_lpm(dev)) {
|
||||
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
usb_enable_lpm(dev);
|
||||
if (dev->actconfig)
|
||||
usb_enable_lpm(dev);
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
|
@ -1819,7 +1822,7 @@ free_interfaces:
|
|||
if (!cp) {
|
||||
usb_set_device_state(dev, USB_STATE_ADDRESS);
|
||||
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
|
||||
usb_enable_lpm(dev);
|
||||
/* Leave LPM disabled while the device is unconfigured. */
|
||||
mutex_unlock(hcd->bandwidth_mutex);
|
||||
usb_autosuspend_device(dev);
|
||||
goto free_interfaces;
|
||||
|
@ -1877,6 +1880,8 @@ free_interfaces:
|
|||
|
||||
/* Now that the interfaces are installed, re-enable LPM. */
|
||||
usb_unlocked_enable_lpm(dev);
|
||||
/* Enable LTM if it was turned off by usb_disable_device. */
|
||||
usb_enable_ltm(dev);
|
||||
|
||||
/* Now that all the interfaces are set up, register them
|
||||
* to trigger binding of drivers to interfaces. probe()
|
||||
|
|
|
@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
}
|
||||
static DEVICE_ATTR(removable, S_IRUGO, show_removable, NULL);
|
||||
|
||||
static ssize_t
|
||||
show_ltm_capable(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (usb_device_supports_ltm(to_usb_device(dev)))
|
||||
return sprintf(buf, "%s\n", "yes");
|
||||
return sprintf(buf, "%s\n", "no");
|
||||
}
|
||||
static DEVICE_ATTR(ltm_capable, S_IRUGO, show_ltm_capable, NULL);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static ssize_t
|
||||
|
@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = {
|
|||
&dev_attr_authorized.attr,
|
||||
&dev_attr_remove.attr,
|
||||
&dev_attr_removable.attr,
|
||||
&dev_attr_ltm_capable.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group dev_attr_grp = {
|
||||
|
|
|
@ -396,6 +396,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
dev->dev.dma_mask = bus->controller->dma_mask;
|
||||
set_dev_node(&dev->dev, dev_to_node(bus->controller));
|
||||
dev->state = USB_STATE_ATTACHED;
|
||||
dev->lpm_disable_count = 1;
|
||||
atomic_set(&dev->urbnum, 0);
|
||||
|
||||
INIT_LIST_HEAD(&dev->ep0.urb_list);
|
||||
|
|
|
@ -544,12 +544,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
|||
if (hcd->speed != HCD_USB3)
|
||||
goto error;
|
||||
|
||||
/* Set the U1 and U2 exit latencies. */
|
||||
memcpy(buf, &usb_bos_descriptor,
|
||||
USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
|
||||
buf[12] = HCS_U1_LATENCY(temp);
|
||||
put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
|
||||
|
||||
/* Indicate whether the host has LTM support. */
|
||||
temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
|
||||
if (HCC_LTC(temp))
|
||||
buf[8] |= USB_LTM_SUPPORT;
|
||||
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
|
||||
case GetPortStatus:
|
||||
|
|
|
@ -561,7 +561,6 @@ struct usb_device {
|
|||
struct usb3_lpm_parameters u1_params;
|
||||
struct usb3_lpm_parameters u2_params;
|
||||
unsigned lpm_disable_count;
|
||||
unsigned hub_initiated_lpm_disable_count;
|
||||
};
|
||||
#define to_usb_device(d) container_of(d, struct usb_device, dev)
|
||||
|
||||
|
@ -634,6 +633,17 @@ extern void usb_enable_lpm(struct usb_device *udev);
|
|||
extern int usb_unlocked_disable_lpm(struct usb_device *udev);
|
||||
extern void usb_unlocked_enable_lpm(struct usb_device *udev);
|
||||
|
||||
extern int usb_disable_ltm(struct usb_device *udev);
|
||||
extern void usb_enable_ltm(struct usb_device *udev);
|
||||
|
||||
static inline 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;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* for drivers using iso endpoints */
|
||||
|
|
Loading…
Reference in New Issue