diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
| -rw-r--r-- | drivers/usb/core/hub.c | 37 | 
1 files changed, 27 insertions, 10 deletions
| diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 00070a8a6507..47a1c8bddf86 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1110,7 +1110,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  		} else {  			hub_power_on(hub, true);  		} -	} +	/* Give some time on remote wakeup to let links to transit to U0 */ +	} else if (hub_is_superspeed(hub->hdev)) +		msleep(20); +   init2:  	/* @@ -1225,7 +1228,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)  			 */  			if (portchange || (hub_is_superspeed(hub->hdev) &&  						port_resumed)) -				set_bit(port1, hub->change_bits); +				set_bit(port1, hub->event_bits);  		} else if (udev->persist_enabled) {  #ifdef CONFIG_PM @@ -2777,6 +2780,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 @@ -3570,7 +3575,7 @@ static int finish_port_resume(struct usb_device *udev)   * This routine should only be called when persist is enabled.   */  static int wait_for_connected(struct usb_device *udev, -		struct usb_hub *hub, int *port1, +		struct usb_hub *hub, int port1,  		u16 *portchange, u16 *portstatus)  {  	int status = 0, delay_ms = 0; @@ -3584,7 +3589,7 @@ static int wait_for_connected(struct usb_device *udev,  		}  		msleep(20);  		delay_ms += 20; -		status = hub_port_status(hub, *port1, portstatus, portchange); +		status = hub_port_status(hub, port1, portstatus, portchange);  	}  	dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms);  	return status; @@ -3690,7 +3695,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)  	}  	if (udev->persist_enabled) -		status = wait_for_connected(udev, hub, &port1, &portchange, +		status = wait_for_connected(udev, hub, port1, &portchange,  				&portstatus);  	status = check_port_resume_type(udev, @@ -5543,6 +5548,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 +5625,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 +5653,7 @@ static void port_event(struct usb_hub *hub, int port1)  			usb_lock_port(port_dev);  			connect_change = 0;  		} +		break;  	}  	if (connect_change) |