@@ -4393,8 +4393,6 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
if (usb_set_lpm_timeout(udev, state, 0))
return -EBUSY;
- usb_set_device_initiated_lpm(udev, state, false);
-
if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
"bus schedule bandwidth may be impacted.\n",
@@ -4424,6 +4422,7 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
int usb_disable_lpm(struct usb_device *udev)
{
struct usb_hcd *hcd;
+ int err;
if (!udev || !udev->parent ||
udev->speed < USB_SPEED_SUPER ||
@@ -4441,14 +4440,19 @@ int usb_disable_lpm(struct usb_device *udev)
/* If LPM is enabled, attempt to disable it. */
if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
- goto enable_lpm;
+ goto disable_failed;
if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
- goto enable_lpm;
+ goto disable_failed;
+
+ err = usb_set_device_initiated_lpm(udev, USB3_LPM_U1, false);
+ if (!err)
+ usb_set_device_initiated_lpm(udev, USB3_LPM_U2, false);
return 0;
-enable_lpm:
- usb_enable_lpm(udev);
+disable_failed:
+ udev->lpm_disable_count--;
+
return -EBUSY;
}
EXPORT_SYMBOL_GPL(usb_disable_lpm);
Several usb requests are needed to allow or forbid a USB3 link from going into U1 or U2 hardware link power management (LPM) states. Fail fast on issues in LPM disabling path. LPM disabling is done in hub workqueue paths that are often already handling possible link issues. Enabling and disabling LPM involves four usb requests. Two requests sent to the upstream hub of the connected device: SetPortFeature(U1_TIMEOUT) SetPortFeature(U2_TIMEOUT) And two to the device itself: SetFeature(U1_ENABLE) SetFeature(U2_ENABLE) The requests to the hub sets the inactivity timeout used by the hub to know when to initiate U1 and U2 LPM link state transitions. These requests are also used prevent U1/U2 LPM transitions completely by passing zero timeout value. The requsts sent to the device only controls if device is allowed to initiate U1/U2 transitions. If not enabled then only hub initiates U1/U2 transitions. Hub may block these device initiated attempts. Reorder and send the hub requests first, these are more likely to succeed due to shorter path, and we can consider LPM disabled if these succeed as U1/U2 link state can not be entered after that. Fail immediately if a request fails, and don't try to enable back LPM after a failed request, that will just send more LPM requests over a bad link. If a device request controlling device initiateed LPM fails then exit immediately, but consider LPM disabled at this stage. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> --- drivers/usb/core/hub.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)