Skip to content

Commit

Permalink
Merge tag 'for-usb-linus-2012-10-11' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…linux/kernel/git/sarah/xhci into usb-linus

USB 3.0 Link PM fixes.

Hi Greg,

Here's four fixes for the USB 3.0 Link Power Management code.  They
work around some USB 3.0 devices that don't support LPM, like the
Western Digital My Passport hard drive.  They also fix an LPM disable
ref count bug that would cause LPM to remain disabled for a device
after a failed probe.

Please queue for 3.7 after the merge window closes.  Several patches are
marked for stable.

Sarah Sharp
  • Loading branch information
Greg Kroah-Hartman committed Oct 13, 2012
2 parents 0b381a2 + 1510a1a commit b001b29
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 13 deletions.
4 changes: 4 additions & 0 deletions drivers/usb/core/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,10 @@ static int usb_probe_interface(struct device *dev)
intf->condition = USB_INTERFACE_UNBOUND;
usb_cancel_queued_reset(intf);

/* If the LPM disable succeeded, balance the ref counts. */
if (!lpm_disable_error)
usb_unlocked_enable_lpm(udev);

/* Unbound interfaces are always runtime-PM-disabled and -suspended */
if (driver->supports_autosuspend)
pm_runtime_disable(dev);
Expand Down
36 changes: 23 additions & 13 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -3241,8 +3241,7 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state)
(state == USB3_LPM_U2 &&
(u2_sel > USB3_LPM_MAX_U2_SEL_PEL ||
u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) {
dev_dbg(&udev->dev, "Device-initiated %s disabled due "
"to long SEL %llu ms or PEL %llu ms\n",
dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n",
usb3_lpm_names[state], u1_sel, u1_pel);
return -EINVAL;
}
Expand Down Expand Up @@ -3319,16 +3318,6 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
}

if (enable) {
/*
* First, let the device know about the exit latencies
* associated with the link state we're about to enable.
*/
ret = usb_req_set_sel(udev, state);
if (ret < 0) {
dev_warn(&udev->dev, "Set SEL for device-initiated "
"%s failed.\n", usb3_lpm_names[state]);
return -EBUSY;
}
/*
* Now send the control transfer to enable device-initiated LPM
* for either U1 or U2.
Expand Down Expand Up @@ -3414,7 +3403,28 @@ static int usb_set_lpm_timeout(struct usb_device *udev,
static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
enum usb3_link_state state)
{
int timeout;
int timeout, ret;
__u8 u1_mel = udev->bos->ss_cap->bU1devExitLat;
__le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat;

/* If the device says it doesn't have *any* exit latency to come out of
* U1 or U2, it's probably lying. Assume it doesn't implement that link
* state.
*/
if ((state == USB3_LPM_U1 && u1_mel == 0) ||
(state == USB3_LPM_U2 && u2_mel == 0))
return;

/*
* First, let the device know about the exit latencies
* associated with the link state we're about to enable.
*/
ret = usb_req_set_sel(udev, state);
if (ret < 0) {
dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n",
usb3_lpm_names[state]);
return;
}

/* We allow the host controller to set the U1/U2 timeout internally
* first, so that it can change its schedule to account for the
Expand Down

0 comments on commit b001b29

Please sign in to comment.