forked from Minki/linux
[PATCH] OHCI PM updates
This simplifies the OHCI root hub suspend logic: - Uses new usbcore root hub calls to make autosuspend work again: * Uses a newish usbcore root hub wakeup mechanism, making requests to khubd not keventd. * Uses an even newer sibling suspend hook. - Expect someone always made usbcore call ohci_hub_suspend() before bus glue fires; and that ohci_hub_resume() is only called after that bus glue ran. Previously, only CONFIG_USB_SUSPEND promised those things. (Includes updates to PCI and OMAP bus glue.) - Handle a not-noticed-before special case during resume from one of the swsusp snapshots when using "usb-handoff": the controller isn't left in RESET state. (A bug to fix in the usb-handoff code...) Also cleans up a minor debug printk glitch, and switches an mdelay over to an msleep (how did that stick around for so long?). Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> drivers/usb/host/ohci-dbg.c | 4 ---- drivers/usb/host/ohci-hcd.c | 2 +- drivers/usb/host/ohci-hub.c | 42 ++++++++++++------------------------------ drivers/usb/host/ohci-mem.c | 1 - drivers/usb/host/ohci-omap.c | 36 ++++++++++++------------------------ drivers/usb/host/ohci-pci.c | 40 ++++++++-------------------------------- drivers/usb/host/ohci.h | 1 - 7 files changed, 33 insertions(+), 93 deletions(-)
This commit is contained in:
parent
5f827ea3c3
commit
f197b2c54b
@ -193,10 +193,6 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
|
||||
|
||||
maybe_print_eds (controller, "donehead",
|
||||
ohci_readl (controller, ®s->donehead), next, size);
|
||||
|
||||
/* broken fminterval means traffic won't flow! */
|
||||
ohci_dbg (controller, "fminterval %08x\n",
|
||||
ohci_readl (controller, ®s->fminterval));
|
||||
}
|
||||
|
||||
#define dbg_port_sw(hc,num,value,next,size) \
|
||||
|
@ -723,7 +723,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
|
||||
ohci_vdbg (ohci, "resume detect\n");
|
||||
ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus);
|
||||
if (hcd->state != HC_STATE_QUIESCING)
|
||||
schedule_work(&ohci->rh_resume);
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
}
|
||||
|
||||
if (ints & OHCI_INTR_WDH) {
|
||||
|
@ -73,7 +73,6 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
|
||||
ohci_dbg (ohci, "suspend root hub\n");
|
||||
|
||||
/* First stop any processing */
|
||||
hcd->state = HC_STATE_QUIESCING;
|
||||
if (ohci->hc_control & OHCI_SCHED_ENABLES) {
|
||||
int limit;
|
||||
|
||||
@ -108,7 +107,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
|
||||
else
|
||||
ohci->hc_control &= ~OHCI_CTRL_RWE;
|
||||
|
||||
/* Suspend hub */
|
||||
/* Suspend hub ... this is the "global (to this bus) suspend" mode,
|
||||
* which doesn't imply ports will first be individually suspended.
|
||||
*/
|
||||
ohci->hc_control &= ~OHCI_CTRL_HCFS;
|
||||
ohci->hc_control |= OHCI_USB_SUSPEND;
|
||||
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
|
||||
@ -118,8 +119,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
|
||||
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
|
||||
|
||||
done:
|
||||
/* external suspend vs self autosuspend ... same effect */
|
||||
if (status == 0)
|
||||
hcd->state = HC_STATE_SUSPENDED;
|
||||
usb_hcd_suspend_root_hub(hcd);
|
||||
spin_unlock_irqrestore (&ohci->lock, flags);
|
||||
return status;
|
||||
}
|
||||
@ -146,7 +148,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
||||
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
|
||||
|
||||
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
|
||||
/* this can happen after suspend-to-disk */
|
||||
/* this can happen after resuming a swsusp snapshot */
|
||||
if (hcd->state == HC_STATE_RESUMING) {
|
||||
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
|
||||
ohci->hc_control);
|
||||
@ -169,11 +171,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
||||
ohci_info (ohci, "wakeup\n");
|
||||
break;
|
||||
case OHCI_USB_OPER:
|
||||
ohci_dbg (ohci, "already resumed\n");
|
||||
status = 0;
|
||||
/* this can happen after resuming a swsusp snapshot */
|
||||
ohci_dbg (ohci, "snapshot resume? reinit\n");
|
||||
status = -EBUSY;
|
||||
break;
|
||||
default: /* RESET, we lost power */
|
||||
ohci_dbg (ohci, "root hub hardware reset\n");
|
||||
ohci_dbg (ohci, "lost power\n");
|
||||
status = -EBUSY;
|
||||
}
|
||||
spin_unlock_irq (&ohci->lock);
|
||||
@ -198,8 +201,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
||||
}
|
||||
|
||||
/* Some controllers (lucent erratum) need extra-long delays */
|
||||
hcd->state = HC_STATE_RESUMING;
|
||||
mdelay (20 /* usb 11.5.1.10 */ + 15);
|
||||
msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1);
|
||||
|
||||
temp = ohci_readl (ohci, &ohci->regs->control);
|
||||
temp &= OHCI_CTRL_HCFS;
|
||||
@ -273,27 +275,9 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
|
||||
(void) ohci_readl (ohci, &ohci->regs->control);
|
||||
}
|
||||
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ohci_rh_resume (void *_hcd)
|
||||
{
|
||||
struct usb_hcd *hcd = _hcd;
|
||||
|
||||
usb_lock_device (hcd->self.root_hub);
|
||||
(void) ohci_hub_resume (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void ohci_rh_resume (void *_hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (_hcd);
|
||||
ohci_dbg(ohci, "rh_resume ??\n");
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -367,7 +351,6 @@ done:
|
||||
#ifdef CONFIG_PM
|
||||
/* save power by suspending idle root hubs;
|
||||
* INTR_RD wakes us when there's work
|
||||
* NOTE: if we can do this, we don't need a root hub timer!
|
||||
*/
|
||||
if (can_suspend
|
||||
&& !changed
|
||||
@ -380,7 +363,6 @@ done:
|
||||
) {
|
||||
ohci_vdbg (ohci, "autosuspend\n");
|
||||
(void) ohci_hub_suspend (hcd);
|
||||
hcd->state = HC_STATE_RUNNING;
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
}
|
||||
#endif
|
||||
@ -554,7 +536,7 @@ static int ohci_hub_control (
|
||||
temp = RH_PS_POCI;
|
||||
if ((ohci->hc_control & OHCI_CTRL_HCFS)
|
||||
!= OHCI_USB_OPER)
|
||||
schedule_work (&ohci->rh_resume);
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
break;
|
||||
case USB_PORT_FEAT_C_SUSPEND:
|
||||
temp = RH_PS_PSSC;
|
||||
|
@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
|
||||
ohci->next_statechange = jiffies;
|
||||
spin_lock_init (&ohci->lock);
|
||||
INIT_LIST_HEAD (&ohci->pending);
|
||||
INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci));
|
||||
ohci->reboot_notifier.notifier_call = ohci_reboot;
|
||||
}
|
||||
|
||||
|
@ -458,41 +458,29 @@ static int ohci_hcd_omap_drv_remove(struct device *dev)
|
||||
static int ohci_omap_suspend(struct device *dev, pm_message_t message)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
|
||||
int status = -EINVAL;
|
||||
|
||||
down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
|
||||
status = ohci_hub_suspend(ohci_to_hcd(ohci));
|
||||
if (status == 0) {
|
||||
omap_ohci_clock_power(0);
|
||||
ohci_to_hcd(ohci)->self.root_hub->state =
|
||||
USB_STATE_SUSPENDED;
|
||||
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
|
||||
dev->power.power_state = PMSG_SUSPEND;
|
||||
}
|
||||
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
|
||||
return status;
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
omap_ohci_clock_power(0);
|
||||
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
|
||||
dev->power.power_state = PMSG_SUSPEND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ohci_omap_resume(struct device *dev)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
|
||||
int status = 0;
|
||||
|
||||
if (time_before(jiffies, ohci->next_statechange))
|
||||
msleep(5);
|
||||
ohci->next_statechange = jiffies;
|
||||
|
||||
omap_ohci_clock_power(1);
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
/* get extra cleanup even if remote wakeup isn't in use */
|
||||
status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub);
|
||||
#else
|
||||
down(&ohci_to_hcd(ohci)->self.root_hub->serialize);
|
||||
status = ohci_hub_resume(ohci_to_hcd(ohci));
|
||||
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
|
||||
#endif
|
||||
if (status == 0)
|
||||
dev->power.power_state = PMSG_ON;
|
||||
return status;
|
||||
dev->power.power_state = PMSG_ON;
|
||||
usb_hcd_resume_root_hub(dev_get_drvdata(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -112,23 +112,13 @@ ohci_pci_start (struct usb_hcd *hcd)
|
||||
|
||||
static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
/* root hub was already suspended */
|
||||
|
||||
/* suspend root hub, hoping it keeps power during suspend */
|
||||
if (time_before (jiffies, ohci->next_statechange))
|
||||
msleep (100);
|
||||
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
(void) usb_suspend_device (hcd->self.root_hub);
|
||||
#else
|
||||
usb_lock_device (hcd->self.root_hub);
|
||||
(void) ohci_hub_suspend (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
#endif
|
||||
|
||||
/* let things settle down a bit */
|
||||
msleep (100);
|
||||
|
||||
/* FIXME these PMAC things get called in the wrong places. ASIC
|
||||
* clocks should be turned off AFTER entering D3, and on BEFORE
|
||||
* trying to enter D0. Evidently the PCI layer doesn't currently
|
||||
* provide the right sort of platform hooks for this ...
|
||||
*/
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
if (_machine == _MACH_Pmac) {
|
||||
struct device_node *of_node;
|
||||
@ -145,9 +135,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
|
||||
|
||||
static int ohci_pci_resume (struct usb_hcd *hcd)
|
||||
{
|
||||
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
|
||||
int retval = 0;
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
if (_machine == _MACH_Pmac) {
|
||||
struct device_node *of_node;
|
||||
@ -159,19 +146,8 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
|
||||
}
|
||||
#endif /* CONFIG_PPC_PMAC */
|
||||
|
||||
/* resume root hub */
|
||||
if (time_before (jiffies, ohci->next_statechange))
|
||||
msleep (100);
|
||||
#ifdef CONFIG_USB_SUSPEND
|
||||
/* get extra cleanup even if remote wakeup isn't in use */
|
||||
retval = usb_resume_device (hcd->self.root_hub);
|
||||
#else
|
||||
usb_lock_device (hcd->self.root_hub);
|
||||
retval = ohci_hub_resume (hcd);
|
||||
usb_unlock_device (hcd->self.root_hub);
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -389,7 +389,6 @@ struct ohci_hcd {
|
||||
unsigned long next_statechange; /* suspend/resume */
|
||||
u32 fminterval; /* saved register */
|
||||
|
||||
struct work_struct rh_resume;
|
||||
struct notifier_block reboot_notifier;
|
||||
|
||||
unsigned long flags; /* for HC bugs */
|
||||
|
Loading…
Reference in New Issue
Block a user