mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-07 07:05:20 +00:00
[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
7 changed files with 33 additions and 93 deletions
|
@ -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, ®s->donehead), next, size);
|
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) \
|
#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_vdbg (ohci, "resume detect\n");
|
||||||
ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus);
|
ohci_writel (ohci, OHCI_INTR_RD, ®s->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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
||||||
(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:
|
||||||
#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:
|
||||||
) {
|
) {
|
||||||
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 (
|
||||||
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)
|
||||||
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)
|
||||||
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);
|
|
||||||
ohci_to_hcd(ohci)->self.root_hub->state =
|
omap_ohci_clock_power(0);
|
||||||
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
|
dev->power.power_state = PMSG_ON;
|
||||||
/* get extra cleanup even if remote wakeup isn't in use */
|
usb_hcd_resume_root_hub(dev_get_drvdata(dev));
|
||||||
status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub);
|
return 0;
|
||||||
#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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#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)
|
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 */
|
/* FIXME these PMAC things get called in the wrong places. ASIC
|
||||||
if (time_before (jiffies, ohci->next_statechange))
|
* clocks should be turned off AFTER entering D3, and on BEFORE
|
||||||
msleep (100);
|
* trying to enter D0. Evidently the PCI layer doesn't currently
|
||||||
|
* provide the right sort of platform hooks for this ...
|
||||||
#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);
|
|
||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
#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 {
|
||||||
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 */
|
||||||
|
|
Loading…
Add table
Reference in a new issue