diff options
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
| -rw-r--r-- | drivers/usb/host/ohci-hub.c | 113 | 
1 files changed, 39 insertions, 74 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index cddcda95b57..b4940de1eba 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -90,6 +90,24 @@ __acquires(ohci->lock)  	dl_done_list (ohci);  	finish_unlinks (ohci, ohci_frame_no(ohci)); +	/* +	 * Some controllers don't handle "global" suspend properly if +	 * there are unsuspended ports.  For these controllers, put all +	 * the enabled ports into suspend before suspending the root hub. +	 */ +	if (ohci->flags & OHCI_QUIRK_GLOBAL_SUSPEND) { +		__hc32 __iomem	*portstat = ohci->regs->roothub.portstatus; +		int		i; +		unsigned	temp; + +		for (i = 0; i < ohci->num_ports; (++i, ++portstat)) { +			temp = ohci_readl(ohci, portstat); +			if ((temp & (RH_PS_PES | RH_PS_PSS)) == +					RH_PS_PES) +				ohci_writel(ohci, RH_PS_PSS, portstat); +		} +	} +  	/* maybe resume can wake root hub */  	if (ohci_to_hcd(ohci)->self.root_hub->do_remote_wakeup || autostop) {  		ohci->hc_control |= OHCI_CTRL_RWE; @@ -111,6 +129,7 @@ __acquires(ohci->lock)  	if (!autostop) {  		ohci->next_statechange = jiffies + msecs_to_jiffies (5);  		ohci->autostop = 0; +		ohci->rh_state = OHCI_RH_SUSPENDED;  	}  done: @@ -140,7 +159,7 @@ __acquires(ohci->lock)  	if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {  		/* this can happen after resuming a swsusp snapshot */ -		if (hcd->state == HC_STATE_RESUMING) { +		if (ohci->rh_state != OHCI_RH_RUNNING) {  			ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",  					ohci->hc_control);  			status = -EBUSY; @@ -175,7 +194,6 @@ __acquires(ohci->lock)  	if (status == -EBUSY) {  		if (!autostopped) {  			spin_unlock_irq (&ohci->lock); -			(void) ohci_init (ohci);  			status = ohci_restart (ohci);  			usb_root_hub_lost_power(hcd->self.root_hub); @@ -212,10 +230,11 @@ __acquires(ohci->lock)  	/* Sometimes PCI D3 suspend trashes frame timings ... */  	periodic_reinit (ohci); -	/* the following code is executed with ohci->lock held and -	 * irqs disabled if and only if autostopped is true +	/* +	 * The following code is executed with ohci->lock held and +	 * irqs disabled if and only if autostopped is true.  This +	 * will cause sparse to warn about a "context imbalance".  	 */ -  skip_resume:  	/* interrupts might have been disabled */  	ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable); @@ -274,6 +293,7 @@ skip_resume:  		(void) ohci_readl (ohci, &ohci->regs->control);  	} +	ohci->rh_state = OHCI_RH_RUNNING;  	return 0;  } @@ -314,54 +334,6 @@ static int ohci_bus_resume (struct usb_hcd *hcd)  	return rc;  } -/* Carry out the final steps of resuming the controller device */ -static void ohci_finish_controller_resume(struct usb_hcd *hcd) -{ -	struct ohci_hcd		*ohci = hcd_to_ohci(hcd); -	int			port; -	bool			need_reinit = false; - -	/* See if the controller is already running or has been reset */ -	ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); -	if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { -		need_reinit = true; -	} else { -		switch (ohci->hc_control & OHCI_CTRL_HCFS) { -		case OHCI_USB_OPER: -		case OHCI_USB_RESET: -			need_reinit = true; -		} -	} - -	/* If needed, reinitialize and suspend the root hub */ -	if (need_reinit) { -		spin_lock_irq(&ohci->lock); -		hcd->state = HC_STATE_RESUMING; -		ohci_rh_resume(ohci); -		hcd->state = HC_STATE_QUIESCING; -		ohci_rh_suspend(ohci, 0); -		hcd->state = HC_STATE_SUSPENDED; -		spin_unlock_irq(&ohci->lock); -	} - -	/* Normally just turn on port power and enable interrupts */ -	else { -		ohci_dbg(ohci, "powerup ports\n"); -		for (port = 0; port < ohci->num_ports; port++) -			ohci_writel(ohci, RH_PS_PPS, -					&ohci->regs->roothub.portstatus[port]); - -		ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrenable); -		ohci_readl(ohci, &ohci->regs->intrenable); -		msleep(20); -	} - -	/* Does the root hub have a port wakeup pending? */ -	if (ohci_readl(ohci, &ohci->regs->intrstatus) & -			(OHCI_INTR_RD | OHCI_INTR_RHSC)) -		usb_hcd_resume_root_hub(hcd); -} -  /* Carry out polling-, autostop-, and autoresume-related state changes */  static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,  		int any_connected, int rhsc_status) @@ -484,8 +456,7 @@ static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed,  /* build "status change" packet (one or two bytes) from HC registers */ -static int -ohci_hub_status_data (struct usb_hcd *hcd, char *buf) +int ohci_hub_status_data(struct usb_hcd *hcd, char *buf)  {  	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);  	int		i, changed = 0, length = 1; @@ -550,6 +521,7 @@ done:  	return changed ? length : 0;  } +EXPORT_SYMBOL_GPL(ohci_hub_status_data);  /*-------------------------------------------------------------------------*/ @@ -578,17 +550,18 @@ ohci_hub_descriptor (  	    temp |= 0x0010;  	else if (rh & RH_A_OCPM)	/* per-port overcurrent reporting? */  	    temp |= 0x0008; -	desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp); +	desc->wHubCharacteristics = cpu_to_le16(temp); -	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */ +	/* ports removable, and usb 1.0 legacy PortPwrCtrlMask */  	rh = roothub_b (ohci); -	memset(desc->bitmap, 0xff, sizeof(desc->bitmap)); -	desc->bitmap [0] = rh & RH_B_DR; +	memset(desc->u.hs.DeviceRemovable, 0xff, +			sizeof(desc->u.hs.DeviceRemovable)); +	desc->u.hs.DeviceRemovable[0] = rh & RH_B_DR;  	if (ohci->num_ports > 7) { -		desc->bitmap [1] = (rh & RH_B_DR) >> 8; -		desc->bitmap [2] = 0xff; +		desc->u.hs.DeviceRemovable[1] = (rh & RH_B_DR) >> 8; +		desc->u.hs.DeviceRemovable[2] = 0xff;  	} else -		desc->bitmap [1] = 0xff; +		desc->u.hs.DeviceRemovable[1] = 0xff;  }  /*-------------------------------------------------------------------------*/ @@ -625,14 +598,8 @@ static int ohci_start_port_reset (struct usb_hcd *hcd, unsigned port)  /* See usb 7.1.7.5:  root hubs must issue at least 50 msec reset signaling,   * not necessarily continuous ... to guard against resume signaling. - * The short timeout is safe for non-root hubs, and is backward-compatible - * with earlier Linux hosts.   */ -#ifdef	CONFIG_USB_SUSPEND  #define	PORT_RESET_MSEC		50 -#else -#define	PORT_RESET_MSEC		10 -#endif  /* this timer value might be vendor-specific ... */  #define	PORT_RESET_HW_MSEC	10 @@ -697,7 +664,7 @@ static inline int root_port_reset (struct ohci_hcd *ohci, unsigned port)  	return 0;  } -static int ohci_hub_control ( +int ohci_hub_control(  	struct usb_hcd	*hcd,  	u16		typeReq,  	u16		wValue, @@ -776,10 +743,8 @@ static int ohci_hub_control (  		temp = roothub_portstatus (ohci, wIndex);  		put_unaligned_le32(temp, buf); -#ifndef	OHCI_VERBOSE_DEBUG -	if (*(u16*)(buf+2))	/* only if wPortChange is interesting */ -#endif -		dbg_port (ohci, "GetStatus", wIndex, temp); +		if (*(u16*)(buf+2))	/* only if wPortChange is interesting */ +			dbg_port(ohci, "GetStatus", wIndex, temp);  		break;  	case SetHubFeature:  		switch (wValue) { @@ -825,4 +790,4 @@ error:  	}  	return retval;  } - +EXPORT_SYMBOL_GPL(ohci_hub_control);  | 
