diff options
author | Sarah Sharp <sarah.a.sharp@intel.com> | 2008-04-08 17:30:18 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-04-25 00:16:49 -0400 |
commit | 21da84a89312dd8d014ca3352d1ab5c2279ec548 (patch) | |
tree | 148e28526110b173d73903191ed0166c5618c683 /drivers/usb/host | |
parent | 3cf2723432dd27402a4a4941ad2d04eae5dd639c (diff) |
USB: ehci shutdown refactored
This patch refactors some shutdown code so it can be shared between
ehci_stop() and ehci_shutdown().
This also fixes a couple potential bugs:
- ehci_shutdown() was not locking ehci->lock before halting the HC.
- ehci_shutdown() didn't disable the watchdog and IAA timers.
- ehci_stop() was resetting the host controller when it may have been
running, which the EHCI spec says "may result in undefined behavior".
ehci_stop() was calling port_power() to turn off the ports, which waited
20ms after applying the port change. The msleep was for the case where
the HC might take 20ms to turn the ports on; since we're shutting them
off, we can avoid the msleep and just use ehci_turn_off_ports().
ehci_stop() doesn't need to clear the intr_enable register or revert
ownership of the companion controllers to the BIOS, because the host
controller reset should have done that. There might be a buggy host
controller that doesn't follow the reset rules, but for now we assume
it's redundant code and remove it.
[ A subsequent patch will cancel the timers later ... this version
carries forward existing bugs where timers could get re-armed
after they're canceled. ]
Signed-off-by: Sarah Sharp <sarah.a.sharp@intel.com>
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 41 |
1 files changed, 25 insertions, 16 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index a02dcff5eb21..369a8a5ea7bb 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -331,17 +331,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) | |||
331 | &ehci->regs->port_status[port]); | 331 | &ehci->regs->port_status[port]); |
332 | } | 332 | } |
333 | 333 | ||
334 | /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). | 334 | /* |
335 | * This forcibly disables dma and IRQs, helping kexec and other cases | 335 | * Halt HC, turn off all ports, and let the BIOS use the companion controllers. |
336 | * where the next system software may expect clean state. | 336 | * Should be called with ehci->lock held. |
337 | */ | 337 | */ |
338 | static void | 338 | static void ehci_silence_controller(struct ehci_hcd *ehci) |
339 | ehci_shutdown (struct usb_hcd *hcd) | ||
340 | { | 339 | { |
341 | struct ehci_hcd *ehci; | 340 | ehci_halt(ehci); |
342 | |||
343 | ehci = hcd_to_ehci (hcd); | ||
344 | (void) ehci_halt (ehci); | ||
345 | ehci_turn_off_all_ports(ehci); | 341 | ehci_turn_off_all_ports(ehci); |
346 | 342 | ||
347 | /* make BIOS/etc use companion controller during reboot */ | 343 | /* make BIOS/etc use companion controller during reboot */ |
@@ -351,6 +347,22 @@ ehci_shutdown (struct usb_hcd *hcd) | |||
351 | ehci_readl(ehci, &ehci->regs->configured_flag); | 347 | ehci_readl(ehci, &ehci->regs->configured_flag); |
352 | } | 348 | } |
353 | 349 | ||
350 | /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). | ||
351 | * This forcibly disables dma and IRQs, helping kexec and other cases | ||
352 | * where the next system software may expect clean state. | ||
353 | */ | ||
354 | static void ehci_shutdown(struct usb_hcd *hcd) | ||
355 | { | ||
356 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
357 | |||
358 | del_timer_sync(&ehci->watchdog); | ||
359 | del_timer_sync(&ehci->iaa_watchdog); | ||
360 | |||
361 | spin_lock_irq(&ehci->lock); | ||
362 | ehci_silence_controller(ehci); | ||
363 | spin_unlock_irq(&ehci->lock); | ||
364 | } | ||
365 | |||
354 | static void ehci_port_power (struct ehci_hcd *ehci, int is_on) | 366 | static void ehci_port_power (struct ehci_hcd *ehci, int is_on) |
355 | { | 367 | { |
356 | unsigned port; | 368 | unsigned port; |
@@ -401,15 +413,15 @@ static void ehci_work (struct ehci_hcd *ehci) | |||
401 | timer_action (ehci, TIMER_IO_WATCHDOG); | 413 | timer_action (ehci, TIMER_IO_WATCHDOG); |
402 | } | 414 | } |
403 | 415 | ||
416 | /* | ||
417 | * Called when the ehci_hcd module is removed. | ||
418 | */ | ||
404 | static void ehci_stop (struct usb_hcd *hcd) | 419 | static void ehci_stop (struct usb_hcd *hcd) |
405 | { | 420 | { |
406 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | 421 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); |
407 | 422 | ||
408 | ehci_dbg (ehci, "stop\n"); | 423 | ehci_dbg (ehci, "stop\n"); |
409 | 424 | ||
410 | /* Turn off port power on all root hub ports. */ | ||
411 | ehci_port_power (ehci, 0); | ||
412 | |||
413 | /* no more interrupts ... */ | 425 | /* no more interrupts ... */ |
414 | del_timer_sync (&ehci->watchdog); | 426 | del_timer_sync (&ehci->watchdog); |
415 | del_timer_sync(&ehci->iaa_watchdog); | 427 | del_timer_sync(&ehci->iaa_watchdog); |
@@ -418,13 +430,10 @@ static void ehci_stop (struct usb_hcd *hcd) | |||
418 | if (HC_IS_RUNNING (hcd->state)) | 430 | if (HC_IS_RUNNING (hcd->state)) |
419 | ehci_quiesce (ehci); | 431 | ehci_quiesce (ehci); |
420 | 432 | ||
433 | ehci_silence_controller(ehci); | ||
421 | ehci_reset (ehci); | 434 | ehci_reset (ehci); |
422 | ehci_writel(ehci, 0, &ehci->regs->intr_enable); | ||
423 | spin_unlock_irq(&ehci->lock); | 435 | spin_unlock_irq(&ehci->lock); |
424 | 436 | ||
425 | /* let companion controllers work when we aren't */ | ||
426 | ehci_writel(ehci, 0, &ehci->regs->configured_flag); | ||
427 | |||
428 | remove_companion_file(ehci); | 437 | remove_companion_file(ehci); |
429 | remove_debug_files (ehci); | 438 | remove_debug_files (ehci); |
430 | 439 | ||