diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index d28b068ef081de9376a2fb49f30f0b807e700350..a770b258fb25aa1e78d269d33bf8a36599741b69 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -111,6 +111,7 @@ #define EHCI_TUNE_MULT_TT 1 #define EHCI_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ +#define EHCI_ASYNC_JIFFIES (HZ/3) /* async idle timeout */ /* Initial IRQ latency: lower than default */ static int log2_irq_thresh = 0; // 0 to 6 @@ -247,9 +248,14 @@ static void ehci_watchdog (unsigned long param) struct ehci_hcd *ehci = (struct ehci_hcd *) param; unsigned long flags; - /* guard against lost IAA, which wedges everything */ spin_lock_irqsave (&ehci->lock, flags); + /* guard against lost IAA, which wedges everything */ ehci_irq (&ehci->hcd); + /* unlink the last qh after it's idled a while */ + if (ehci->async_idle) { + start_unlink_async (ehci, ehci->async); + ehci->async_idle = 0; + } spin_unlock_irqrestore (&ehci->lock, flags); } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index a999df04e218e88cd616ba70cd339c829b807c6a..3a02e28112b94f577603f70b7f81ad9f3f8a1cfd 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -736,6 +736,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } qh->qh_state = QH_STATE_LINKED; /* qtd completions reported later by interrupt */ + + ehci->async_idle = 0; } /*-------------------------------------------------------------------------*/ @@ -1004,16 +1006,18 @@ scan_async (struct ehci_hcd *ehci, unsigned long flags) } /* unlink idle entries, reducing HC PCI usage as - * well as HCD schedule-scanning costs + * well as HCD schedule-scanning costs. removing + * the last qh is deferred, since it's costly. */ if (list_empty (&qh->qtd_list) && !ehci->reclaim) { if (qh->qh_next.qh != qh) { // dbg ("irq/empty"); start_unlink_async (ehci, qh); - } else { - // FIXME: arrange to stop - // after it's been idle a while. - // stop/restart isn't free... + } else if (!timer_pending (&ehci->watchdog)) { + /* can't use IAA for last entry */ + ehci->async_idle = 1; + mod_timer (&ehci->watchdog, + jiffies + EHCI_ASYNC_JIFFIES); } } qh = qh->qh_next.qh; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 30a2035df99633df44c52ef8d97a6aaba8be609f..e7e6f4ba81647dec84de7902412dd7a3f352852c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -39,7 +39,8 @@ struct ehci_hcd { /* one per controller */ /* async schedule support */ struct ehci_qh *async; struct ehci_qh *reclaim; - int reclaim_ready; + int reclaim_ready : 1, + async_idle : 1; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 /* some HCs can do less */