diff options
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
| -rw-r--r-- | drivers/usb/host/ehci-hub.c | 650 | 
1 files changed, 412 insertions, 238 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 796ea0c8900..cc305c71ac3 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -27,19 +27,17 @@   */  /*-------------------------------------------------------------------------*/ +#include <linux/usb/otg.h>  #define	PORT_WAKE_BITS	(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)  #ifdef	CONFIG_PM -static int ehci_hub_control( -	struct usb_hcd	*hcd, -	u16		typeReq, -	u16		wValue, -	u16		wIndex, -	char		*buf, -	u16		wLength -); +static int persist_enabled_on_companion(struct usb_device *udev, void *unused) +{ +	return !udev->maxchild && udev->persist_enabled && +		udev->bus->root_hub->speed < USB_SPEED_HIGH; +}  /* After a power loss, ports that were owned by the companion must be   * reset so that the companion can still own them. @@ -55,9 +53,33 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)  	if (!ehci->owned_ports)  		return; +	/* +	 * USB 1.1 devices are mostly HIDs, which don't need to persist across +	 * suspends. If we ensure that none of our companion's devices have +	 * persist_enabled (by looking through all USB 1.1 buses in the system), +	 * we can skip this and avoid slowing resume down. Devices without +	 * persist will just get reenumerated shortly after resume anyway. +	 */ +	if (!usb_for_each_dev(NULL, persist_enabled_on_companion)) +		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); +	spin_lock_irq(&ehci->lock);  	port = HCS_N_PORTS(ehci->hcs_params);  	while (port--) {  		if (test_bit(port, &ehci->owned_ports)) { @@ -69,23 +91,30 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)  				clear_bit(port, &ehci->owned_ports);  			else if (test_bit(port, &ehci->companion_ports))  				ehci_writel(ehci, status & ~PORT_PE, reg); -			else +			else { +				spin_unlock_irq(&ehci->lock);  				ehci_hub_control(hcd, SetPortFeature,  						USB_PORT_FEAT_RESET, port + 1,  						NULL, 0); +				spin_lock_irq(&ehci->lock); +			}  		}  	} +	spin_unlock_irq(&ehci->lock);  	if (!ehci->owned_ports)  		return;  	msleep(90);		/* Wait for resets to complete */ +	spin_lock_irq(&ehci->lock);  	port = HCS_N_PORTS(ehci->hcs_params);  	while (port--) {  		if (test_bit(port, &ehci->owned_ports)) { +			spin_unlock_irq(&ehci->lock);  			ehci_hub_control(hcd, GetPortStatus,  					0, port + 1,  					(char *) &buf, sizeof(buf)); +			spin_lock_irq(&ehci->lock);  			/* The companion should now own the port,  			 * but if something went wrong the port must not @@ -104,6 +133,28 @@ static void ehci_handover_companion_ports(struct ehci_hcd *ehci)  	}  	ehci->owned_ports = 0; +	spin_unlock_irq(&ehci->lock); +} + +static int ehci_port_change(struct ehci_hcd *ehci) +{ +	int i = HCS_N_PORTS(ehci->hcs_params); + +	/* First check if the controller indicates a change event */ + +	if (ehci_readl(ehci, &ehci->regs->status) & STS_PCD) +		return 1; + +	/* +	 * Not all controllers appear to update this while going from D3 to D0, +	 * so check the individual port status registers as well +	 */ + +	while (i--) +		if (ehci_readl(ehci, &ehci->regs->port_status[i]) & PORT_CSC) +			return 1; + +	return 0;  }  static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, @@ -120,18 +171,20 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,  	if (!ehci_to_hcd(ehci)->self.root_hub->do_remote_wakeup || do_wakeup)  		return; +	spin_lock_irq(&ehci->lock); +  	/* clear phy low-power mode before changing wakeup flags */ -	if (ehci->has_hostpc) { +	if (ehci->has_tdi_phy_lpm) {  		port = HCS_N_PORTS(ehci->hcs_params);  		while (port--) { -			u32 __iomem	*hostpc_reg; +			u32 __iomem	*hostpc_reg = &ehci->regs->hostpc[port]; -			hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs -					+ HOSTPC0 + 4 * port);  			temp = ehci_readl(ehci, hostpc_reg);  			ehci_writel(ehci, temp & ~HOSTPC_PHCD, hostpc_reg);  		} +		spin_unlock_irq(&ehci->lock);  		msleep(5); +		spin_lock_irq(&ehci->lock);  	}  	port = HCS_N_PORTS(ehci->hcs_params); @@ -149,27 +202,25 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,  			else  				t2 |= PORT_WKOC_E | PORT_WKCONN_E;  		} -		ehci_vdbg(ehci, "port %d, %08x -> %08x\n", -				port + 1, t1, t2);  		ehci_writel(ehci, t2, reg);  	}  	/* enter phy low-power mode again */ -	if (ehci->has_hostpc) { +	if (ehci->has_tdi_phy_lpm) {  		port = HCS_N_PORTS(ehci->hcs_params);  		while (port--) { -			u32 __iomem	*hostpc_reg; +			u32 __iomem	*hostpc_reg = &ehci->regs->hostpc[port]; -			hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs -					+ HOSTPC0 + 4 * port);  			temp = ehci_readl(ehci, hostpc_reg);  			ehci_writel(ehci, temp | HOSTPC_PHCD, hostpc_reg);  		}  	}  	/* Does the root hub have a port wakeup pending? */ -	if (!suspending && (ehci_readl(ehci, &ehci->regs->status) & STS_PCD)) +	if (!suspending && ehci_port_change(ehci))  		usb_hcd_resume_root_hub(ehci_to_hcd(ehci)); + +	spin_unlock_irq(&ehci->lock);  }  static int ehci_bus_suspend (struct usb_hcd *hcd) @@ -178,15 +229,19 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  	int			port;  	int			mask;  	int			changed; +	bool			fs_idle_delay;  	ehci_dbg(ehci, "suspend root hub\n");  	if (time_before (jiffies, ehci->next_statechange))  		msleep(5); -	del_timer_sync(&ehci->watchdog); -	del_timer_sync(&ehci->iaa_watchdog); + +	/* stop the schedules */ +	ehci_quiesce(ehci);  	spin_lock_irq (&ehci->lock); +	if (ehci->rh_state < EHCI_RH_RUNNING) +		goto done;  	/* Once the controller is stopped, port resumes that are already  	 * in progress won't complete.  Hence if remote wakeup is enabled @@ -194,26 +249,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  	 * remote wakeup, we must fail the suspend.  	 */  	if (hcd->self.root_hub->do_remote_wakeup) { -		port = HCS_N_PORTS(ehci->hcs_params); -		while (port--) { -			if (ehci->reset_done[port] != 0) { -				spin_unlock_irq(&ehci->lock); -				ehci_dbg(ehci, "suspend failed because " -						"port %d is resuming\n", -						port + 1); -				return -EBUSY; -			} +		if (ehci->resuming_ports) { +			spin_unlock_irq(&ehci->lock); +			ehci_dbg(ehci, "suspend failed because a port is resuming\n"); +			return -EBUSY;  		}  	} -	/* stop schedules, clean any completed work */ -	if (HC_IS_RUNNING(hcd->state)) { -		ehci_quiesce (ehci); -		hcd->state = HC_STATE_QUIESCING; -	} -	ehci->command = ehci_readl(ehci, &ehci->regs->command); -	ehci_work(ehci); -  	/* Unlike other USB host controller types, EHCI doesn't have  	 * any notion of "global" or bus-wide suspend.  The driver has  	 * to manually suspend all the active unsuspended ports, and @@ -222,6 +264,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  	ehci->bus_suspended = 0;  	ehci->owned_ports = 0;  	changed = 0; +	fs_idle_delay = false;  	port = HCS_N_PORTS(ehci->hcs_params);  	while (port--) {  		u32 __iomem	*reg = &ehci->regs->port_status [port]; @@ -250,25 +293,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  		}  		if (t1 != t2) { -			ehci_vdbg (ehci, "port %d, %08x -> %08x\n", -				port + 1, t1, t2); +			/* +			 * On some controllers, Wake-On-Disconnect will +			 * generate false wakeup signals until the bus +			 * switches over to full-speed idle.  For their +			 * sake, add a delay if we need one. +			 */ +			if ((t2 & PORT_WKDISC_E) && +					ehci_port_speed(ehci, t2) == +						USB_PORT_STAT_HIGH_SPEED) +				fs_idle_delay = true;  			ehci_writel(ehci, t2, reg);  			changed = 1;  		}  	} +	spin_unlock_irq(&ehci->lock); -	if (changed && ehci->has_hostpc) { -		spin_unlock_irq(&ehci->lock); -		msleep(5);	/* 5 ms for HCD to enter low-power mode */ -		spin_lock_irq(&ehci->lock); +	if ((changed && ehci->has_tdi_phy_lpm) || fs_idle_delay) { +		/* +		 * Wait for HCD to enter low-power mode or for the bus +		 * to switch to full-speed idle. +		 */ +		usleep_range(5000, 5500); +	} +	if (changed && ehci->has_tdi_phy_lpm) { +		spin_lock_irq(&ehci->lock);  		port = HCS_N_PORTS(ehci->hcs_params);  		while (port--) { -			u32 __iomem	*hostpc_reg; +			u32 __iomem	*hostpc_reg = &ehci->regs->hostpc[port];  			u32		t3; -			hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs -					+ HOSTPC0 + 4 * port);  			t3 = ehci_readl(ehci, hostpc_reg);  			ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);  			t3 = ehci_readl(ehci, hostpc_reg); @@ -276,6 +331,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  					port, (t3 & HOSTPC_PHCD) ?  					"succeeded" : "failed");  		} +		spin_unlock_irq(&ehci->lock);  	}  	/* Apparently some devices need a >= 1-uframe delay here */ @@ -284,10 +340,19 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  	/* turn off now-idle HC */  	ehci_halt (ehci); -	hcd->state = HC_STATE_SUSPENDED; -	if (ehci->reclaim) -		end_unlink_async(ehci); +	spin_lock_irq(&ehci->lock); +	if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_DEAD)) +		ehci_handle_controller_death(ehci); +	if (ehci->rh_state != EHCI_RH_RUNNING) +		goto done; +	ehci->rh_state = EHCI_RH_SUSPENDED; + +	end_unlink_async(ehci); +	unlink_empty_async_suspended(ehci); +	ehci_handle_start_intr_unlinks(ehci); +	ehci_handle_intr_unlinks(ehci); +	end_free_itds(ehci);  	/* allow remote wakeup */  	mask = INTR_MASK; @@ -296,13 +361,13 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)  	ehci_writel(ehci, mask, &ehci->regs->intr_enable);  	ehci_readl(ehci, &ehci->regs->intr_enable); + done:  	ehci->next_statechange = jiffies + msecs_to_jiffies(10); +	ehci->enabled_hrtimer_events = 0; +	ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;  	spin_unlock_irq (&ehci->lock); -	/* ehci_work() may have re-enabled the watchdog timer, which we do not -	 * want, and so we must delete any pending watchdog timer events. -	 */ -	del_timer_sync(&ehci->watchdog); +	hrtimer_cancel(&ehci->hrtimer);  	return 0;  } @@ -314,21 +379,19 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  	u32			temp;  	u32			power_okay;  	int			i; -	u8			resume_needed = 0; +	unsigned long		resume_needed = 0;  	if (time_before (jiffies, ehci->next_statechange))  		msleep(5);  	spin_lock_irq (&ehci->lock); -	if (!HCD_HW_ACCESSIBLE(hcd)) { -		spin_unlock_irq(&ehci->lock); -		return -ESHUTDOWN; -	} +	if (!HCD_HW_ACCESSIBLE(hcd) || ehci->shutdown) +		goto shutdown;  	if (unlikely(ehci->debug)) { -		if (!dbgp_reset_prep()) +		if (!dbgp_reset_prep(hcd))  			ehci->debug = NULL;  		else -			dbgp_external_startup(); +			dbgp_external_startup(hcd);  	}  	/* Ideally and we've got a real resume here, and no port's power @@ -352,23 +415,39 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  	ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next);  	/* restore CMD_RUN, framelist size, and irq threshold */ +	ehci->command |= CMD_RUN;  	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;  	/* clear phy low-power mode before resume */ -	if (ehci->bus_suspended && ehci->has_hostpc) { +	if (ehci->bus_suspended && ehci->has_tdi_phy_lpm) {  		i = HCS_N_PORTS(ehci->hcs_params);  		while (i--) {  			if (test_bit(i, &ehci->bus_suspended)) { -				u32 __iomem	*hostpc_reg; +				u32 __iomem	*hostpc_reg = +							&ehci->regs->hostpc[i]; -				hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs -						+ HOSTPC0 + 4 * i);  				temp = ehci_readl(ehci, hostpc_reg);  				ehci_writel(ehci, temp & ~HOSTPC_PHCD,  						hostpc_reg); @@ -377,6 +456,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  		spin_unlock_irq(&ehci->lock);  		msleep(5);  		spin_lock_irq(&ehci->lock); +		if (ehci->shutdown) +			goto shutdown;  	}  	/* manually resume the ports we suspended during bus_suspend() */ @@ -387,7 +468,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  		if (test_bit(i, &ehci->bus_suspended) &&  				(temp & PORT_SUSPEND)) {  			temp |= PORT_RESUME; -			resume_needed = 1; +			set_bit(i, &resume_needed);  		}  		ehci_writel(ehci, temp, &ehci->regs->port_status [i]);  	} @@ -397,40 +478,37 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  		spin_unlock_irq(&ehci->lock);  		msleep(20);  		spin_lock_irq(&ehci->lock); +		if (ehci->shutdown) +			goto shutdown;  	}  	i = HCS_N_PORTS (ehci->hcs_params);  	while (i--) {  		temp = ehci_readl(ehci, &ehci->regs->port_status [i]); -		if (test_bit(i, &ehci->bus_suspended) && -				(temp & PORT_SUSPEND)) { -			temp &= ~(PORT_RWC_BITS | PORT_RESUME); +		if (test_bit(i, &resume_needed)) { +			temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);  			ehci_writel(ehci, temp, &ehci->regs->port_status [i]); -			ehci_vdbg (ehci, "resumed port %d\n", i + 1);  		}  	} -	(void) ehci_readl(ehci, &ehci->regs->command); - -	/* maybe re-activate the schedule(s) */ -	temp = 0; -	if (ehci->async->qh_next.qh) -		temp |= CMD_ASE; -	if (ehci->periodic_sched) -		temp |= CMD_PSE; -	if (temp) { -		ehci->command |= temp; -		ehci_writel(ehci, ehci->command, &ehci->regs->command); -	}  	ehci->next_statechange = jiffies + msecs_to_jiffies(5); -	hcd->state = HC_STATE_RUNNING; +	spin_unlock_irq(&ehci->lock); + +	ehci_handover_companion_ports(ehci);  	/* Now we can safely re-enable irqs */ +	spin_lock_irq(&ehci->lock); +	if (ehci->shutdown) +		goto shutdown;  	ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); +	(void) ehci_readl(ehci, &ehci->regs->intr_enable); +	spin_unlock_irq(&ehci->lock); -	spin_unlock_irq (&ehci->lock); -	ehci_handover_companion_ports(ehci);  	return 0; + + shutdown: +	spin_unlock_irq(&ehci->lock); +	return -ESHUTDOWN;  }  #else @@ -442,29 +520,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd)  /*-------------------------------------------------------------------------*/ -/* Display the ports dedicated to the companion controller */ -static ssize_t show_companion(struct device *dev, -			      struct device_attribute *attr, -			      char *buf) -{ -	struct ehci_hcd		*ehci; -	int			nports, index, n; -	int			count = PAGE_SIZE; -	char			*ptr = buf; - -	ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); -	nports = HCS_N_PORTS(ehci->hcs_params); - -	for (index = 0; index < nports; ++index) { -		if (test_bit(index, &ehci->companion_ports)) { -			n = scnprintf(ptr, count, "%d\n", index + 1); -			ptr += n; -			count -= n; -		} -	} -	return ptr - buf; -} -  /*   * Sets the owner of a port   */ @@ -499,57 +554,6 @@ static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)  	}  } -/* - * Dedicate or undedicate a port to the companion controller. - * Syntax is "[-]portnum", where a leading '-' sign means - * return control of the port to the EHCI controller. - */ -static ssize_t store_companion(struct device *dev, -			       struct device_attribute *attr, -			       const char *buf, size_t count) -{ -	struct ehci_hcd		*ehci; -	int			portnum, new_owner; - -	ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); -	new_owner = PORT_OWNER;		/* Owned by companion */ -	if (sscanf(buf, "%d", &portnum) != 1) -		return -EINVAL; -	if (portnum < 0) { -		portnum = - portnum; -		new_owner = 0;		/* Owned by EHCI */ -	} -	if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) -		return -ENOENT; -	portnum--; -	if (new_owner) -		set_bit(portnum, &ehci->companion_ports); -	else -		clear_bit(portnum, &ehci->companion_ports); -	set_owner(ehci, portnum, new_owner); -	return count; -} -static DEVICE_ATTR(companion, 0644, show_companion, store_companion); - -static inline void create_companion_file(struct ehci_hcd *ehci) -{ -	int	i; - -	/* with integrated TT there is no companion! */ -	if (!ehci_is_TDI(ehci)) -		i = device_create_file(ehci_to_hcd(ehci)->self.controller, -				       &dev_attr_companion); -} - -static inline void remove_companion_file(struct ehci_hcd *ehci) -{ -	/* with integrated TT there is no companion! */ -	if (!ehci_is_TDI(ehci)) -		device_remove_file(ehci_to_hcd(ehci)->self.controller, -				   &dev_attr_companion); -} - -  /*-------------------------------------------------------------------------*/  static int check_reset_complete ( @@ -584,7 +588,8 @@ static int check_reset_complete (  		if (ehci->has_amcc_usb23)  			set_ohci_hcfs(ehci, 1);  	} else { -		ehci_dbg (ehci, "port %d high speed\n", index + 1); +		ehci_dbg(ehci, "port %d reset complete, port enabled\n", +			index + 1);  		/* ensure 440EPx ohci controller state is suspended */  		if (ehci->has_amcc_usb23)  			set_ohci_hcfs(ehci, 0); @@ -602,15 +607,11 @@ static int  ehci_hub_status_data (struct usb_hcd *hcd, char *buf)  {  	struct ehci_hcd	*ehci = hcd_to_ehci (hcd); -	u32		temp, status = 0; +	u32		temp, status;  	u32		mask;  	int		ports, i, retval = 1;  	unsigned long	flags; -	u32		ppcd = 0; - -	/* if !USB_SUSPEND, root hub timers won't get shut down ... */ -	if (!HC_IS_RUNNING(hcd->state)) -		return 0; +	u32		ppcd = ~0;  	/* init status to no-changes */  	buf [0] = 0; @@ -620,6 +621,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)  		retval++;  	} +	/* Inform the core about resumes-in-progress by returning +	 * a non-zero value even if there are no status changes. +	 */ +	status = ehci->resuming_ports; +  	/* Some boards (mostly VIA?) report bogus overcurrent indications,  	 * causing massive log spam unless we completely ignore them.  It  	 * may be relevant that VIA VT8235 controllers, where PORT_POWER is @@ -643,9 +649,10 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)  	for (i = 0; i < ports; i++) {  		/* leverage per-port change bits feature */ -		if (ehci->has_ppcd && !(ppcd & (1 << i))) -			continue; -		temp = ehci_readl(ehci, &ehci->regs->port_status [i]); +		if (ppcd & (1 << i)) +			temp = ehci_readl(ehci, &ehci->regs->port_status[i]); +		else +			temp = 0;  		/*  		 * Return status information even for ports with OWNER set. @@ -664,7 +671,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)  			status = STS_PCD;  		}  	} -	/* FIXME autosuspend idle root hubs */ + +	/* If a resume is in progress, make sure it can finish */ +	if (ehci->resuming_ports) +		mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25)); +  	spin_unlock_irqrestore (&ehci->lock, flags);  	return status ? retval : 0;  } @@ -688,8 +699,8 @@ ehci_hub_descriptor (  	desc->bDescLength = 7 + 2 * temp;  	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */ -	memset (&desc->bitmap [0], 0, temp); -	memset (&desc->bitmap [temp], 0xff, temp); +	memset(&desc->u.hs.DeviceRemovable[0], 0, temp); +	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);  	temp = 0x0008;			/* per-port overcurrent reporting */  	if (HCS_PPC (ehci->hcs_params)) @@ -705,8 +716,147 @@ ehci_hub_descriptor (  }  /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_HCD_TEST_MODE + +#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06 -static int ehci_hub_control ( +static void usb_ehset_completion(struct urb *urb) +{ +	struct completion  *done = urb->context; + +	complete(done); +} +static int submit_single_step_set_feature( +	struct usb_hcd	*hcd, +	struct urb	*urb, +	int		is_setup +); + +/* + * Allocate and initialize a control URB. This request will be used by the + * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages + * of the GetDescriptor request are sent 15 seconds after the SETUP stage. + * Return NULL if failed. + */ +static struct urb *request_single_step_set_feature_urb( +	struct usb_device	*udev, +	void			*dr, +	void			*buf, +	struct completion	*done +) { +	struct urb *urb; +	struct usb_hcd *hcd = bus_to_hcd(udev->bus); +	struct usb_host_endpoint *ep; + +	urb = usb_alloc_urb(0, GFP_KERNEL); +	if (!urb) +		return NULL; + +	urb->pipe = usb_rcvctrlpipe(udev, 0); +	ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out) +				[usb_pipeendpoint(urb->pipe)]; +	if (!ep) { +		usb_free_urb(urb); +		return NULL; +	} + +	urb->ep = ep; +	urb->dev = udev; +	urb->setup_packet = (void *)dr; +	urb->transfer_buffer = buf; +	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE; +	urb->complete = usb_ehset_completion; +	urb->status = -EINPROGRESS; +	urb->actual_length = 0; +	urb->transfer_flags = URB_DIR_IN; +	usb_get_urb(urb); +	atomic_inc(&urb->use_count); +	atomic_inc(&urb->dev->urbnum); +	urb->setup_dma = dma_map_single( +			hcd->self.controller, +			urb->setup_packet, +			sizeof(struct usb_ctrlrequest), +			DMA_TO_DEVICE); +	urb->transfer_dma = dma_map_single( +			hcd->self.controller, +			urb->transfer_buffer, +			urb->transfer_buffer_length, +			DMA_FROM_DEVICE); +	urb->context = done; +	return urb; +} + +static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port) +{ +	int retval = -ENOMEM; +	struct usb_ctrlrequest *dr; +	struct urb *urb; +	struct usb_device *udev; +	struct ehci_hcd *ehci = hcd_to_ehci(hcd); +	struct usb_device_descriptor *buf; +	DECLARE_COMPLETION_ONSTACK(done); + +	/* Obtain udev of the rhub's child port */ +	udev = usb_hub_find_child(hcd->self.root_hub, port); +	if (!udev) { +		ehci_err(ehci, "No device attached to the RootHub\n"); +		return -ENODEV; +	} +	buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); +	if (!dr) { +		kfree(buf); +		return -ENOMEM; +	} + +	/* Fill Setup packet for GetDescriptor */ +	dr->bRequestType = USB_DIR_IN; +	dr->bRequest = USB_REQ_GET_DESCRIPTOR; +	dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8); +	dr->wIndex = 0; +	dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE); +	urb = request_single_step_set_feature_urb(udev, dr, buf, &done); +	if (!urb) +		goto cleanup; + +	/* Submit just the SETUP stage */ +	retval = submit_single_step_set_feature(hcd, urb, 1); +	if (retval) +		goto out1; +	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) { +		usb_kill_urb(urb); +		retval = -ETIMEDOUT; +		ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__); +		goto out1; +	} +	msleep(15 * 1000); + +	/* Complete remaining DATA and STATUS stages using the same URB */ +	urb->status = -EINPROGRESS; +	usb_get_urb(urb); +	atomic_inc(&urb->use_count); +	atomic_inc(&urb->dev->urbnum); +	retval = submit_single_step_set_feature(hcd, urb, 0); +	if (!retval && !wait_for_completion_timeout(&done, +						msecs_to_jiffies(2000))) { +		usb_kill_urb(urb); +		retval = -ETIMEDOUT; +		ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__); +	} +out1: +	usb_free_urb(urb); +cleanup: +	kfree(dr); +	kfree(buf); +	return retval; +} +#endif /* CONFIG_USB_HCD_TEST_MODE */ +/*-------------------------------------------------------------------------*/ + +int ehci_hub_control(  	struct usb_hcd	*hcd,  	u16		typeReq,  	u16		wValue, @@ -718,7 +868,7 @@ static int ehci_hub_control (  	int		ports = HCS_N_PORTS (ehci->hcs_params);  	u32 __iomem	*status_reg = &ehci->regs->port_status[  				(wIndex & 0xff) - 1]; -	u32 __iomem	*hostpc_reg = NULL; +	u32 __iomem	*hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1];  	u32		temp, temp1, status;  	unsigned long	flags;  	int		retval = 0; @@ -731,9 +881,6 @@ static int ehci_hub_control (  	 * power, "this is the one", etc.  EHCI spec supports this.  	 */ -	if (ehci->has_hostpc) -		hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs -				+ HOSTPC0 + 4 * ((wIndex & 0xff) - 1));  	spin_lock_irqsave (&ehci->lock, flags);  	switch (typeReq) {  	case ClearHubFeature: @@ -751,6 +898,7 @@ static int ehci_hub_control (  			goto error;  		wIndex--;  		temp = ehci_readl(ehci, status_reg); +		temp &= ~PORT_RWC_BITS;  		/*  		 * Even if OWNER is set, so the port is owned by the @@ -764,21 +912,27 @@ static int ehci_hub_control (  			ehci_writel(ehci, temp & ~PORT_PE, status_reg);  			break;  		case USB_PORT_FEAT_C_ENABLE: -			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_PEC, -					status_reg); +			ehci_writel(ehci, temp | PORT_PEC, status_reg);  			break;  		case USB_PORT_FEAT_SUSPEND:  			if (temp & PORT_RESET)  				goto error;  			if (ehci->no_selective_suspend)  				break; +#ifdef CONFIG_USB_OTG +			if ((hcd->self.otg_port == (wIndex + 1)) +			    && hcd->self.b_hnp_enable) { +				otg_start_hnp(hcd->phy->otg); +				break; +			} +#endif  			if (!(temp & PORT_SUSPEND))  				break;  			if ((temp & PORT_PE) == 0)  				goto error;  			/* clear phy low-power mode before resume */ -			if (hostpc_reg) { +			if (ehci->has_tdi_phy_lpm) {  				temp1 = ehci_readl(ehci, hostpc_reg);  				ehci_writel(ehci, temp1 & ~HOSTPC_PHCD,  						hostpc_reg); @@ -787,32 +941,26 @@ static int ehci_hub_control (  				spin_lock_irqsave(&ehci->lock, flags);  			}  			/* resume signaling for 20 msec */ -			temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); +			temp &= ~PORT_WAKE_BITS;  			ehci_writel(ehci, temp | PORT_RESUME, status_reg);  			ehci->reset_done[wIndex] = jiffies  					+ msecs_to_jiffies(20); +			set_bit(wIndex, &ehci->resuming_ports); +			usb_hcd_start_port_resume(&hcd->self, wIndex);  			break;  		case USB_PORT_FEAT_C_SUSPEND:  			clear_bit(wIndex, &ehci->port_c_suspend);  			break;  		case USB_PORT_FEAT_POWER:  			if (HCS_PPC (ehci->hcs_params)) -				ehci_writel(ehci, -					  temp & ~(PORT_RWC_BITS | PORT_POWER), -					  status_reg); +				ehci_writel(ehci, temp & ~PORT_POWER, +						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_RWC_BITS) | PORT_CSC, -					status_reg); +			ehci_writel(ehci, temp | PORT_CSC, status_reg);  			break;  		case USB_PORT_FEAT_C_OVER_CURRENT: -			ehci_writel(ehci, (temp & ~PORT_RWC_BITS) | PORT_OCC, -					status_reg); +			ehci_writel(ehci, temp | PORT_OCC, status_reg);  			break;  		case USB_PORT_FEAT_C_RESET:  			/* GetPortStatus clears reset */ @@ -854,54 +1002,56 @@ static int ehci_hub_control (  			 * power switching; they're allowed to just limit the  			 * current.  khubd will turn the power back on.  			 */ -			if (HCS_PPC (ehci->hcs_params)){ +			if (((temp & PORT_OC) || (ehci->need_oc_pp_cycle)) +					&& HCS_PPC(ehci->hcs_params)) {  				ehci_writel(ehci,  					temp & ~(PORT_RWC_BITS | PORT_POWER),  					status_reg); +				temp = ehci_readl(ehci, status_reg);  			}  		} -		/* whoever resumes must GetPortStatus to complete it!! */ -		if (temp & PORT_RESUME) { +		/* no reset or resume pending */ +		if (!ehci->reset_done[wIndex]) {  			/* Remote Wakeup received? */ -			if (!ehci->reset_done[wIndex]) { +			if (temp & PORT_RESUME) {  				/* resume signaling for 20 msec */  				ehci->reset_done[wIndex] = jiffies  						+ msecs_to_jiffies(20); +				usb_hcd_start_port_resume(&hcd->self, wIndex); +				set_bit(wIndex, &ehci->resuming_ports);  				/* check the port again */  				mod_timer(&ehci_to_hcd(ehci)->rh_timer,  						ehci->reset_done[wIndex]);  			} -			/* resume completed? */ -			else if (time_after_eq(jiffies, -					ehci->reset_done[wIndex])) { -				clear_bit(wIndex, &ehci->suspended_ports); -				set_bit(wIndex, &ehci->port_c_suspend); -				ehci->reset_done[wIndex] = 0; +		/* reset or resume not yet complete */ +		} else if (!time_after_eq(jiffies, ehci->reset_done[wIndex])) { +			;	/* wait until it is complete */ -				/* stop resume signaling */ -				temp = ehci_readl(ehci, status_reg); -				ehci_writel(ehci, -					temp & ~(PORT_RWC_BITS | PORT_RESUME), -					status_reg); -				retval = handshake(ehci, status_reg, -					   PORT_RESUME, 0, 2000 /* 2msec */); -				if (retval != 0) { -					ehci_err(ehci, -						"port %d resume error %d\n", +		/* resume completed */ +		} else if (test_bit(wIndex, &ehci->resuming_ports)) { +			clear_bit(wIndex, &ehci->suspended_ports); +			set_bit(wIndex, &ehci->port_c_suspend); +			ehci->reset_done[wIndex] = 0; +			usb_hcd_end_port_resume(&hcd->self, wIndex); + +			/* stop resume signaling */ +			temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME); +			ehci_writel(ehci, temp, status_reg); +			clear_bit(wIndex, &ehci->resuming_ports); +			retval = ehci_handshake(ehci, status_reg, +					PORT_RESUME, 0, 2000 /* 2msec */); +			if (retval != 0) { +				ehci_err(ehci, "port %d resume error %d\n",  						wIndex + 1, retval); -					goto error; -				} -				temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); +				goto error;  			} -		} +			temp = ehci_readl(ehci, status_reg);  		/* whoever resets must GetPortStatus to complete it!! */ -		if ((temp & PORT_RESET) -				&& time_after_eq(jiffies, -					ehci->reset_done[wIndex])) { +		} else {  			status |= USB_PORT_STAT_C_RESET << 16;  			ehci->reset_done [wIndex] = 0; @@ -911,7 +1061,7 @@ static int ehci_hub_control (  			/* REVISIT:  some hardware needs 550+ usec to clear  			 * this bit; seems too long to spin routinely...  			 */ -			retval = handshake(ehci, status_reg, +			retval = ehci_handshake(ehci, status_reg,  					PORT_RESET, 0, 1000);  			if (retval != 0) {  				ehci_err (ehci, "port %d reset error %d\n", @@ -924,9 +1074,6 @@ static int ehci_hub_control (  					ehci_readl(ehci, status_reg));  		} -		if (!(temp & (PORT_RESUME|PORT_RESET))) -			ehci->reset_done[wIndex] = 0; -  		/* transfer dedicated ports to the companion hc */  		if ((temp & PORT_CONNECT) &&  				test_bit(wIndex, &ehci->companion_ports)) { @@ -960,9 +1107,11 @@ static int ehci_hub_control (  			status |= USB_PORT_STAT_SUSPEND;  		} else if (test_bit(wIndex, &ehci->suspended_ports)) {  			clear_bit(wIndex, &ehci->suspended_ports); +			clear_bit(wIndex, &ehci->resuming_ports);  			ehci->reset_done[wIndex] = 0;  			if (temp & PORT_PE)  				set_bit(wIndex, &ehci->port_c_suspend); +			usb_hcd_end_port_resume(&hcd->self, wIndex);  		}  		if (temp & PORT_OC) @@ -974,10 +1123,8 @@ static int ehci_hub_control (  		if (test_bit(wIndex, &ehci->port_c_suspend))  			status |= USB_PORT_STAT_C_SUSPEND << 16; -#ifndef	VERBOSE_DEBUG -	if (status & ~0xffff)	/* only if wPortChange is interesting */ -#endif -		dbg_port (ehci, "GetStatus", wIndex + 1, temp); +		if (status & ~0xffff)	/* only if wPortChange is interesting */ +			dbg_port(ehci, "GetStatus", wIndex + 1, temp);  		put_unaligned_le32(status, buf);  		break;  	case SetHubFeature: @@ -1020,12 +1167,12 @@ static int ehci_hub_control (  			/* After above check the port must be connected.  			 * Set appropriate bit thus could put phy into low power -			 * mode if we have hostpc feature +			 * mode if we have tdi_phy_lpm feature  			 */  			temp &= ~PORT_WKCONN_E;  			temp |= PORT_WKDISC_E | PORT_WKOC_E;  			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); -			if (hostpc_reg) { +			if (ehci->has_tdi_phy_lpm) {  				spin_unlock_irqrestore(&ehci->lock, flags);  				msleep(5);/* 5ms for HCD enter low pwr mode */  				spin_lock_irqsave(&ehci->lock, flags); @@ -1045,7 +1192,7 @@ static int ehci_hub_control (  						status_reg);  			break;  		case USB_PORT_FEAT_RESET: -			if (temp & PORT_RESUME) +			if (temp & (PORT_SUSPEND|PORT_RESUME))  				goto error;  			/* line status bits may report this as low speed,  			 * which can be fine if this root hub has a @@ -1059,7 +1206,6 @@ static int ehci_hub_control (  					wIndex + 1);  				temp |= PORT_OWNER;  			} else { -				ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);  				temp |= PORT_RESET;  				temp &= ~PORT_PE; @@ -1080,10 +1226,37 @@ static int ehci_hub_control (  		 * about the EHCI-specific stuff.  		 */  		case USB_PORT_FEAT_TEST: +#ifdef CONFIG_USB_HCD_TEST_MODE +			if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { +				spin_unlock_irqrestore(&ehci->lock, flags); +				retval = ehset_single_step_set_feature(hcd, +									wIndex); +				spin_lock_irqsave(&ehci->lock, flags); +				break; +			} +#endif  			if (!selector || selector > 5)  				goto error; +			spin_unlock_irqrestore(&ehci->lock, flags);  			ehci_quiesce(ehci); +			spin_lock_irqsave(&ehci->lock, flags); + +			/* Put all enabled ports into suspend */ +			while (ports--) { +				u32 __iomem *sreg = +						&ehci->regs->port_status[ports]; + +				temp = ehci_readl(ehci, sreg) & ~PORT_RWC_BITS; +				if (temp & PORT_PE) +					ehci_writel(ehci, temp | PORT_SUSPEND, +							sreg); +			} + +			spin_unlock_irqrestore(&ehci->lock, flags);  			ehci_halt(ehci); +			spin_lock_irqsave(&ehci->lock, flags); + +			temp = ehci_readl(ehci, status_reg);  			temp |= selector << 16;  			ehci_writel(ehci, temp, status_reg);  			break; @@ -1103,6 +1276,7 @@ error_exit:  	spin_unlock_irqrestore (&ehci->lock, flags);  	return retval;  } +EXPORT_SYMBOL_GPL(ehci_hub_control);  static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)  {  | 
