USB: EHCI: add new root-hub state: STOPPING
authorAlan Stern <stern@rowland.harvard.edu>
Wed, 11 Jul 2012 15:21:48 +0000 (11:21 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Jul 2012 23:50:14 +0000 (16:50 -0700)
This patch (as1571) adds a new state for ehci-hcd's root hubs:
EHCI_RH_STOPPING.  This value is used at times when the root hub is
being stopped and we don't know whether or not the hardware has
finished all its DMA yet.

Although the purpose may not be apparent, this distinction will come
in useful later on.  Future patches will avoid actions that depend on
the root hub being operational (like turning on the async or periodic
schedules) when they see the state is EHCI_RH_STOPPING.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci.h

index 76120957d60a3a8cc3d369e3525d94faa29c3122..f0c00de035efffedea7d550376ce2420cc82f26c 100644 (file)
@@ -706,6 +706,8 @@ static const char *rh_state_string(struct ehci_hcd *ehci)
                return "suspended";
        case EHCI_RH_RUNNING:
                return "running";
+       case EHCI_RH_STOPPING:
+               return "stopping";
        }
        return "?";
 }
index 8b75e4279a47ae44dd5dc3e1138214f375a88d49..bc94822f4c5d0093275ef1ce5f27770cd12b8374 100644 (file)
@@ -357,10 +357,8 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
 {
        u32     temp;
 
-#ifdef DEBUG
        if (ehci->rh_state != EHCI_RH_RUNNING)
-               BUG ();
-#endif
+               return;
 
        /* wait for any schedule enables/disables to take effect */
        temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
@@ -494,6 +492,7 @@ static void ehci_shutdown(struct usb_hcd *hcd)
        del_timer_sync(&ehci->iaa_watchdog);
 
        spin_lock_irq(&ehci->lock);
+       ehci->rh_state = EHCI_RH_STOPPING;
        ehci_silence_controller(ehci);
        spin_unlock_irq(&ehci->lock);
 }
@@ -562,8 +561,7 @@ static void ehci_stop (struct usb_hcd *hcd)
        del_timer_sync(&ehci->iaa_watchdog);
 
        spin_lock_irq(&ehci->lock);
-       if (ehci->rh_state == EHCI_RH_RUNNING)
-               ehci_quiesce (ehci);
+       ehci_quiesce(ehci);
 
        ehci_silence_controller(ehci);
        ehci_reset (ehci);
@@ -951,6 +949,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
        /* PCI errors [4.15.2.4] */
        if (unlikely ((status & STS_FATAL) != 0)) {
                ehci_err(ehci, "fatal error\n");
+               ehci->rh_state = EHCI_RH_STOPPING;
                dbg_cmd(ehci, "fatal", cmd);
                dbg_status(ehci, "fatal", status);
                ehci_halt(ehci);
@@ -1026,7 +1025,7 @@ static int ehci_urb_enqueue (
 static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
        /* failfast */
-       if (ehci->rh_state != EHCI_RH_RUNNING && ehci->async_unlink)
+       if (ehci->rh_state < EHCI_RH_RUNNING && ehci->async_unlink)
                end_unlink_async(ehci);
 
        /* If the QH isn't linked then there's nothing we can do
@@ -1148,7 +1147,7 @@ rescan:
                goto idle_timeout;
        }
 
-       if (ehci->rh_state != EHCI_RH_RUNNING)
+       if (ehci->rh_state < EHCI_RH_RUNNING)
                qh->qh_state = QH_STATE_IDLE;
        switch (qh->qh_state) {
        case QH_STATE_LINKED:
index 77d3324b4b28d760159dd244e62745b7ef99977e..fb1b99e74937c93d2cd40e8e1de54f3aeb6a15ad 100644 (file)
@@ -227,8 +227,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
        }
 
        /* stop schedules, clean any completed work */
-       if (ehci->rh_state == EHCI_RH_RUNNING)
-               ehci_quiesce (ehci);
+       ehci_quiesce(ehci);
        ehci_work(ehci);
 
        /* Unlike other USB host controller types, EHCI doesn't have
index 5193612c96ea6564cac1f95de4b97e6b6d4d5259..285d5a0f3f70a117d8576ee7c8f6011ca1091cec 100644 (file)
@@ -433,7 +433,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
                /* stop scanning when we reach qtds the hc is using */
                } else if (likely (!stopped
-                               && ehci->rh_state == EHCI_RH_RUNNING)) {
+                               && ehci->rh_state >= EHCI_RH_RUNNING)) {
                        break;
 
                /* scan the whole queue for unlinks whenever it stops */
@@ -441,7 +441,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
                        stopped = 1;
 
                        /* cancel everything if we halt, suspend, etc */
-                       if (ehci->rh_state != EHCI_RH_RUNNING)
+                       if (ehci->rh_state < EHCI_RH_RUNNING)
                                last_status = -ESHUTDOWN;
 
                        /* this qtd is active; skip it unless a previous qtd
@@ -1241,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
        wmb ();
 
        /* If the controller isn't running, we don't have to wait for it */
-       if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) {
+       if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) {
                /* if (unlikely (qh->unlink_next != 0))
                 *      this will recurse, probably not much
                 */
@@ -1263,7 +1263,7 @@ static void scan_async (struct ehci_hcd *ehci)
        enum ehci_timer_action  action = TIMER_IO_WATCHDOG;
 
        timer_action_done (ehci, TIMER_ASYNC_SHRINK);
-       stopped = (ehci->rh_state != EHCI_RH_RUNNING);
+       stopped = (ehci->rh_state < EHCI_RH_RUNNING);
 
        ehci->qh_scan_next = ehci->async->qh_next.qh;
        while (ehci->qh_scan_next) {
index 027df3de2dc9ff41307a35563e0c701fb2735d00..3429b8a33c58ca9949b0d58e1b5766a26e3fb78c 100644 (file)
@@ -2299,7 +2299,7 @@ scan_periodic (struct ehci_hcd *ehci)
         * Touches as few pages as possible:  cache-friendly.
         */
        now_uframe = ehci->next_uframe;
-       if (ehci->rh_state == EHCI_RH_RUNNING) {
+       if (ehci->rh_state >= EHCI_RH_RUNNING) {
                clock = ehci_read_frame_index(ehci);
                clock_frame = (clock >> 3) & (ehci->periodic_size - 1);
        } else  {
@@ -2334,7 +2334,7 @@ restart:
                        union ehci_shadow       temp;
                        int                     live;
 
-                       live = (ehci->rh_state == EHCI_RH_RUNNING);
+                       live = (ehci->rh_state >= EHCI_RH_RUNNING);
                        switch (hc32_to_cpu(ehci, type)) {
                        case Q_TYPE_QH:
                                /* handle any completions */
@@ -2459,7 +2459,7 @@ restart:
                 * We can't advance our scan without collecting the ISO
                 * transfers that are still pending in this frame.
                 */
-               if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) {
+               if (incomplete && ehci->rh_state >= EHCI_RH_RUNNING) {
                        ehci->next_uframe = now_uframe;
                        break;
                }
@@ -2475,7 +2475,7 @@ restart:
                if (now_uframe == clock) {
                        unsigned        now;
 
-                       if (ehci->rh_state != EHCI_RH_RUNNING
+                       if (ehci->rh_state < EHCI_RH_RUNNING
                                        || ehci->periodic_sched == 0)
                                break;
                        ehci->next_uframe = now_uframe;
index 475f23e10bbf7b0bfb29351a07ee49d736804304..9e8e82ecce58b0394606425796dc7de66414cef5 100644 (file)
@@ -62,10 +62,15 @@ struct ehci_stats {
 
 #define        EHCI_MAX_ROOT_PORTS     15              /* see HCS_N_PORTS */
 
+/*
+ * ehci_rh_state values of EHCI_RH_RUNNING or above mean that the
+ * controller may be doing DMA.  Lower values mean there's no DMA.
+ */
 enum ehci_rh_state {
        EHCI_RH_HALTED,
        EHCI_RH_SUSPENDED,
-       EHCI_RH_RUNNING
+       EHCI_RH_RUNNING,
+       EHCI_RH_STOPPING
 };
 
 struct ehci_hcd {                      /* one per controller */