ChangeSet 1.1025, 2003/03/05 14:36:23-08:00, david-b@pacbell.net

[PATCH] ehci, sync with 2.5 latest

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).


 drivers/usb/host/ehci-dbg.c |    6 ++++--
 drivers/usb/host/ehci-hcd.c |   25 +++++++++++++++++++++++--
 drivers/usb/host/ehci-q.c   |   31 +++++++++++++++++++++++--------
 drivers/usb/host/ehci.h     |    3 +++
 4 files changed, 53 insertions(+), 12 deletions(-)


diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c	Thu Mar  6 14:22:35 2003
+++ b/drivers/usb/host/ehci-dbg.c	Thu Mar  6 14:22:35 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;
 
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Thu Mar  6 14:22:35 2003
+++ b/drivers/usb/host/ehci-hcd.c	Thu Mar  6 14:22:35 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
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c	Thu Mar  6 14:22:35 2003
+++ b/drivers/usb/host/ehci-q.c	Thu Mar  6 14:22:35 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);
 }
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h	Thu Mar  6 14:22:35 2003
+++ b/drivers/usb/host/ehci.h	Thu Mar  6 14:22:35 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)
