diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 43 |
1 files changed, 28 insertions, 15 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 673ee4696262..1af04bdeaf0c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -739,13 +739,16 @@ static void hub_tt_work(struct work_struct *work) | |||
739 | int limit = 100; | 739 | int limit = 100; |
740 | 740 | ||
741 | spin_lock_irqsave (&hub->tt.lock, flags); | 741 | spin_lock_irqsave (&hub->tt.lock, flags); |
742 | while (--limit && !list_empty (&hub->tt.clear_list)) { | 742 | while (!list_empty(&hub->tt.clear_list)) { |
743 | struct list_head *next; | 743 | struct list_head *next; |
744 | struct usb_tt_clear *clear; | 744 | struct usb_tt_clear *clear; |
745 | struct usb_device *hdev = hub->hdev; | 745 | struct usb_device *hdev = hub->hdev; |
746 | const struct hc_driver *drv; | 746 | const struct hc_driver *drv; |
747 | int status; | 747 | int status; |
748 | 748 | ||
749 | if (!hub->quiescing && --limit < 0) | ||
750 | break; | ||
751 | |||
749 | next = hub->tt.clear_list.next; | 752 | next = hub->tt.clear_list.next; |
750 | clear = list_entry (next, struct usb_tt_clear, clear_list); | 753 | clear = list_entry (next, struct usb_tt_clear, clear_list); |
751 | list_del (&clear->clear_list); | 754 | list_del (&clear->clear_list); |
@@ -1210,7 +1213,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) | |||
1210 | if (hub->has_indicators) | 1213 | if (hub->has_indicators) |
1211 | cancel_delayed_work_sync(&hub->leds); | 1214 | cancel_delayed_work_sync(&hub->leds); |
1212 | if (hub->tt.hub) | 1215 | if (hub->tt.hub) |
1213 | cancel_work_sync(&hub->tt.clear_work); | 1216 | flush_work(&hub->tt.clear_work); |
1214 | } | 1217 | } |
1215 | 1218 | ||
1216 | /* caller has locked the hub device */ | 1219 | /* caller has locked the hub device */ |
@@ -3241,8 +3244,7 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) | |||
3241 | (state == USB3_LPM_U2 && | 3244 | (state == USB3_LPM_U2 && |
3242 | (u2_sel > USB3_LPM_MAX_U2_SEL_PEL || | 3245 | (u2_sel > USB3_LPM_MAX_U2_SEL_PEL || |
3243 | u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) { | 3246 | u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) { |
3244 | dev_dbg(&udev->dev, "Device-initiated %s disabled due " | 3247 | dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n", |
3245 | "to long SEL %llu ms or PEL %llu ms\n", | ||
3246 | usb3_lpm_names[state], u1_sel, u1_pel); | 3248 | usb3_lpm_names[state], u1_sel, u1_pel); |
3247 | return -EINVAL; | 3249 | return -EINVAL; |
3248 | } | 3250 | } |
@@ -3320,16 +3322,6 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev, | |||
3320 | 3322 | ||
3321 | if (enable) { | 3323 | if (enable) { |
3322 | /* | 3324 | /* |
3323 | * First, let the device know about the exit latencies | ||
3324 | * associated with the link state we're about to enable. | ||
3325 | */ | ||
3326 | ret = usb_req_set_sel(udev, state); | ||
3327 | if (ret < 0) { | ||
3328 | dev_warn(&udev->dev, "Set SEL for device-initiated " | ||
3329 | "%s failed.\n", usb3_lpm_names[state]); | ||
3330 | return -EBUSY; | ||
3331 | } | ||
3332 | /* | ||
3333 | * Now send the control transfer to enable device-initiated LPM | 3325 | * Now send the control transfer to enable device-initiated LPM |
3334 | * for either U1 or U2. | 3326 | * for either U1 or U2. |
3335 | */ | 3327 | */ |
@@ -3414,7 +3406,28 @@ static int usb_set_lpm_timeout(struct usb_device *udev, | |||
3414 | static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, | 3406 | static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, |
3415 | enum usb3_link_state state) | 3407 | enum usb3_link_state state) |
3416 | { | 3408 | { |
3417 | int timeout; | 3409 | int timeout, ret; |
3410 | __u8 u1_mel = udev->bos->ss_cap->bU1devExitLat; | ||
3411 | __le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat; | ||
3412 | |||
3413 | /* If the device says it doesn't have *any* exit latency to come out of | ||
3414 | * U1 or U2, it's probably lying. Assume it doesn't implement that link | ||
3415 | * state. | ||
3416 | */ | ||
3417 | if ((state == USB3_LPM_U1 && u1_mel == 0) || | ||
3418 | (state == USB3_LPM_U2 && u2_mel == 0)) | ||
3419 | return; | ||
3420 | |||
3421 | /* | ||
3422 | * First, let the device know about the exit latencies | ||
3423 | * associated with the link state we're about to enable. | ||
3424 | */ | ||
3425 | ret = usb_req_set_sel(udev, state); | ||
3426 | if (ret < 0) { | ||
3427 | dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n", | ||
3428 | usb3_lpm_names[state]); | ||
3429 | return; | ||
3430 | } | ||
3418 | 3431 | ||
3419 | /* We allow the host controller to set the U1/U2 timeout internally | 3432 | /* We allow the host controller to set the U1/U2 timeout internally |
3420 | * first, so that it can change its schedule to account for the | 3433 | * first, so that it can change its schedule to account for the |