diff options
author | David Brownell <david-b@pacbell.net> | 2005-09-23 01:42:53 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-10-28 19:47:40 -0400 |
commit | f197b2c54b9d9a133a9a8ff92f35cadf8945ea14 (patch) | |
tree | a5a4da41e38d8681977a9f3b52fa2e5f1db278ea /drivers | |
parent | 5f827ea3c3820cd8e0a1a35e4d275c8b78ee94e1 (diff) |
[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(-)
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/host/ohci-dbg.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 42 | ||||
-rw-r--r-- | drivers/usb/host/ohci-mem.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ohci-omap.c | 38 | ||||
-rw-r--r-- | drivers/usb/host/ohci-pci.c | 40 | ||||
-rw-r--r-- | drivers/usb/host/ohci.h | 1 |
7 files changed, 34 insertions, 94 deletions
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 7924c74f958e..7bfffcbbd226 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c | |||
@@ -193,10 +193,6 @@ ohci_dump_status (struct ohci_hcd *controller, char **next, unsigned *size) | |||
193 | 193 | ||
194 | maybe_print_eds (controller, "donehead", | 194 | maybe_print_eds (controller, "donehead", |
195 | ohci_readl (controller, ®s->donehead), next, size); | 195 | ohci_readl (controller, ®s->donehead), next, size); |
196 | |||
197 | /* broken fminterval means traffic won't flow! */ | ||
198 | ohci_dbg (controller, "fminterval %08x\n", | ||
199 | ohci_readl (controller, ®s->fminterval)); | ||
200 | } | 196 | } |
201 | 197 | ||
202 | #define dbg_port_sw(hc,num,value,next,size) \ | 198 | #define dbg_port_sw(hc,num,value,next,size) \ |
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index c26ab5f2a403..5c0c6c8a7a82 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
@@ -723,7 +723,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs) | |||
723 | ohci_vdbg (ohci, "resume detect\n"); | 723 | ohci_vdbg (ohci, "resume detect\n"); |
724 | ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus); | 724 | ohci_writel (ohci, OHCI_INTR_RD, ®s->intrstatus); |
725 | if (hcd->state != HC_STATE_QUIESCING) | 725 | if (hcd->state != HC_STATE_QUIESCING) |
726 | schedule_work(&ohci->rh_resume); | 726 | usb_hcd_resume_root_hub(hcd); |
727 | } | 727 | } |
728 | 728 | ||
729 | if (ints & OHCI_INTR_WDH) { | 729 | if (ints & OHCI_INTR_WDH) { |
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index b96948ea3005..39a60e731ec2 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
@@ -73,7 +73,6 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) | |||
73 | ohci_dbg (ohci, "suspend root hub\n"); | 73 | ohci_dbg (ohci, "suspend root hub\n"); |
74 | 74 | ||
75 | /* First stop any processing */ | 75 | /* First stop any processing */ |
76 | hcd->state = HC_STATE_QUIESCING; | ||
77 | if (ohci->hc_control & OHCI_SCHED_ENABLES) { | 76 | if (ohci->hc_control & OHCI_SCHED_ENABLES) { |
78 | int limit; | 77 | int limit; |
79 | 78 | ||
@@ -108,7 +107,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) | |||
108 | else | 107 | else |
109 | ohci->hc_control &= ~OHCI_CTRL_RWE; | 108 | ohci->hc_control &= ~OHCI_CTRL_RWE; |
110 | 109 | ||
111 | /* Suspend hub */ | 110 | /* Suspend hub ... this is the "global (to this bus) suspend" mode, |
111 | * which doesn't imply ports will first be individually suspended. | ||
112 | */ | ||
112 | ohci->hc_control &= ~OHCI_CTRL_HCFS; | 113 | ohci->hc_control &= ~OHCI_CTRL_HCFS; |
113 | ohci->hc_control |= OHCI_USB_SUSPEND; | 114 | ohci->hc_control |= OHCI_USB_SUSPEND; |
114 | ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); | 115 | ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); |
@@ -118,8 +119,9 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) | |||
118 | ohci->next_statechange = jiffies + msecs_to_jiffies (5); | 119 | ohci->next_statechange = jiffies + msecs_to_jiffies (5); |
119 | 120 | ||
120 | done: | 121 | done: |
122 | /* external suspend vs self autosuspend ... same effect */ | ||
121 | if (status == 0) | 123 | if (status == 0) |
122 | hcd->state = HC_STATE_SUSPENDED; | 124 | usb_hcd_suspend_root_hub(hcd); |
123 | spin_unlock_irqrestore (&ohci->lock, flags); | 125 | spin_unlock_irqrestore (&ohci->lock, flags); |
124 | return status; | 126 | return status; |
125 | } | 127 | } |
@@ -146,7 +148,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) | |||
146 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); | 148 | ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); |
147 | 149 | ||
148 | if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { | 150 | if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { |
149 | /* this can happen after suspend-to-disk */ | 151 | /* this can happen after resuming a swsusp snapshot */ |
150 | if (hcd->state == HC_STATE_RESUMING) { | 152 | if (hcd->state == HC_STATE_RESUMING) { |
151 | ohci_dbg (ohci, "BIOS/SMM active, control %03x\n", | 153 | ohci_dbg (ohci, "BIOS/SMM active, control %03x\n", |
152 | ohci->hc_control); | 154 | ohci->hc_control); |
@@ -169,11 +171,12 @@ static int ohci_hub_resume (struct usb_hcd *hcd) | |||
169 | ohci_info (ohci, "wakeup\n"); | 171 | ohci_info (ohci, "wakeup\n"); |
170 | break; | 172 | break; |
171 | case OHCI_USB_OPER: | 173 | case OHCI_USB_OPER: |
172 | ohci_dbg (ohci, "already resumed\n"); | 174 | /* this can happen after resuming a swsusp snapshot */ |
173 | status = 0; | 175 | ohci_dbg (ohci, "snapshot resume? reinit\n"); |
176 | status = -EBUSY; | ||
174 | break; | 177 | break; |
175 | default: /* RESET, we lost power */ | 178 | default: /* RESET, we lost power */ |
176 | ohci_dbg (ohci, "root hub hardware reset\n"); | 179 | ohci_dbg (ohci, "lost power\n"); |
177 | status = -EBUSY; | 180 | status = -EBUSY; |
178 | } | 181 | } |
179 | spin_unlock_irq (&ohci->lock); | 182 | spin_unlock_irq (&ohci->lock); |
@@ -198,8 +201,7 @@ static int ohci_hub_resume (struct usb_hcd *hcd) | |||
198 | } | 201 | } |
199 | 202 | ||
200 | /* Some controllers (lucent erratum) need extra-long delays */ | 203 | /* Some controllers (lucent erratum) need extra-long delays */ |
201 | hcd->state = HC_STATE_RESUMING; | 204 | msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1); |
202 | mdelay (20 /* usb 11.5.1.10 */ + 15); | ||
203 | 205 | ||
204 | temp = ohci_readl (ohci, &ohci->regs->control); | 206 | temp = ohci_readl (ohci, &ohci->regs->control); |
205 | temp &= OHCI_CTRL_HCFS; | 207 | temp &= OHCI_CTRL_HCFS; |
@@ -273,27 +275,9 @@ static int ohci_hub_resume (struct usb_hcd *hcd) | |||
273 | (void) ohci_readl (ohci, &ohci->regs->control); | 275 | (void) ohci_readl (ohci, &ohci->regs->control); |
274 | } | 276 | } |
275 | 277 | ||
276 | hcd->state = HC_STATE_RUNNING; | ||
277 | return 0; | 278 | return 0; |
278 | } | 279 | } |
279 | 280 | ||
280 | static void ohci_rh_resume (void *_hcd) | ||
281 | { | ||
282 | struct usb_hcd *hcd = _hcd; | ||
283 | |||
284 | usb_lock_device (hcd->self.root_hub); | ||
285 | (void) ohci_hub_resume (hcd); | ||
286 | usb_unlock_device (hcd->self.root_hub); | ||
287 | } | ||
288 | |||
289 | #else | ||
290 | |||
291 | static void ohci_rh_resume (void *_hcd) | ||
292 | { | ||
293 | struct ohci_hcd *ohci = hcd_to_ohci (_hcd); | ||
294 | ohci_dbg(ohci, "rh_resume ??\n"); | ||
295 | } | ||
296 | |||
297 | #endif /* CONFIG_PM */ | 281 | #endif /* CONFIG_PM */ |
298 | 282 | ||
299 | /*-------------------------------------------------------------------------*/ | 283 | /*-------------------------------------------------------------------------*/ |
@@ -367,7 +351,6 @@ done: | |||
367 | #ifdef CONFIG_PM | 351 | #ifdef CONFIG_PM |
368 | /* save power by suspending idle root hubs; | 352 | /* save power by suspending idle root hubs; |
369 | * INTR_RD wakes us when there's work | 353 | * INTR_RD wakes us when there's work |
370 | * NOTE: if we can do this, we don't need a root hub timer! | ||
371 | */ | 354 | */ |
372 | if (can_suspend | 355 | if (can_suspend |
373 | && !changed | 356 | && !changed |
@@ -380,7 +363,6 @@ done: | |||
380 | ) { | 363 | ) { |
381 | ohci_vdbg (ohci, "autosuspend\n"); | 364 | ohci_vdbg (ohci, "autosuspend\n"); |
382 | (void) ohci_hub_suspend (hcd); | 365 | (void) ohci_hub_suspend (hcd); |
383 | hcd->state = HC_STATE_RUNNING; | ||
384 | usb_unlock_device (hcd->self.root_hub); | 366 | usb_unlock_device (hcd->self.root_hub); |
385 | } | 367 | } |
386 | #endif | 368 | #endif |
@@ -554,7 +536,7 @@ static int ohci_hub_control ( | |||
554 | temp = RH_PS_POCI; | 536 | temp = RH_PS_POCI; |
555 | if ((ohci->hc_control & OHCI_CTRL_HCFS) | 537 | if ((ohci->hc_control & OHCI_CTRL_HCFS) |
556 | != OHCI_USB_OPER) | 538 | != OHCI_USB_OPER) |
557 | schedule_work (&ohci->rh_resume); | 539 | usb_hcd_resume_root_hub(hcd); |
558 | break; | 540 | break; |
559 | case USB_PORT_FEAT_C_SUSPEND: | 541 | case USB_PORT_FEAT_C_SUSPEND: |
560 | temp = RH_PS_PSSC; | 542 | temp = RH_PS_PSSC; |
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index 9fb83dfb1eb4..bfbe328a4788 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c | |||
@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) | |||
28 | ohci->next_statechange = jiffies; | 28 | ohci->next_statechange = jiffies; |
29 | spin_lock_init (&ohci->lock); | 29 | spin_lock_init (&ohci->lock); |
30 | INIT_LIST_HEAD (&ohci->pending); | 30 | INIT_LIST_HEAD (&ohci->pending); |
31 | INIT_WORK (&ohci->rh_resume, ohci_rh_resume, ohci_to_hcd(ohci)); | ||
32 | ohci->reboot_notifier.notifier_call = ohci_reboot; | 31 | ohci->reboot_notifier.notifier_call = ohci_reboot; |
33 | } | 32 | } |
34 | 33 | ||
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index e67c07dc426d..b3498b9b965f 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c | |||
@@ -458,41 +458,29 @@ static int ohci_hcd_omap_drv_remove(struct device *dev) | |||
458 | static int ohci_omap_suspend(struct device *dev, pm_message_t message) | 458 | static int ohci_omap_suspend(struct device *dev, pm_message_t message) |
459 | { | 459 | { |
460 | struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); | 460 | struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); |
461 | int status = -EINVAL; | 461 | |
462 | 462 | if (time_before(jiffies, ohci->next_statechange)) | |
463 | down(&ohci_to_hcd(ohci)->self.root_hub->serialize); | 463 | msleep(5); |
464 | status = ohci_hub_suspend(ohci_to_hcd(ohci)); | 464 | ohci->next_statechange = jiffies; |
465 | if (status == 0) { | 465 | |
466 | omap_ohci_clock_power(0); | 466 | omap_ohci_clock_power(0); |
467 | ohci_to_hcd(ohci)->self.root_hub->state = | 467 | ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; |
468 | USB_STATE_SUSPENDED; | 468 | dev->power.power_state = PMSG_SUSPEND; |
469 | ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; | 469 | return 0; |
470 | dev->power.power_state = PMSG_SUSPEND; | ||
471 | } | ||
472 | up(&ohci_to_hcd(ohci)->self.root_hub->serialize); | ||
473 | return status; | ||
474 | } | 470 | } |
475 | 471 | ||
476 | static int ohci_omap_resume(struct device *dev) | 472 | static int ohci_omap_resume(struct device *dev) |
477 | { | 473 | { |
478 | struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); | 474 | struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); |
479 | int status = 0; | ||
480 | 475 | ||
481 | if (time_before(jiffies, ohci->next_statechange)) | 476 | if (time_before(jiffies, ohci->next_statechange)) |
482 | msleep(5); | 477 | msleep(5); |
483 | ohci->next_statechange = jiffies; | 478 | ohci->next_statechange = jiffies; |
479 | |||
484 | omap_ohci_clock_power(1); | 480 | omap_ohci_clock_power(1); |
485 | #ifdef CONFIG_USB_SUSPEND | 481 | dev->power.power_state = PMSG_ON; |
486 | /* get extra cleanup even if remote wakeup isn't in use */ | 482 | usb_hcd_resume_root_hub(dev_get_drvdata(dev)); |
487 | status = usb_resume_device(ohci_to_hcd(ohci)->self.root_hub); | 483 | return 0; |
488 | #else | ||
489 | down(&ohci_to_hcd(ohci)->self.root_hub->serialize); | ||
490 | status = ohci_hub_resume(ohci_to_hcd(ohci)); | ||
491 | up(&ohci_to_hcd(ohci)->self.root_hub->serialize); | ||
492 | #endif | ||
493 | if (status == 0) | ||
494 | dev->power.power_state = PMSG_ON; | ||
495 | return status; | ||
496 | } | 484 | } |
497 | 485 | ||
498 | #endif | 486 | #endif |
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 0afa237b02ad..99a0ad41aec5 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c | |||
@@ -112,23 +112,13 @@ ohci_pci_start (struct usb_hcd *hcd) | |||
112 | 112 | ||
113 | static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) | 113 | static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) |
114 | { | 114 | { |
115 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 115 | /* root hub was already suspended */ |
116 | |||
117 | /* suspend root hub, hoping it keeps power during suspend */ | ||
118 | if (time_before (jiffies, ohci->next_statechange)) | ||
119 | msleep (100); | ||
120 | |||
121 | #ifdef CONFIG_USB_SUSPEND | ||
122 | (void) usb_suspend_device (hcd->self.root_hub); | ||
123 | #else | ||
124 | usb_lock_device (hcd->self.root_hub); | ||
125 | (void) ohci_hub_suspend (hcd); | ||
126 | usb_unlock_device (hcd->self.root_hub); | ||
127 | #endif | ||
128 | 116 | ||
129 | /* let things settle down a bit */ | 117 | /* FIXME these PMAC things get called in the wrong places. ASIC |
130 | msleep (100); | 118 | * clocks should be turned off AFTER entering D3, and on BEFORE |
131 | 119 | * trying to enter D0. Evidently the PCI layer doesn't currently | |
120 | * provide the right sort of platform hooks for this ... | ||
121 | */ | ||
132 | #ifdef CONFIG_PPC_PMAC | 122 | #ifdef CONFIG_PPC_PMAC |
133 | if (_machine == _MACH_Pmac) { | 123 | if (_machine == _MACH_Pmac) { |
134 | struct device_node *of_node; | 124 | struct device_node *of_node; |
@@ -145,9 +135,6 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) | |||
145 | 135 | ||
146 | static int ohci_pci_resume (struct usb_hcd *hcd) | 136 | static int ohci_pci_resume (struct usb_hcd *hcd) |
147 | { | 137 | { |
148 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
149 | int retval = 0; | ||
150 | |||
151 | #ifdef CONFIG_PPC_PMAC | 138 | #ifdef CONFIG_PPC_PMAC |
152 | if (_machine == _MACH_Pmac) { | 139 | if (_machine == _MACH_Pmac) { |
153 | struct device_node *of_node; | 140 | struct device_node *of_node; |
@@ -159,19 +146,8 @@ static int ohci_pci_resume (struct usb_hcd *hcd) | |||
159 | } | 146 | } |
160 | #endif /* CONFIG_PPC_PMAC */ | 147 | #endif /* CONFIG_PPC_PMAC */ |
161 | 148 | ||
162 | /* resume root hub */ | 149 | usb_hcd_resume_root_hub(hcd); |
163 | if (time_before (jiffies, ohci->next_statechange)) | 150 | return 0; |
164 | msleep (100); | ||
165 | #ifdef CONFIG_USB_SUSPEND | ||
166 | /* get extra cleanup even if remote wakeup isn't in use */ | ||
167 | retval = usb_resume_device (hcd->self.root_hub); | ||
168 | #else | ||
169 | usb_lock_device (hcd->self.root_hub); | ||
170 | retval = ohci_hub_resume (hcd); | ||
171 | usb_unlock_device (hcd->self.root_hub); | ||
172 | #endif | ||
173 | |||
174 | return retval; | ||
175 | } | 151 | } |
176 | 152 | ||
177 | #endif /* CONFIG_PM */ | 153 | #endif /* CONFIG_PM */ |
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 8a9b9d9209e9..caacf14371f5 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h | |||
@@ -389,7 +389,6 @@ struct ohci_hcd { | |||
389 | unsigned long next_statechange; /* suspend/resume */ | 389 | unsigned long next_statechange; /* suspend/resume */ |
390 | u32 fminterval; /* saved register */ | 390 | u32 fminterval; /* saved register */ |
391 | 391 | ||
392 | struct work_struct rh_resume; | ||
393 | struct notifier_block reboot_notifier; | 392 | struct notifier_block reboot_notifier; |
394 | 393 | ||
395 | unsigned long flags; /* for HC bugs */ | 394 | unsigned long flags; /* for HC bugs */ |