aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hub.c24
1 files changed, 19 insertions, 5 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 00070a8a6507..e907dfa0ca6d 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2777,6 +2777,8 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
#define PORT_INIT_TRIES 4
#endif /* CONFIG_USB_FEW_INIT_RETRIES */
+#define DETECT_DISCONNECT_TRIES 5
+
#define HUB_ROOT_RESET_TIME 60 /* times are in msec */
#define HUB_SHORT_RESET_TIME 10
#define HUB_BH_RESET_TIME 50
@@ -5543,6 +5545,7 @@ static void port_event(struct usb_hub *hub, int port1)
struct usb_device *udev = port_dev->child;
struct usb_device *hdev = hub->hdev;
u16 portstatus, portchange;
+ int i = 0;
connect_change = test_bit(port1, hub->change_bits);
clear_bit(port1, hub->event_bits);
@@ -5619,17 +5622,27 @@ static void port_event(struct usb_hub *hub, int port1)
connect_change = 1;
/*
- * Warm reset a USB3 protocol port if it's in
- * SS.Inactive state.
+ * Avoid trying to recover a USB3 SS.Inactive port with a warm reset if
+ * the device was disconnected. A 12ms disconnect detect timer in
+ * SS.Inactive state transitions the port to RxDetect automatically.
+ * SS.Inactive link error state is common during device disconnect.
*/
- if (hub_port_warm_reset_required(hub, port1, portstatus)) {
- dev_dbg(&port_dev->dev, "do warm reset\n");
- if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
+ while (hub_port_warm_reset_required(hub, port1, portstatus)) {
+ if ((i++ < DETECT_DISCONNECT_TRIES) && udev) {
+ u16 unused;
+
+ msleep(20);
+ hub_port_status(hub, port1, &portstatus, &unused);
+ dev_dbg(&port_dev->dev, "Wait for inactive link disconnect detect\n");
+ continue;
+ } else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
|| udev->state == USB_STATE_NOTATTACHED) {
+ dev_dbg(&port_dev->dev, "do warm reset, port only\n");
if (hub_port_reset(hub, port1, NULL,
HUB_BH_RESET_TIME, true) < 0)
hub_port_disable(hub, port1, 1);
} else {
+ dev_dbg(&port_dev->dev, "do warm reset, full device\n");
usb_unlock_port(port_dev);
usb_lock_device(udev);
usb_reset_device(udev);
@@ -5637,6 +5650,7 @@ static void port_event(struct usb_hub *hub, int port1)
usb_lock_port(port_dev);
connect_change = 0;
}
+ break;
}
if (connect_change)