USB: EHCI: resolve some unlikely races

This patch (as1589) resolves some unlikely races involving system
shutdown or controller death in ehci-hcd:

	Shutdown races with both root-hub resume and controller
	resume.

	Controller death races with root-hub suspend.

A new bitflag is added to indicate that the controller has been shut
down (whether for system shutdown or because it died).  Tests are
added in the suspend and resume pathways to avoid reactivating the
controller after any sort of shutdown.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index ac4c8dd..e44ca54 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -343,6 +343,7 @@
 	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);
 
 	spin_lock_irq(&ehci->lock);
+	ehci->shutdown = true;
 	ehci->rh_state = EHCI_RH_STOPPING;
 	ehci->enabled_hrtimer_events = 0;
 	spin_unlock_irq(&ehci->lock);
@@ -823,6 +824,7 @@
 		usb_hc_died(hcd);
 
 		/* Don't let the controller do anything more */
+		ehci->shutdown = true;
 		ehci->rh_state = EHCI_RH_STOPPING;
 		ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
 		ehci_writel(ehci, ehci->command, &ehci->regs->command);
@@ -1129,6 +1131,9 @@
 	/* Mark hardware accessible again as we are back to full power by now */
 	set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
 
+	if (ehci->shutdown)
+		return 0;		/* Controller is dead */
+
 	/*
 	 * If CF is still set and we aren't resuming from hibernation
 	 * then we maintained suspend power.
@@ -1139,10 +1144,17 @@
 		int	mask = INTR_MASK;
 
 		ehci_prepare_ports_for_controller_resume(ehci);
+
+		spin_lock_irq(&ehci->lock);
+		if (ehci->shutdown)
+			goto skip;
+
 		if (!hcd->self.root_hub->do_remote_wakeup)
 			mask &= ~STS_PCD;
 		ehci_writel(ehci, mask, &ehci->regs->intr_enable);
 		ehci_readl(ehci, &ehci->regs->intr_enable);
+ skip:
+		spin_unlock_irq(&ehci->lock);
 		return 0;
 	}
 
@@ -1154,14 +1166,20 @@
 	(void) ehci_halt(ehci);
 	(void) ehci_reset(ehci);
 
+	spin_lock_irq(&ehci->lock);
+	if (ehci->shutdown)
+		goto skip;
+
 	ehci_writel(ehci, ehci->command, &ehci->regs->command);
 	ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
 	ehci_readl(ehci, &ehci->regs->command);	/* unblock posted writes */
 
+	ehci->rh_state = EHCI_RH_SUSPENDED;
+	spin_unlock_irq(&ehci->lock);
+
 	/* here we "know" root ports should always stay powered */
 	ehci_port_power(ehci, 1);
 
-	ehci->rh_state = EHCI_RH_SUSPENDED;
 	return 1;
 }