diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
| -rw-r--r-- | drivers/usb/host/ehci-hub.c | 47 | 
1 files changed, 33 insertions, 14 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 914ce9370e7..4ccb97c0678 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -56,6 +56,19 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)  	if (!ehci->owned_ports)  		return; +	/* Make sure the ports are powered */ +	port = HCS_N_PORTS(ehci->hcs_params); +	while (port--) { +		if (test_bit(port, &ehci->owned_ports)) { +			reg = &ehci->regs->port_status[port]; +			status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS; +			if (!(status & PORT_POWER)) { +				status |= PORT_POWER; +				ehci_writel(ehci, status, reg); +			} +		} +	} +  	/* Give the connections some time to appear */  	msleep(20); @@ -384,11 +397,24 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  	ehci_writel(ehci, ehci->command, &ehci->regs->command);  	ehci->rh_state = EHCI_RH_RUNNING; -	/* Some controller/firmware combinations need a delay during which -	 * they set up the port statuses.  See Bugzilla #8190. */ -	spin_unlock_irq(&ehci->lock); -	msleep(8); -	spin_lock_irq(&ehci->lock); +	/* +	 * According to Bugzilla #8190, the port status for some controllers +	 * will be wrong without a delay. At their wrong status, the port +	 * is enabled, but not suspended neither resumed. +	 */ +	i = HCS_N_PORTS(ehci->hcs_params); +	while (i--) { +		temp = ehci_readl(ehci, &ehci->regs->port_status[i]); +		if ((temp & PORT_PE) && +				!(temp & (PORT_SUSPEND | PORT_RESUME))) { +			ehci_dbg(ehci, "Port status(0x%x) is wrong\n", temp); +			spin_unlock_irq(&ehci->lock); +			msleep(8); +			spin_lock_irq(&ehci->lock); +			break; +		} +	} +  	if (ehci->shutdown)  		goto shutdown; @@ -764,11 +790,6 @@ static int ehci_hub_control (  						status_reg);  			break;  		case USB_PORT_FEAT_C_CONNECTION: -			if (ehci->has_lpm) { -				/* clear PORTSC bits on disconnect */ -				temp &= ~PORT_LPM; -				temp &= ~PORT_DEV_ADDR; -			}  			ehci_writel(ehci, temp | PORT_CSC, status_reg);  			break;  		case USB_PORT_FEAT_C_OVER_CURRENT: @@ -1088,8 +1109,7 @@ error_exit:  	return retval;  } -static void __maybe_unused ehci_relinquish_port(struct usb_hcd *hcd, -		int portnum) +static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)  {  	struct ehci_hcd		*ehci = hcd_to_ehci(hcd); @@ -1098,8 +1118,7 @@ static void __maybe_unused ehci_relinquish_port(struct usb_hcd *hcd,  	set_owner(ehci, --portnum, PORT_OWNER);  } -static int __maybe_unused ehci_port_handed_over(struct usb_hcd *hcd, -		int portnum) +static int ehci_port_handed_over(struct usb_hcd *hcd, int portnum)  {  	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);  	u32 __iomem		*reg;  |