Commit f197b2c5 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg 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(-)
parent 5f827ea3
...@@ -193,10 +193,6 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size) ...@@ -193,10 +193,6 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size)
maybe_print_eds (controller, "donehead", maybe_print_eds (controller, "donehead",
ohci_readl (controller, &regs->donehead), next, size); ohci_readl (controller, &regs->donehead), next, size);
/* broken fminterval means traffic won't flow! */
ohci_dbg (controller, "fminterval %08x\n",
ohci_readl (controller, &regs->fminterval));
} }
#define dbg_port_sw(hc,num,value,next,size) \ #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) ...@@ -723,7 +723,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
ohci_vdbg (ohci, "resume detect\n"); ohci_vdbg (ohci, "resume detect\n");
ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus); ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus);
if (hcd->state != HC_STATE_QUIESCING) if (hcd->state != HC_STATE_QUIESCING)
schedule_work(&ohci->rh_resume); usb_hcd_resume_root_hub(hcd);
} }
if (ints & OHCI_INTR_WDH) { if (ints & OHCI_INTR_WDH) {
......
...@@ -73,7 +73,6 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) ...@@ -73,7 +73,6 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
ohci_dbg (ohci, "suspend root hub\n"); ohci_dbg (ohci, "suspend root hub\n");
/* First stop any processing */ /* First stop any processing */
hcd->state = HC_STATE_QUIESCING;
if (ohci->hc_control & OHCI_SCHED_ENABLES) { if (ohci->hc_control & OHCI_SCHED_ENABLES) {
int limit; int limit;
...@@ -108,7 +107,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) ...@@ -108,7 +107,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
else else
ohci->hc_control &= ~OHCI_CTRL_RWE; 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_CTRL_HCFS;
ohci->hc_control |= OHCI_USB_SUSPEND; ohci->hc_control |= OHCI_USB_SUSPEND;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
...@@ -118,8 +119,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) ...@@ -118,8 +119,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
ohci->next_statechange = jiffies + msecs_to_jiffies (5); ohci->next_statechange = jiffies + msecs_to_jiffies (5);
done: done:
/* external suspend vs self autosuspend ... same effect */
if (status == 0) if (status == 0)
hcd->state = HC_STATE_SUSPENDED; usb_hcd_suspend_root_hub(hcd);
spin_unlock_irqrestore (&ohci->lock, flags); spin_unlock_irqrestore (&ohci->lock, flags);
return status; return status;
} }
...@@ -146,7 +148,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ...@@ -146,7 +148,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { 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) { if (hcd->state == HC_STATE_RESUMING) {
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n", ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
ohci->hc_control); ohci->hc_control);
...@@ -169,11 +171,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ...@@ -169,11 +171,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
ohci_info (ohci, "wakeup\n"); ohci_info (ohci, "wakeup\n");
break; break;
case OHCI_USB_OPER: case OHCI_USB_OPER:
ohci_dbg (ohci, "already resumed\n"); /* this can happen after resuming a swsusp snapshot */
status = 0; ohci_dbg (ohci, "snapshot resume? reinit\n");
status = -EBUSY;
break; break;
default: /* RESET, we lost power */ default: /* RESET, we lost power */
ohci_dbg (ohci, "root hub hardware reset\n"); ohci_dbg (ohci, "lost power\n");
status = -EBUSY; status = -EBUSY;
} }
spin_unlock_irq (&ohci->lock); spin_unlock_irq (&ohci->lock);
...@@ -198,8 +201,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ...@@ -198,8 +201,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
} }
/* Some controllers (lucent erratum) need extra-long delays */ /* Some controllers (lucent erratum) need extra-long delays */
hcd->state = HC_STATE_RESUMING; msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1);
mdelay (20 /* usb 11.5.1.10 */ + 15);
temp = ohci_readl (ohci, &ohci->regs->control); temp = ohci_readl (ohci, &ohci->regs->control);
temp &= OHCI_CTRL_HCFS; temp &= OHCI_CTRL_HCFS;
...@@ -273,27 +275,9 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ...@@ -273,27 +275,9 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
(void) ohci_readl (ohci, &ohci->regs->control); (void) ohci_readl (ohci, &ohci->regs->control);
} }
hcd->state = HC_STATE_RUNNING;
return 0; 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 */ #endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -367,7 +351,6 @@ done: ...@@ -367,7 +351,6 @@ done:
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* save power by suspending idle root hubs; /* save power by suspending idle root hubs;
* INTR_RD wakes us when there's work * 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 if (can_suspend
&& !changed && !changed
...@@ -380,7 +363,6 @@ done: ...@@ -380,7 +363,6 @@ done:
) { ) {
ohci_vdbg (ohci, "autosuspend\n"); ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_hub_suspend (hcd); (void) ohci_hub_suspend (hcd);
hcd->state = HC_STATE_RUNNING;
usb_unlock_device (hcd->self.root_hub); usb_unlock_device (hcd->self.root_hub);
} }
#endif #endif
...@@ -554,7 +536,7 @@ static int ohci_hub_control ( ...@@ -554,7 +536,7 @@ static int ohci_hub_control (
temp = RH_PS_POCI; temp = RH_PS_POCI;
if ((ohci->hc_control & OHCI_CTRL_HCFS) if ((ohci->hc_control & OHCI_CTRL_HCFS)
!= OHCI_USB_OPER) != OHCI_USB_OPER)
schedule_work (&ohci->rh_resume); usb_hcd_resume_root_hub(hcd);
break; break;
case USB_PORT_FEAT_C_SUSPEND: case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC; temp = RH_PS_PSSC;
......
...@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) ...@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
ohci->next_statechange = jiffies; ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock); spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending); INIT_LIST_HEAD (&ohci->pending);
INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci));
ohci->reboot_notifier.notifier_call = ohci_reboot; ohci->reboot_notifier.notifier_call = ohci_reboot;
} }
......
...@@ -458,41 +458,29 @@ static int ohci_hcd_omap_drv_remove(struct device *dev) ...@@ -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) static int ohci_omap_suspend(struct device *dev, pm_message_t message)
{ {
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = -EINVAL;
down(&ohci_to_hcd(ohci)->self.root_hub->serialize); if (time_before(jiffies, ohci->next_statechange))
status = ohci_hub_suspend(ohci_to_hcd(ohci)); msleep(5);
if (status == 0) { ohci->next_statechange = jiffies;
omap_ohci_clock_power(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; ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
dev->power.power_state = PMSG_SUSPEND; dev->power.power_state = PMSG_SUSPEND;
} return 0;
up(&ohci_to_hcd(ohci)->self.root_hub->serialize);
return status;
} }
static int ohci_omap_resume(struct device *dev) static int ohci_omap_resume(struct device *dev)
{ {
struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev));
int status = 0;
if (time_before(jiffies, ohci->next_statechange)) if (time_before(jiffies, ohci->next_statechange))
msleep(5); msleep(5);
ohci->next_statechange = jiffies; ohci->next_statechange = jiffies;
omap_ohci_clock_power(1); 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; dev->power.power_state = PMSG_ON;
return status; usb_hcd_resume_root_hub(dev_get_drvdata(dev));
return 0;
} }
#endif #endif
......
...@@ -112,23 +112,13 @@ ohci_pci_start (struct usb_hcd *hcd) ...@@ -112,23 +112,13 @@ ohci_pci_start (struct usb_hcd *hcd)
static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) 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 #ifdef CONFIG_PPC_PMAC
if (_machine == _MACH_Pmac) { if (_machine == _MACH_Pmac) {
struct device_node *of_node; struct device_node *of_node;
...@@ -145,9 +135,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) ...@@ -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) static int ohci_pci_resume (struct usb_hcd *hcd)
{ {
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int retval = 0;
#ifdef CONFIG_PPC_PMAC #ifdef CONFIG_PPC_PMAC
if (_machine == _MACH_Pmac) { if (_machine == _MACH_Pmac) {
struct device_node *of_node; struct device_node *of_node;
...@@ -159,19 +146,8 @@ static int ohci_pci_resume (struct usb_hcd *hcd) ...@@ -159,19 +146,8 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
} }
#endif /* CONFIG_PPC_PMAC */ #endif /* CONFIG_PPC_PMAC */
/* resume root hub */ usb_hcd_resume_root_hub(hcd);
if (time_before (jiffies, ohci->next_statechange)) return 0;
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;
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
......
...@@ -389,7 +389,6 @@ struct ohci_hcd { ...@@ -389,7 +389,6 @@ struct ohci_hcd {
unsigned long next_statechange; /* suspend/resume */ unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */ u32 fminterval; /* saved register */
struct work_struct rh_resume;
struct notifier_block reboot_notifier; struct notifier_block reboot_notifier;
unsigned long flags; /* for HC bugs */ unsigned long flags; /* for HC bugs */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment