This patch syncs the 2.4 version with the latest from 2.5 ...
to make it easier for folk to use this before the "host"
directory rename, I decided not to depend on that patch yet.

VIA users will see the most benefit from this, as well as
anyone rebooting with usb-only configurations.

 - uses reboot notifier to make sure the companion
   controller can be used during reboot

 - keeps statistics for lost IAA IRQs (seems to be an
   issue on at least one VT8235)

 - defers using IAA, which makes VT8235 more stable
   (and on 2.4 with usb-storage, 4-5 times faster!)
   and generally reduces IRQs at the cost of some
   extra dma accesses.

 - assumes IAA is a bit flakey, re-initting the async
   queue head (which I've seen become invalid) and not
   resetting the qh "next" pointer after IAA says it's
   safe to do so (likely how it became invalid, by a
   memory access race and/or silicon bug).

Please merge.

- Dave
--- greg-2.4/drivers/usb/hcd/ehci-dbg.c Mon Mar  3 11:11:20 2003
+++ usb-2.4/drivers/usb/host/ehci-dbg.c Mon Mar  3 11:33:58 2003
@@ -615,8 +615,10 @@
        }
 
 #ifdef EHCI_STATS
-       temp = snprintf (next, size, "irq normal %ld err %ld reclaim %ld\n",
-               ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
+       temp = snprintf (next, size,
+               "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
+               ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+               ehci->stats.lost_iaa);
        size -= temp;
        next += temp;
 
--- greg-2.4/drivers/usb/hcd/ehci-hcd.c Mon Mar  3 11:11:20 2003
+++ usb-2.4/drivers/usb/host/ehci-hcd.c Mon Mar  3 11:33:58 2003
@@ -30,6 +30,7 @@
 #include <linux/timer.h>
 #include <linux/list.h>
 #include <linux/interrupt.h>
+#include <linux/reboot.h>
 
 #ifdef CONFIG_USB_DEBUG
        #define DEBUG
@@ -261,6 +262,7 @@
 
                if (status & STS_IAA) {
                        ehci_vdbg (ehci, "lost IAA\n");
+                       COUNT (ehci->stats.lost_iaa);
                        writel (STS_IAA, &ehci->regs->status);
                        ehci->reclaim_ready = 1;
                }
@@ -307,6 +309,19 @@
        return 0;
 }
 
+static int
+ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+{
+       struct ehci_hcd         *ehci;
+
+       ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+
+       /* make BIOS/etc use companion controller during reboot */
+       writel (0, &ehci->regs->configured_flag);
+       return 0;
+}
+
+
 /* called by khubd or root hub init threads */
 
 static int ehci_start (struct usb_hcd *hcd)
@@ -465,6 +480,9 @@
         * are explicitly handed to companion controller(s), so no TT is
         * involved with the root hub.
         */
+       ehci->reboot_notifier.notifier_call = ehci_reboot;
+       register_reboot_notifier (&ehci->reboot_notifier);
+
        ehci->hcd.state = USB_STATE_READY;
        writel (FLAG_CF, &ehci->regs->configured_flag);
        readl (&ehci->regs->command);   /* unblock posted write */
@@ -491,6 +509,7 @@
                        ehci_ready (ehci);
                ehci_reset (ehci);
                bus->root_hub = 0;
+               usb_free_dev (udev); 
                retval = -ENODEV;
                goto done2;
        }
@@ -520,6 +539,7 @@
 
        /* let companion controllers work when we aren't */
        writel (0, &ehci->regs->configured_flag);
+       unregister_reboot_notifier (&ehci->reboot_notifier);
 
        remove_debug_files (ehci);
 
@@ -530,8 +550,9 @@
        ehci_mem_cleanup (ehci);
 
 #ifdef EHCI_STATS
-       ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld\n",
-               ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
+       ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
+               ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
+               ehci->stats.lost_iaa);
        ehci_dbg (ehci, "complete %ld unlink %ld\n",
                ehci->stats.complete, ehci->stats.unlink);
 #endif
--- greg-2.4/drivers/usb/hcd/ehci-q.c   Mon Mar  3 11:11:20 2003
+++ usb-2.4/drivers/usb/host/ehci-q.c   Mon Mar  3 11:33:58 2003
@@ -746,6 +746,11 @@
                if (!(cmd & CMD_ASE)) {
                        /* in case a clear of CMD_ASE didn't take yet */
                        (void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
+
+                       /* force async head to be valid */
+                       writel ((u32)ehci->async->qh_dma,
+                                       &ehci->regs->async_next);
+
                        cmd |= CMD_ASE | CMD_RUN;
                        writel (cmd, &ehci->regs->command);
                        ehci->hcd.state = USB_STATE_RUNNING;
@@ -834,6 +839,7 @@
                                && !usb_pipecontrol (urb->pipe)) {
                        /* "never happens": drivers do stall cleanup right */
                        if (qh->qh_state != QH_STATE_IDLE
+                                       && !list_empty (&qh->qtd_list)
                                        && qh->qh_state != QH_STATE_COMPLETING)
                                ehci_warn (ehci, "clear toggle dev%d "
                                                "ep%d%s: not idle\n",
@@ -949,7 +955,7 @@
 
        del_timer (&ehci->watchdog);
 
-       qh->hw_next = cpu_to_le32 (qh->qh_dma);
+       // qh->hw_next = cpu_to_le32 (qh->qh_dma);
        qh->qh_state = QH_STATE_IDLE;
        qh->qh_next.qh = 0;
        qh_put (ehci, qh);                      // refcount from reclaim 
@@ -1048,6 +1054,7 @@
 scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
        struct ehci_qh          *qh;
+       int                     unlink_delay = 0;
 
        if (!++(ehci->stamp))
                ehci->stamp++;
@@ -1074,17 +1081,25 @@
                                }
                        }
 
-                       /* unlink idle entries, reducing HC PCI usage as
-                        * well as HCD schedule-scanning costs.
-                        *
-                        * FIXME don't unlink idle entries so quickly; it
-                        * can penalize (common) half duplex protocols.
+                       /* unlink idle entries, reducing HC PCI usage as well
+                        * as HCD schedule-scanning costs.  delay for any qh
+                        * we just scanned, there's a not-unusual case that it
+                        * doesn't stay idle for long.
+                        * (plus, avoids some kind of re-activation race.)
                         */
-                       if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
-                               start_unlink_async (ehci, qh);
+                       if (list_empty (&qh->qtd_list)) {
+                               if (qh->stamp == ehci->stamp)
+                                       unlink_delay = 1;
+                               else if (!ehci->reclaim) {
+                                       start_unlink_async (ehci, qh);
+                                       unlink_delay = 0;
+                               }
                        }
 
                        qh = qh->qh_next.qh;
                } while (qh);
        }
+
+       if (unlink_delay && !timer_pending (&ehci->watchdog))
+               mod_timer (&ehci->watchdog, jiffies + EHCI_WATCHDOG_JIFFIES/2);
 }
--- greg-2.4/drivers/usb/hcd/ehci.h     Mon Mar  3 11:11:20 2003
+++ usb-2.4/drivers/usb/host/ehci.h     Mon Mar  3 11:33:58 2003
@@ -27,6 +27,7 @@
        unsigned long           normal;
        unsigned long           error;
        unsigned long           reclaim;
+       unsigned long           lost_iaa;
 
        /* termination of urbs from core */
        unsigned long           complete;
@@ -81,8 +82,10 @@
        struct pci_pool         *sitd_pool;     /* sitd per split iso urb */
 
        struct timer_list       watchdog;
+       struct notifier_block   reboot_notifier;
        unsigned                stamp;
 
+       /* irq statistics */
 #ifdef EHCI_STATS
        struct ehci_stats       stats;
 #      define COUNT(x) do { (x)++; } while (0)

Reply via email to