diff options
author | David Brownell <david-b@pacbell.net> | 2005-11-23 18:45:28 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-11-24 02:04:28 -0500 |
commit | f03c17fc9abe8582d6ad830290b3093fdf1eea61 (patch) | |
tree | b110f763ddf4043ef1b994ea2f284d02f6039921 | |
parent | b4723ae3cc66fd067a8e661b5c05d5bd41be29b5 (diff) |
[PATCH] USB: EHCI updates
This fixes some bugs in EHCI suspend/resume that joined us over the past
few releases (as usbcore, PCI, pmcore, and other components evolved):
- Removes suspend and resume recursion from the EHCI driver, getting
rid of the USB_SUSPEND special casing.
- Updates the wakeup mechanism to work again; there's a newish usbcore
call it needs to use.
- Provide simpler tests for "do we need to restart from scratch", to
address another case where PCI Vaux was lost. (In this case it was
restoring a swsusp snapshot, but there could be others.)
Un-exports a symbol that was temporarily exported.
A notable change from previous version is that this doesn't move
the spinlock init, so there's still a resume/reinit path bug.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | drivers/usb/core/hub.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 7 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 77 |
4 files changed, 46 insertions, 42 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 840727948d84..f78bd124d290 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -1669,7 +1669,6 @@ int usb_suspend_device(struct usb_device *udev) | |||
1669 | return 0; | 1669 | return 0; |
1670 | #endif | 1670 | #endif |
1671 | } | 1671 | } |
1672 | EXPORT_SYMBOL_GPL(usb_suspend_device); | ||
1673 | 1672 | ||
1674 | /* | 1673 | /* |
1675 | * If the USB "suspend" state is in use (rather than "global suspend"), | 1674 | * If the USB "suspend" state is in use (rather than "global suspend"), |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index af3c05eb86fc..f15144e96e14 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -636,9 +636,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) | |||
636 | * stop that signaling. | 636 | * stop that signaling. |
637 | */ | 637 | */ |
638 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); | 638 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); |
639 | mod_timer (&hcd->rh_timer, | ||
640 | ehci->reset_done [i] + 1); | ||
641 | ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); | 639 | ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); |
640 | usb_hcd_resume_root_hub(hcd); | ||
642 | } | 641 | } |
643 | } | 642 | } |
644 | 643 | ||
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 88cb4ada686e..82caf336e9b6 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -94,6 +94,13 @@ static int ehci_bus_resume (struct usb_hcd *hcd) | |||
94 | msleep(5); | 94 | msleep(5); |
95 | spin_lock_irq (&ehci->lock); | 95 | spin_lock_irq (&ehci->lock); |
96 | 96 | ||
97 | /* Ideally and we've got a real resume here, and no port's power | ||
98 | * was lost. (For PCI, that means Vaux was maintained.) But we | ||
99 | * could instead be restoring a swsusp snapshot -- so that BIOS was | ||
100 | * the last user of the controller, not reset/pm hardware keeping | ||
101 | * state we gave to it. | ||
102 | */ | ||
103 | |||
97 | /* re-init operational registers in case we lost power */ | 104 | /* re-init operational registers in case we lost power */ |
98 | if (readl (&ehci->regs->intr_enable) == 0) { | 105 | if (readl (&ehci->regs->intr_enable) == 0) { |
99 | /* at least some APM implementations will try to deliver | 106 | /* at least some APM implementations will try to deliver |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index dfd9bd0b1828..0f2be91e5e0e 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -235,10 +235,11 @@ static void ehci_pci_stop (struct usb_hcd *hcd) | |||
235 | 235 | ||
236 | /* suspend/resume, section 4.3 */ | 236 | /* suspend/resume, section 4.3 */ |
237 | 237 | ||
238 | /* These routines rely on the bus (pci, platform, etc) | 238 | /* These routines rely on the PCI bus glue |
239 | * to handle powerdown and wakeup, and currently also on | 239 | * to handle powerdown and wakeup, and currently also on |
240 | * transceivers that don't need any software attention to set up | 240 | * transceivers that don't need any software attention to set up |
241 | * the right sort of wakeup. | 241 | * the right sort of wakeup. |
242 | * Also they depend on separate root hub suspend/resume. | ||
242 | */ | 243 | */ |
243 | 244 | ||
244 | static int ehci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) | 245 | static int ehci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) |
@@ -246,17 +247,9 @@ static int ehci_pci_suspend (struct usb_hcd *hcd, pm_message_t message) | |||
246 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 247 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
247 | 248 | ||
248 | if (time_before (jiffies, ehci->next_statechange)) | 249 | if (time_before (jiffies, ehci->next_statechange)) |
249 | msleep (100); | 250 | msleep (10); |
250 | |||
251 | #ifdef CONFIG_USB_SUSPEND | ||
252 | (void) usb_suspend_device (hcd->self.root_hub); | ||
253 | #else | ||
254 | usb_lock_device (hcd->self.root_hub); | ||
255 | (void) ehci_bus_suspend (hcd); | ||
256 | usb_unlock_device (hcd->self.root_hub); | ||
257 | #endif | ||
258 | 251 | ||
259 | // save (PCI) FLADJ in case of Vaux power loss | 252 | // could save FLADJ in case of Vaux power loss |
260 | // ... we'd only use it to handle clock skew | 253 | // ... we'd only use it to handle clock skew |
261 | 254 | ||
262 | return 0; | 255 | return 0; |
@@ -269,13 +262,18 @@ static int ehci_pci_resume (struct usb_hcd *hcd) | |||
269 | struct usb_device *root = hcd->self.root_hub; | 262 | struct usb_device *root = hcd->self.root_hub; |
270 | int retval = -EINVAL; | 263 | int retval = -EINVAL; |
271 | 264 | ||
272 | // maybe restore (PCI) FLADJ | 265 | // maybe restore FLADJ |
273 | 266 | ||
274 | if (time_before (jiffies, ehci->next_statechange)) | 267 | if (time_before (jiffies, ehci->next_statechange)) |
275 | msleep (100); | 268 | msleep (100); |
276 | 269 | ||
270 | /* If CF is clear, we lost PCI Vaux power and need to restart. */ | ||
271 | if (readl (&ehci->regs->configured_flag) != cpu_to_le32(FLAG_CF)) | ||
272 | goto restart; | ||
273 | |||
277 | /* If any port is suspended (or owned by the companion), | 274 | /* If any port is suspended (or owned by the companion), |
278 | * we know we can/must resume the HC (and mustn't reset it). | 275 | * we know we can/must resume the HC (and mustn't reset it). |
276 | * We just defer that to the root hub code. | ||
279 | */ | 277 | */ |
280 | for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { | 278 | for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { |
281 | u32 status; | 279 | u32 status; |
@@ -283,42 +281,43 @@ static int ehci_pci_resume (struct usb_hcd *hcd) | |||
283 | status = readl (&ehci->regs->port_status [port]); | 281 | status = readl (&ehci->regs->port_status [port]); |
284 | if (!(status & PORT_POWER)) | 282 | if (!(status & PORT_POWER)) |
285 | continue; | 283 | continue; |
286 | if (status & (PORT_SUSPEND | PORT_OWNER)) { | 284 | if (status & (PORT_SUSPEND | PORT_RESUME | PORT_OWNER)) { |
287 | down (&hcd->self.root_hub->serialize); | 285 | usb_hcd_resume_root_hub(hcd); |
288 | retval = ehci_bus_resume (hcd); | 286 | return 0; |
289 | up (&hcd->self.root_hub->serialize); | ||
290 | break; | ||
291 | } | 287 | } |
288 | } | ||
289 | |||
290 | restart: | ||
291 | ehci_dbg(ehci, "lost power, restarting\n"); | ||
292 | for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) { | ||
293 | port--; | ||
292 | if (!root->children [port]) | 294 | if (!root->children [port]) |
293 | continue; | 295 | continue; |
294 | dbg_port (ehci, __FUNCTION__, port + 1, status); | ||
295 | usb_set_device_state (root->children[port], | 296 | usb_set_device_state (root->children[port], |
296 | USB_STATE_NOTATTACHED); | 297 | USB_STATE_NOTATTACHED); |
297 | } | 298 | } |
298 | 299 | ||
299 | /* Else reset, to cope with power loss or flush-to-storage | 300 | /* Else reset, to cope with power loss or flush-to-storage |
300 | * style "resume" having activated BIOS during reboot. | 301 | * style "resume" having let BIOS kick in during reboot. |
301 | */ | 302 | */ |
302 | if (port == 0) { | 303 | (void) ehci_halt (ehci); |
303 | (void) ehci_halt (ehci); | 304 | (void) ehci_reset (ehci); |
304 | (void) ehci_reset (ehci); | 305 | (void) ehci_pci_reset (hcd); |
305 | (void) ehci_pci_reset (hcd); | 306 | |
306 | 307 | /* emptying the schedule aborts any urbs */ | |
307 | /* emptying the schedule aborts any urbs */ | 308 | spin_lock_irq (&ehci->lock); |
308 | spin_lock_irq (&ehci->lock); | 309 | if (ehci->reclaim) |
309 | if (ehci->reclaim) | 310 | ehci->reclaim_ready = 1; |
310 | ehci->reclaim_ready = 1; | 311 | ehci_work (ehci, NULL); |
311 | ehci_work (ehci, NULL); | 312 | spin_unlock_irq (&ehci->lock); |
312 | spin_unlock_irq (&ehci->lock); | 313 | |
313 | 314 | /* restart; khubd will disconnect devices */ | |
314 | /* restart; khubd will disconnect devices */ | 315 | retval = ehci_run (hcd); |
315 | retval = ehci_run (hcd); | 316 | |
316 | 317 | /* here we "know" root ports should always stay powered; | |
317 | /* here we "know" root ports should always stay powered; | 318 | * but some controllers may lose all power. |
318 | * but some controllers may lose all power. | 319 | */ |
319 | */ | 320 | ehci_port_power (ehci, 1); |
320 | ehci_port_power (ehci, 1); | ||
321 | } | ||
322 | 321 | ||
323 | return retval; | 322 | return retval; |
324 | } | 323 | } |