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 #include #include +#include #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)