Commit 6470cbc4 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'for-usb-next-2012-07-11' of...

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 Sharp
parents 43fe3a99 024f117c
...@@ -208,3 +208,15 @@ Description: ...@@ -208,3 +208,15 @@ Description:
such as ACPI. This file will read either "removable" or such as ACPI. This file will read either "removable" or
"fixed" if the information is available, and "unknown" "fixed" if the information is available, and "unknown"
otherwise. 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, ...@@ -2614,6 +2614,50 @@ static int check_port_resume_type(struct usb_device *udev,
return status; 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 #ifdef CONFIG_USB_SUSPEND
/* /*
...@@ -2709,6 +2753,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) ...@@ -2709,6 +2753,11 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_enabled == 1) if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0); 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)) { if (usb_unlocked_disable_lpm(udev)) {
dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.", dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
__func__); __func__);
...@@ -2738,7 +2787,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) ...@@ -2738,7 +2787,8 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_capable == 1) if (udev->usb2_hw_lpm_capable == 1)
usb_set_usb2_hardware_lpm(udev, 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); usb_unlocked_enable_lpm(udev);
/* System sleep transitions should never fail */ /* System sleep transitions should never fail */
...@@ -2939,7 +2989,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) ...@@ -2939,7 +2989,8 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_capable == 1) if (udev->usb2_hw_lpm_capable == 1)
usb_set_usb2_hardware_lpm(udev, 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); usb_unlocked_enable_lpm(udev);
} }
...@@ -3492,6 +3543,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm); ...@@ -3492,6 +3543,15 @@ EXPORT_SYMBOL_GPL(usb_unlocked_disable_lpm);
void usb_unlocked_enable_lpm(struct usb_device *udev) { } void usb_unlocked_enable_lpm(struct usb_device *udev) { }
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm); 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 #endif
...@@ -4675,6 +4735,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4675,6 +4735,23 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
} }
parent_hub = hdev_to_hub(parent_hdev); 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); set_bit(port1, parent_hub->busy_bits);
for (i = 0; i < SET_CONFIG_TRIES; ++i) { for (i = 0; i < SET_CONFIG_TRIES; ++i) {
...@@ -4702,22 +4779,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4702,22 +4779,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
goto done; goto done;
mutex_lock(hcd->bandwidth_mutex); 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); ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) { if (ret < 0) {
dev_warn(&udev->dev, dev_warn(&udev->dev,
"Busted HC? Not enough HCD resources for " "Busted HC? Not enough HCD resources for "
"old configuration.\n"); "old configuration.\n");
usb_enable_lpm(udev);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate; goto re_enumerate;
} }
...@@ -4729,7 +4795,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4729,7 +4795,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
dev_err(&udev->dev, dev_err(&udev->dev,
"can't restore configuration #%d (error=%d)\n", "can't restore configuration #%d (error=%d)\n",
udev->actconfig->desc.bConfigurationValue, ret); udev->actconfig->desc.bConfigurationValue, ret);
usb_enable_lpm(udev);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate; goto re_enumerate;
} }
...@@ -4768,17 +4833,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -4768,17 +4833,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
desc->bInterfaceNumber, desc->bInterfaceNumber,
desc->bAlternateSetting, desc->bAlternateSetting,
ret); ret);
usb_unlocked_enable_lpm(udev);
goto re_enumerate; goto re_enumerate;
} }
} }
/* Now that the alt settings are re-installed, enable LPM. */
usb_unlocked_enable_lpm(udev);
done: done:
/* Now that the alt settings are re-installed, enable LTM and LPM. */
usb_unlocked_enable_lpm(udev);
usb_enable_ltm(udev);
return 0; return 0;
re_enumerate: re_enumerate:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1); hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV; return -ENODEV;
} }
......
...@@ -1174,6 +1174,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) ...@@ -1174,6 +1174,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
put_device(&dev->actconfig->interface[i]->dev); put_device(&dev->actconfig->interface[i]->dev);
dev->actconfig->interface[i] = NULL; dev->actconfig->interface[i] = NULL;
} }
usb_unlocked_disable_lpm(dev);
usb_disable_ltm(dev);
dev->actconfig = NULL; dev->actconfig = NULL;
if (dev->state == USB_STATE_CONFIGURED) if (dev->state == USB_STATE_CONFIGURED)
usb_set_device_state(dev, USB_STATE_ADDRESS); usb_set_device_state(dev, USB_STATE_ADDRESS);
...@@ -1792,13 +1794,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1792,13 +1794,14 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
* installed, so that the xHCI driver can recalculate the U1/U2 * installed, so that the xHCI driver can recalculate the U1/U2
* timeouts. * timeouts.
*/ */
if (usb_disable_lpm(dev)) { if (dev->actconfig && usb_disable_lpm(dev)) {
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
return -ENOMEM; return -ENOMEM;
} }
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL); ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
if (ret < 0) { if (ret < 0) {
if (dev->actconfig)
usb_enable_lpm(dev); usb_enable_lpm(dev);
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev); usb_autosuspend_device(dev);
...@@ -1819,7 +1822,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1819,7 +1822,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if (!cp) { if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS); usb_set_device_state(dev, USB_STATE_ADDRESS);
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); 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); mutex_unlock(hcd->bandwidth_mutex);
usb_autosuspend_device(dev); usb_autosuspend_device(dev);
goto free_interfaces; goto free_interfaces;
...@@ -1877,6 +1880,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration) ...@@ -1877,6 +1880,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
/* Now that the interfaces are installed, re-enable LPM. */ /* Now that the interfaces are installed, re-enable LPM. */
usb_unlocked_enable_lpm(dev); 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 /* Now that all the interfaces are set up, register them
* to trigger binding of drivers to interfaces. probe() * to trigger binding of drivers to interfaces. probe()
......
...@@ -253,6 +253,15 @@ show_removable(struct device *dev, struct device_attribute *attr, char *buf) ...@@ -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 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 #ifdef CONFIG_PM
static ssize_t static ssize_t
...@@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = { ...@@ -649,6 +658,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_authorized.attr, &dev_attr_authorized.attr,
&dev_attr_remove.attr, &dev_attr_remove.attr,
&dev_attr_removable.attr, &dev_attr_removable.attr,
&dev_attr_ltm_capable.attr,
NULL, NULL,
}; };
static struct attribute_group dev_attr_grp = { static struct attribute_group dev_attr_grp = {
......
...@@ -396,6 +396,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, ...@@ -396,6 +396,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->dev.dma_mask = bus->controller->dma_mask; dev->dev.dma_mask = bus->controller->dma_mask;
set_dev_node(&dev->dev, dev_to_node(bus->controller)); set_dev_node(&dev->dev, dev_to_node(bus->controller));
dev->state = USB_STATE_ATTACHED; dev->state = USB_STATE_ATTACHED;
dev->lpm_disable_count = 1;
atomic_set(&dev->urbnum, 0); atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list); INIT_LIST_HEAD(&dev->ep0.urb_list);
......
...@@ -544,12 +544,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -544,12 +544,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
if (hcd->speed != HCD_USB3) if (hcd->speed != HCD_USB3)
goto error; goto error;
/* Set the U1 and U2 exit latencies. */
memcpy(buf, &usb_bos_descriptor, memcpy(buf, &usb_bos_descriptor,
USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
buf[12] = HCS_U1_LATENCY(temp); buf[12] = HCS_U1_LATENCY(temp);
put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); 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); spin_unlock_irqrestore(&xhci->lock, flags);
return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
case GetPortStatus: case GetPortStatus:
......
...@@ -561,7 +561,6 @@ struct usb_device { ...@@ -561,7 +561,6 @@ struct usb_device {
struct usb3_lpm_parameters u1_params; struct usb3_lpm_parameters u1_params;
struct usb3_lpm_parameters u2_params; struct usb3_lpm_parameters u2_params;
unsigned lpm_disable_count; unsigned lpm_disable_count;
unsigned hub_initiated_lpm_disable_count;
}; };
#define to_usb_device(d) container_of(d, struct usb_device, dev) #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); ...@@ -634,6 +633,17 @@ extern void usb_enable_lpm(struct usb_device *udev);
extern int usb_unlocked_disable_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 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 */ /* for drivers using iso endpoints */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment