USB: EHCI: use hrtimer for the IAA watchdog
authorAlan Stern <stern@rowland.harvard.edu>
Wed, 11 Jul 2012 15:22:44 +0000 (11:22 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Jul 2012 23:56:46 +0000 (16:56 -0700)
This patch (as1581) replaces the iaa_watchdog kernel timer used by
ehci-hcd with an hrtimer event, in keeping with the general conversion
to high-res timers.

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

index fb1966cf56497c757e5f97e30e9bfd40d5dcd165..f74ba277c22d486401a350087b14020f2d149b58 100644 (file)
@@ -93,7 +93,6 @@ static const char     hcd_name [] = "ehci_hcd";
  */
 #define        EHCI_TUNE_FLS           1       /* (medium) 512-frame schedule */
 
-#define EHCI_IAA_MSECS         10              /* arbitrary */
 #define EHCI_IO_JIFFIES                (HZ/10)         /* io watchdog > irq_thresh */
 #define EHCI_SHRINK_JIFFIES    (DIV_ROUND_UP(HZ, 200) + 1)
                                                /* 5-ms async qh unlink delay */
@@ -322,51 +321,6 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_iaa_watchdog(unsigned long param)
-{
-       struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
-       unsigned long           flags;
-
-       spin_lock_irqsave (&ehci->lock, flags);
-
-       /* Lost IAA irqs wedge things badly; seen first with a vt8235.
-        * So we need this watchdog, but must protect it against both
-        * (a) SMP races against real IAA firing and retriggering, and
-        * (b) clean HC shutdown, when IAA watchdog was pending.
-        */
-       if (ehci->async_unlink
-                       && !timer_pending(&ehci->iaa_watchdog)
-                       && ehci->rh_state == EHCI_RH_RUNNING) {
-               u32 cmd, status;
-
-               /* If we get here, IAA is *REALLY* late.  It's barely
-                * conceivable that the system is so busy that CMD_IAAD
-                * is still legitimately set, so let's be sure it's
-                * clear before we read STS_IAA.  (The HC should clear
-                * CMD_IAAD when it sets STS_IAA.)
-                */
-               cmd = ehci_readl(ehci, &ehci->regs->command);
-
-               /* If IAA is set here it either legitimately triggered
-                * before we cleared IAAD above (but _way_ late, so we'll
-                * still count it as lost) ... or a silicon erratum:
-                * - VIA seems to set IAA without triggering the IRQ;
-                * - IAAD potentially cleared without setting IAA.
-                */
-               status = ehci_readl(ehci, &ehci->regs->status);
-               if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
-                       COUNT (ehci->stats.lost_iaa);
-                       ehci_writel(ehci, STS_IAA, &ehci->regs->status);
-               }
-
-               ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
-                               status, cmd);
-               end_unlink_async(ehci);
-       }
-
-       spin_unlock_irqrestore(&ehci->lock, flags);
-}
-
 static void ehci_watchdog(unsigned long param)
 {
        struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
@@ -418,7 +372,6 @@ 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->rh_state = EHCI_RH_STOPPING;
@@ -491,7 +444,6 @@ static void ehci_stop (struct usb_hcd *hcd)
 
        /* no more interrupts ... */
        del_timer_sync (&ehci->watchdog);
-       del_timer_sync(&ehci->iaa_watchdog);
 
        spin_lock_irq(&ehci->lock);
        ehci->enabled_hrtimer_events = 0;
@@ -547,10 +499,6 @@ static int ehci_init(struct usb_hcd *hcd)
        ehci->watchdog.function = ehci_watchdog;
        ehci->watchdog.data = (unsigned long) ehci;
 
-       init_timer(&ehci->iaa_watchdog);
-       ehci->iaa_watchdog.function = ehci_iaa_watchdog;
-       ehci->iaa_watchdog.data = (unsigned long) ehci;
-
        hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
        ehci->hrtimer.function = ehci_hrtimer_func;
        ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
@@ -830,6 +778,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
 
        /* complete the unlinking of some qh [4.15.2.3] */
        if (status & STS_IAA) {
+
+               /* Turn off the IAA watchdog */
+               ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_IAA_WATCHDOG);
+
+               /*
+                * Mild optimization: Allow another IAAD to reset the
+                * hrtimer, if one occurs before the next expiration.
+                * In theory we could always cancel the hrtimer, but
+                * tests show that about half the time it will be reset
+                * for some other event anyway.
+                */
+               if (ehci->next_hrtimer_event == EHCI_HRTIMER_IAA_WATCHDOG)
+                       ++ehci->next_hrtimer_event;
+
                /* guard against (alleged) silicon errata */
                if (cmd & CMD_IAAD)
                        ehci_dbg(ehci, "IAA with IAAD still set?\n");
index 6ad806bbe468baec9abfc95931243ad0188ec1cd..3097872b1e74fa785ab254de42780e95e007fd29 100644 (file)
@@ -209,7 +209,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
        if (time_before (jiffies, ehci->next_statechange))
                msleep(5);
        del_timer_sync(&ehci->watchdog);
-       del_timer_sync(&ehci->iaa_watchdog);
 
        spin_lock_irq (&ehci->lock);
 
index d68764ef44765a52b1b5dd7a480007658a73beb2..7b35933d91a6e69b48cb8cbf6a108c10c2f6dae8 100644 (file)
@@ -1173,8 +1173,6 @@ static void end_unlink_async (struct ehci_hcd *ehci)
        struct ehci_qh          *qh = ehci->async_unlink;
        struct ehci_qh          *next;
 
-       iaa_watchdog_done(ehci);
-
        // qh->hw_next = cpu_to_hc32(qh->qh_dma);
        qh->qh_state = QH_STATE_IDLE;
        qh->qh_next.qh = NULL;
@@ -1243,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 
        ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command);
        (void)ehci_readl(ehci, &ehci->regs->command);
-       iaa_watchdog_start(ehci);
+       ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
 }
 
 /*-------------------------------------------------------------------------*/
index 8feb60ff4228235d669e1abc1d2ff08413ffd9a2..aab0420642261d96d16317a4fc97328a4d1d2830 100644 (file)
@@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = {
        1 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_POLL_DEAD */
        1125 * NSEC_PER_USEC,   /* EHCI_HRTIMER_UNLINK_INTR */
        2 * NSEC_PER_MSEC,      /* EHCI_HRTIMER_FREE_ITDS */
+       10 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_IAA_WATCHDOG */
        10 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_DISABLE_PERIODIC */
        15 * NSEC_PER_MSEC,     /* EHCI_HRTIMER_DISABLE_ASYNC */
 };
@@ -291,6 +292,49 @@ static void end_free_itds(struct ehci_hcd *ehci)
 }
 
 
+/* Handle lost (or very late) IAA interrupts */
+static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
+{
+       if (ehci->rh_state != EHCI_RH_RUNNING)
+               return;
+
+       /*
+        * Lost IAA irqs wedge things badly; seen first with a vt8235.
+        * So we need this watchdog, but must protect it against both
+        * (a) SMP races against real IAA firing and retriggering, and
+        * (b) clean HC shutdown, when IAA watchdog was pending.
+        */
+       if (ehci->async_unlink) {
+               u32 cmd, status;
+
+               /* If we get here, IAA is *REALLY* late.  It's barely
+                * conceivable that the system is so busy that CMD_IAAD
+                * is still legitimately set, so let's be sure it's
+                * clear before we read STS_IAA.  (The HC should clear
+                * CMD_IAAD when it sets STS_IAA.)
+                */
+               cmd = ehci_readl(ehci, &ehci->regs->command);
+
+               /*
+                * If IAA is set here it either legitimately triggered
+                * after the watchdog timer expired (_way_ late, so we'll
+                * still count it as lost) ... or a silicon erratum:
+                * - VIA seems to set IAA without triggering the IRQ;
+                * - IAAD potentially cleared without setting IAA.
+                */
+               status = ehci_readl(ehci, &ehci->regs->status);
+               if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
+                       COUNT(ehci->stats.lost_iaa);
+                       ehci_writel(ehci, STS_IAA, &ehci->regs->status);
+               }
+
+               ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
+                               status, cmd);
+               end_unlink_async(ehci);
+       }
+}
+
+
 /*
  * Handler functions for the hrtimer event types.
  * Keep this array in the same order as the event types indexed by
@@ -302,6 +346,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
        ehci_handle_controller_death,   /* EHCI_HRTIMER_POLL_DEAD */
        ehci_handle_intr_unlinks,       /* EHCI_HRTIMER_UNLINK_INTR */
        end_free_itds,                  /* EHCI_HRTIMER_FREE_ITDS */
+       ehci_iaa_watchdog,              /* EHCI_HRTIMER_IAA_WATCHDOG */
        ehci_disable_PSE,               /* EHCI_HRTIMER_DISABLE_PERIODIC */
        ehci_disable_ASE,               /* EHCI_HRTIMER_DISABLE_ASYNC */
 };
index 755e30b0000ba35b79bc25e9be200a947205ec2d..d9414aae610f5a6c00433278cf659ef9f5878af0 100644 (file)
@@ -84,6 +84,7 @@ enum ehci_hrtimer_event {
        EHCI_HRTIMER_POLL_DEAD,         /* Wait for dead controller to stop */
        EHCI_HRTIMER_UNLINK_INTR,       /* Wait for interrupt QH unlink */
        EHCI_HRTIMER_FREE_ITDS,         /* Wait for unused iTDs and siTDs */
+       EHCI_HRTIMER_IAA_WATCHDOG,      /* Handle lost IAA interrupts */
        EHCI_HRTIMER_DISABLE_PERIODIC,  /* Wait to disable periodic sched */
        EHCI_HRTIMER_DISABLE_ASYNC,     /* Wait to disable async sched */
        EHCI_HRTIMER_NUM_EVENTS         /* Must come last */
@@ -168,7 +169,6 @@ struct ehci_hcd {                   /* one per controller */
        struct dma_pool         *itd_pool;      /* itd per iso urb */
        struct dma_pool         *sitd_pool;     /* sitd per split iso urb */
 
-       struct timer_list       iaa_watchdog;
        struct timer_list       watchdog;
        unsigned long           actions;
        unsigned                periodic_stamp;
@@ -228,20 +228,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
        return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
 }
 
-
-static inline void
-iaa_watchdog_start(struct ehci_hcd *ehci)
-{
-       WARN_ON(timer_pending(&ehci->iaa_watchdog));
-       mod_timer(&ehci->iaa_watchdog,
-                       jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
-}
-
-static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
-{
-       del_timer(&ehci->iaa_watchdog);
-}
-
 enum ehci_timer_action {
        TIMER_IO_WATCHDOG,
        TIMER_ASYNC_SHRINK,