summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/ehci-dbg.c4
-rw-r--r--drivers/usb/host/ehci-hcd.c70
-rw-r--r--drivers/usb/host/ehci-hub.c2
-rw-r--r--drivers/usb/host/ehci-pci.c2
-rw-r--r--drivers/usb/host/ehci-q.c6
-rw-r--r--drivers/usb/host/ehci.h22
6 files changed, 37 insertions, 69 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 23b95b2bfe15..34b7a31cd85b 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -754,7 +754,9 @@ show_registers (struct class_device *class_dev, char *buf)
}
if (ehci->reclaim) {
- temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
+ temp = scnprintf (next, size, "reclaim qh %p%s\n",
+ ehci->reclaim,
+ ehci->reclaim_ready ? " ready" : "");
size -= temp;
next += temp;
}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index aac6ec5dd7cf..9030994aba98 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
-#define EHCI_IAA_MSECS 10 /* arbitrary */
+#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
@@ -254,7 +254,6 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
-static void end_unlink_async (struct ehci_hcd *ehci);
static void ehci_work(struct ehci_hcd *ehci);
#include "ehci-hub.c"
@@ -264,37 +263,25 @@ static void ehci_work(struct ehci_hcd *ehci);
/*-------------------------------------------------------------------------*/
-static void ehci_iaa_watchdog (unsigned long param)
+static void ehci_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
- u32 status;
spin_lock_irqsave (&ehci->lock, flags);
- WARN_ON(!ehci->reclaim);
- /* lost IAA irqs wedge things badly; seen first with a vt8235 */
+ /* lost IAA irqs wedge things badly; seen with a vt8235 */
if (ehci->reclaim) {
- status = readl (&ehci->regs->status);
+ u32 status = readl (&ehci->regs->status);
if (status & STS_IAA) {
ehci_vdbg (ehci, "lost IAA\n");
COUNT (ehci->stats.lost_iaa);
writel (STS_IAA, &ehci->regs->status);
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
}
}
- spin_unlock_irqrestore (&ehci->lock, flags);
-}
-
-static void ehci_watchdog (unsigned long param)
-{
- struct ehci_hcd *ehci = (struct ehci_hcd *) param;
- unsigned long flags;
-
- spin_lock_irqsave (&ehci->lock, flags);
-
- /* stop async processing after it's idled a bit */
+ /* stop async processing after it's idled a bit */
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
start_unlink_async (ehci, ehci->async);
@@ -345,6 +332,8 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
static void ehci_work (struct ehci_hcd *ehci)
{
timer_action_done (ehci, TIMER_IO_WATCHDOG);
+ if (ehci->reclaim_ready)
+ end_unlink_async (ehci);
/* another CPU may drop ehci->lock during a schedule scan while
* it reports urb completions. this flag guards against bogus
@@ -379,7 +368,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);
if (HC_IS_RUNNING (hcd->state))
@@ -426,10 +414,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;
-
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -446,6 +430,7 @@ static int ehci_init(struct usb_hcd *hcd)
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
ehci->reclaim = NULL;
+ ehci->reclaim_ready = 0;
ehci->next_uframe = -1;
/*
@@ -619,7 +604,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) {
COUNT (ehci->stats.reclaim);
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
bh = 1;
}
@@ -723,14 +708,10 @@ static int ehci_urb_enqueue (
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- // BUG_ON(qh->qh_state != QH_STATE_LINKED);
-
- /* failfast */
- if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
- end_unlink_async (ehci);
-
- /* defer till later if busy */
- else if (ehci->reclaim) {
+ /* if we need to use IAA and it's busy, defer */
+ if (qh->qh_state == QH_STATE_LINKED
+ && ehci->reclaim
+ && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
struct ehci_qh *last;
for (last = ehci->reclaim;
@@ -740,8 +721,12 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->qh_state = QH_STATE_UNLINK_WAIT;
last->reclaim = qh;
- /* start IAA cycle */
- } else
+ /* bypass IAA if the hc can't care */
+ } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
+ end_unlink_async (ehci);
+
+ /* something else might have unlinked the qh by now */
+ if (qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh);
}
@@ -763,19 +748,7 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
- switch (qh->qh_state) {
- case QH_STATE_LINKED:
- case QH_STATE_COMPLETING:
- unlink_async (ehci, qh);
- break;
- case QH_STATE_UNLINK:
- case QH_STATE_UNLINK_WAIT:
- /* already started */
- break;
- case QH_STATE_IDLE:
- WARN_ON(1);
- break;
- }
+ unlink_async (ehci, qh);
break;
case PIPE_INTERRUPT:
@@ -867,7 +840,6 @@ rescan:
unlink_async (ehci, qh);
/* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */
- case QH_STATE_UNLINK_WAIT:
idle_timeout:
spin_unlock_irqrestore (&ehci->lock, flags);
schedule_timeout_uninterruptible(1);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 2012213c0a25..1b20722c102b 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
ehci_work(ehci);
/* suspend any active/unsuspended ports, maybe allow wakeup */
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 35e3fab6fc4e..e51c1ed81ac4 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -303,7 +303,7 @@ restart:
/* emptying the schedule aborts any urbs */
spin_lock_irq(&ehci->lock);
if (ehci->reclaim)
- end_unlink_async (ehci);
+ ehci->reclaim_ready = 1;
ehci_work(ehci);
spin_unlock_irq(&ehci->lock);
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 46327272f614..62e46dc60e86 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -967,7 +967,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
struct ehci_qh *qh = ehci->reclaim;
struct ehci_qh *next;
- iaa_watchdog_done (ehci);
+ timer_action_done (ehci, TIMER_IAA_WATCHDOG);
// qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
@@ -977,6 +977,7 @@ static void end_unlink_async (struct ehci_hcd *ehci)
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->reclaim;
ehci->reclaim = next;
+ ehci->reclaim_ready = 0;
qh->reclaim = NULL;
qh_completions (ehci, qh);
@@ -1051,10 +1052,11 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
return;
}
+ ehci->reclaim_ready = 0;
cmd |= CMD_IAAD;
writel (cmd, &ehci->regs->command);
(void) readl (&ehci->regs->command);
- iaa_watchdog_start (ehci);
+ timer_action (ehci, TIMER_IAA_WATCHDOG);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 6aac39f50e07..bbc3082a73d7 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -58,6 +58,7 @@ struct ehci_hcd { /* one per controller */
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
+ unsigned reclaim_ready : 1;
unsigned scanning : 1;
/* periodic schedule support */
@@ -80,7 +81,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 stamp;
@@ -114,21 +114,9 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
}
-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_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
@@ -146,6 +134,9 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
unsigned long t;
switch (action) {
+ case TIMER_IAA_WATCHDOG:
+ t = EHCI_IAA_JIFFIES;
+ break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
@@ -162,7 +153,8 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
- if (time_before_eq(t, ehci->watchdog.expires)
+ if (action != TIMER_IAA_WATCHDOG
+ && t > ehci->watchdog.expires
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);