|
@@ -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_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
|
|
|
+#define EHCI_IAA_MSECS 10 /* arbitrary */
|
|
|
#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,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
|
|
|
static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
|
|
|
|
|
|
#include "ehci-hub.c"
|
|
@@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
-static void ehci_watchdog (unsigned long param)
|
|
|
+static void ehci_iaa_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 with a vt8235 */
|
|
|
+ /* lost IAA irqs wedge things badly; seen first with a vt8235 */
|
|
|
if (ehci->reclaim) {
|
|
|
- u32 status = readl (&ehci->regs->status);
|
|
|
-
|
|
|
+ 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);
|
|
|
- ehci->reclaim_ready = 1;
|
|
|
+ end_unlink_async (ehci, NULL);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* stop async processing after it's idled a bit */
|
|
|
+ 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 */
|
|
|
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
|
|
|
- start_unlink_async (ehci, ehci->async);
|
|
|
+ start_unlink_async (ehci, ehci->async);
|
|
|
|
|
|
/* ehci could run by timer, without IRQs ... */
|
|
|
ehci_work (ehci, NULL);
|
|
@@ -333,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
|
|
|
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
|
|
|
{
|
|
|
timer_action_done (ehci, TIMER_IO_WATCHDOG);
|
|
|
- if (ehci->reclaim_ready)
|
|
|
- end_unlink_async (ehci, regs);
|
|
|
|
|
|
/* another CPU may drop ehci->lock during a schedule scan while
|
|
|
* it reports urb completions. this flag guards against bogus
|
|
@@ -369,6 +379,7 @@ 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))
|
|
@@ -415,6 +426,10 @@ 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.
|
|
@@ -431,7 +446,6 @@ 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;
|
|
|
|
|
|
/*
|
|
@@ -605,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
|
|
|
/* complete the unlinking of some qh [4.15.2.3] */
|
|
|
if (status & STS_IAA) {
|
|
|
COUNT (ehci->stats.reclaim);
|
|
|
- ehci->reclaim_ready = 1;
|
|
|
+ end_unlink_async (ehci, regs);
|
|
|
bh = 1;
|
|
|
}
|
|
|
|
|
@@ -709,10 +723,14 @@ static int ehci_urb_enqueue (
|
|
|
|
|
|
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|
|
{
|
|
|
- /* 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)) {
|
|
|
+ // BUG_ON(qh->qh_state != QH_STATE_LINKED);
|
|
|
+
|
|
|
+ /* failfast */
|
|
|
+ if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
|
|
|
+ end_unlink_async (ehci, NULL);
|
|
|
+
|
|
|
+ /* defer till later if busy */
|
|
|
+ else if (ehci->reclaim) {
|
|
|
struct ehci_qh *last;
|
|
|
|
|
|
for (last = ehci->reclaim;
|
|
@@ -722,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
|
|
|
qh->qh_state = QH_STATE_UNLINK_WAIT;
|
|
|
last->reclaim = qh;
|
|
|
|
|
|
- /* bypass IAA if the hc can't care */
|
|
|
- } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
|
|
|
- end_unlink_async (ehci, NULL);
|
|
|
-
|
|
|
- /* something else might have unlinked the qh by now */
|
|
|
- if (qh->qh_state == QH_STATE_LINKED)
|
|
|
+ /* start IAA cycle */
|
|
|
+ } else
|
|
|
start_unlink_async (ehci, qh);
|
|
|
}
|
|
|
|
|
@@ -749,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
|
|
|
qh = (struct ehci_qh *) urb->hcpriv;
|
|
|
if (!qh)
|
|
|
break;
|
|
|
- unlink_async (ehci, qh);
|
|
|
+ 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;
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
case PIPE_INTERRUPT:
|
|
@@ -841,6 +867,7 @@ 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);
|