Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21da84a8 authored by Sarah Sharp's avatar Sarah Sharp Committed by Greg Kroah-Hartman
Browse files

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: default avatarSarah Sharp <sarah.a.sharp@intel.com>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 3cf27234
Loading
Loading
Loading
Loading
+25 −16
Original line number Diff line number Diff line
@@ -331,17 +331,13 @@ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci)
				&ehci->regs->port_status[port]);
}

/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
 * This forcibly disables dma and IRQs, helping kexec and other cases
 * where the next system software may expect clean state.
/*
 * Halt HC, turn off all ports, and let the BIOS use the companion controllers.
 * Should be called with ehci->lock held.
 */
static void
ehci_shutdown (struct usb_hcd *hcd)
static void ehci_silence_controller(struct ehci_hcd *ehci)
{
	struct ehci_hcd	*ehci;

	ehci = hcd_to_ehci (hcd);
	(void) ehci_halt (ehci);
	ehci_halt(ehci);
	ehci_turn_off_all_ports(ehci);

	/* make BIOS/etc use companion controller during reboot */
@@ -351,6 +347,22 @@ ehci_shutdown (struct usb_hcd *hcd)
	ehci_readl(ehci, &ehci->regs->configured_flag);
}

/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
 * This forcibly disables dma and IRQs, helping kexec and other cases
 * where the next system software may expect clean state.
 */
static void ehci_shutdown(struct usb_hcd *hcd)
{
	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);

	del_timer_sync(&ehci->watchdog);
	del_timer_sync(&ehci->iaa_watchdog);

	spin_lock_irq(&ehci->lock);
	ehci_silence_controller(ehci);
	spin_unlock_irq(&ehci->lock);
}

static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
{
	unsigned port;
@@ -401,15 +413,15 @@ static void ehci_work (struct ehci_hcd *ehci)
		timer_action (ehci, TIMER_IO_WATCHDOG);
}

/*
 * Called when the ehci_hcd module is removed.
 */
static void ehci_stop (struct usb_hcd *hcd)
{
	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);

	ehci_dbg (ehci, "stop\n");

	/* Turn off port power on all root hub ports. */
	ehci_port_power (ehci, 0);

	/* no more interrupts ... */
	del_timer_sync (&ehci->watchdog);
	del_timer_sync(&ehci->iaa_watchdog);
@@ -418,13 +430,10 @@ static void ehci_stop (struct usb_hcd *hcd)
	if (HC_IS_RUNNING (hcd->state))
		ehci_quiesce (ehci);

	ehci_silence_controller(ehci);
	ehci_reset (ehci);
	ehci_writel(ehci, 0, &ehci->regs->intr_enable);
	spin_unlock_irq(&ehci->lock);

	/* let companion controllers work when we aren't */
	ehci_writel(ehci, 0, &ehci->regs->configured_flag);

	remove_companion_file(ehci);
	remove_debug_files (ehci);