Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Sarah Sharp and committed by
Greg Kroah-Hartman
21da84a8 3cf27234

+25 -16
+25 -16
drivers/usb/host/ehci-hcd.c
··· 331 331 &ehci->regs->port_status[port]); 332 332 } 333 333 334 - /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). 335 - * This forcibly disables dma and IRQs, helping kexec and other cases 336 - * where the next system software may expect clean state. 334 + /* 335 + * Halt HC, turn off all ports, and let the BIOS use the companion controllers. 336 + * Should be called with ehci->lock held. 337 337 */ 338 - static void 339 - ehci_shutdown (struct usb_hcd *hcd) 338 + static void ehci_silence_controller(struct ehci_hcd *ehci) 340 339 { 341 - struct ehci_hcd *ehci; 342 - 343 - ehci = hcd_to_ehci (hcd); 344 - (void) ehci_halt (ehci); 340 + ehci_halt(ehci); 345 341 ehci_turn_off_all_ports(ehci); 346 342 347 343 /* make BIOS/etc use companion controller during reboot */ ··· 345 349 346 350 /* unblock posted writes */ 347 351 ehci_readl(ehci, &ehci->regs->configured_flag); 352 + } 353 + 354 + /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). 355 + * This forcibly disables dma and IRQs, helping kexec and other cases 356 + * where the next system software may expect clean state. 357 + */ 358 + static void ehci_shutdown(struct usb_hcd *hcd) 359 + { 360 + struct ehci_hcd *ehci = hcd_to_ehci(hcd); 361 + 362 + del_timer_sync(&ehci->watchdog); 363 + del_timer_sync(&ehci->iaa_watchdog); 364 + 365 + spin_lock_irq(&ehci->lock); 366 + ehci_silence_controller(ehci); 367 + spin_unlock_irq(&ehci->lock); 348 368 } 349 369 350 370 static void ehci_port_power (struct ehci_hcd *ehci, int is_on) ··· 413 401 timer_action (ehci, TIMER_IO_WATCHDOG); 414 402 } 415 403 404 + /* 405 + * Called when the ehci_hcd module is removed. 406 + */ 416 407 static void ehci_stop (struct usb_hcd *hcd) 417 408 { 418 409 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 419 410 420 411 ehci_dbg (ehci, "stop\n"); 421 - 422 - /* Turn off port power on all root hub ports. */ 423 - ehci_port_power (ehci, 0); 424 412 425 413 /* no more interrupts ... */ 426 414 del_timer_sync (&ehci->watchdog); ··· 430 418 if (HC_IS_RUNNING (hcd->state)) 431 419 ehci_quiesce (ehci); 432 420 421 + ehci_silence_controller(ehci); 433 422 ehci_reset (ehci); 434 - ehci_writel(ehci, 0, &ehci->regs->intr_enable); 435 423 spin_unlock_irq(&ehci->lock); 436 - 437 - /* let companion controllers work when we aren't */ 438 - ehci_writel(ehci, 0, &ehci->regs->configured_flag); 439 424 440 425 remove_companion_file(ehci); 441 426 remove_debug_files (ehci);