? ((qh->usecs + qh->c_usecs) / qh->period)
: (qh->usecs * 8);
+ list_add(&qh->intr_node, &ehci->intr_qh_list);
+
/* maybe enable periodic schedule processing */
+ ++ehci->intr_count;
enable_periodic(ehci);
}
/* qh->qh_next still "live" to HC */
qh->qh_state = QH_STATE_UNLINK;
qh->qh_next.ptr = NULL;
+
+ if (ehci->qh_scan_next == qh)
+ ehci->qh_scan_next = list_entry(qh->intr_node.next,
+ struct ehci_qh, intr_node);
+ list_del(&qh->intr_node);
}
static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh)
}
/* maybe turn off periodic schedule */
+ --ehci->intr_count;
disable_periodic(ehci);
}
return status;
}
+static void scan_intr(struct ehci_hcd *ehci)
+{
+ struct ehci_qh *qh;
+
+ list_for_each_entry_safe(qh, ehci->qh_scan_next, &ehci->intr_qh_list,
+ intr_node) {
+ rescan:
+ /* clean any finished work for this qh */
+ if (!list_empty(&qh->qtd_list)) {
+ int temp;
+
+ /*
+ * Unlinks could happen here; completion reporting
+ * drops the lock. That's why ehci->qh_scan_next
+ * always holds the next qh to scan; if the next qh
+ * gets unlinked then ehci->qh_scan_next is adjusted
+ * in qh_unlink_periodic().
+ */
+ temp = qh_completions(ehci, qh);
+ if (unlikely(qh->needs_rescan ||
+ (list_empty(&qh->qtd_list) &&
+ qh->qh_state == QH_STATE_LINKED)))
+ start_unlink_intr(ehci, qh);
+ else if (temp != 0)
+ goto rescan;
+ }
+ }
+}
+
/*-------------------------------------------------------------------------*/
/* ehci_iso_stream ops work with both ITD and SITD */
urb->start_frame = stream->next_uframe;
if (!stream->highspeed)
urb->start_frame >>= 3;
+
+ /* Make sure scan_isoc() sees these */
+ if (ehci->isoc_count == 0)
+ ehci->next_uframe = now;
return 0;
fail:
urb->hcpriv = NULL;
timer_action (ehci, TIMER_IO_WATCHDOG);
+ ++ehci->isoc_count;
enable_periodic(ehci);
}
ehci_urb_done(ehci, urb, 0);
retval = true;
urb = NULL;
+
+ --ehci->isoc_count;
disable_periodic(ehci);
- ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
if (ehci->amd_pll_fix == 1)
usb_amd_quirk_pll_enable();
urb->hcpriv = NULL;
timer_action (ehci, TIMER_IO_WATCHDOG);
+ ++ehci->isoc_count;
enable_periodic(ehci);
}
ehci_urb_done(ehci, urb, 0);
retval = true;
urb = NULL;
+
+ --ehci->isoc_count;
disable_periodic(ehci);
- ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
+ ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
if (ehci->amd_pll_fix == 1)
usb_amd_quirk_pll_enable();
/*-------------------------------------------------------------------------*/
-static void
-scan_periodic (struct ehci_hcd *ehci)
+static void scan_isoc(struct ehci_hcd *ehci)
{
unsigned now_uframe, frame, clock, clock_frame, mod;
unsigned modified;
ehci->clock_frame = clock_frame;
clock &= mod - 1;
clock_frame = clock >> 3;
- ++ehci->periodic_stamp;
for (;;) {
union ehci_shadow q, *q_p;
while (q.ptr != NULL) {
unsigned uf;
- union ehci_shadow temp;
int live;
live = (ehci->rh_state >= EHCI_RH_RUNNING);
switch (hc32_to_cpu(ehci, type)) {
- case Q_TYPE_QH:
- /* handle any completions */
- temp.qh = q.qh;
- type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next);
- q = q.qh->qh_next;
- if (temp.qh->stamp != ehci->periodic_stamp) {
- modified = qh_completions(ehci, temp.qh);
- if (!modified)
- temp.qh->stamp = ehci->periodic_stamp;
- if (unlikely(list_empty(&temp.qh->qtd_list) ||
- temp.qh->needs_rescan))
- start_unlink_intr(ehci, temp.qh);
- }
- break;
- case Q_TYPE_FSTN:
- /* for "save place" FSTNs, look at QH entries
- * in the previous frame for completions.
- */
- if (q.fstn->hw_prev != EHCI_LIST_END(ehci)) {
- ehci_dbg(ehci,
- "ignoring completions from FSTNs\n");
- }
- type = Q_NEXT_TYPE(ehci, q.fstn->hw_next);
- q = q.fstn->fstn_next;
- break;
case Q_TYPE_ITD:
/* If this ITD is still active, leave it for
* later processing ... check the next entry.
ehci_dbg(ehci, "corrupt type %d frame %d shadow %p\n",
type, frame, q.ptr);
// BUG ();
+ /* FALL THROUGH */
+ case Q_TYPE_QH:
+ case Q_TYPE_FSTN:
+ /* End of the iTDs and siTDs */
q.ptr = NULL;
+ break;
}
/* assume completion callbacks modify the queue */
if (unlikely (modified)) {
- if (likely(ehci->periodic_count > 0))
+ if (likely(ehci->isoc_count > 0))
goto restart;
/* short-circuit this scan */
now_uframe = clock;
unsigned now;
if (ehci->rh_state < EHCI_RH_RUNNING
- || ehci->periodic_count == 0)
+ || ehci->isoc_count == 0)
break;
ehci->next_uframe = now_uframe;
now = ehci_read_frame_index(ehci) & (mod - 1);
/* rescan the rest of this frame, then ... */
clock = now;
clock_frame = clock >> 3;
- if (ehci->clock_frame != clock_frame) {
- ehci->clock_frame = clock_frame;
- ++ehci->periodic_stamp;
- }
+ ehci->clock_frame = clock_frame;
} else {
now_uframe++;
now_uframe &= mod - 1;
bool need_rescan:1;
bool intr_unlinking:1;
bool async_unlinking:1;
+ struct ehci_qh *qh_scan_next;
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *async_unlink;
struct ehci_qh *async_unlink_last;
struct ehci_qh *async_iaa;
- struct ehci_qh *qh_scan_next;
unsigned async_unlink_cycle;
unsigned async_count; /* async activity count */
unsigned periodic_size;
__hc32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
+ struct list_head intr_qh_list;
unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */
struct ehci_qh *intr_unlink_last;
unsigned intr_unlink_cycle;
int next_uframe; /* scan periodic, start here */
+ unsigned intr_count; /* intr activity count */
+ unsigned isoc_count; /* isoc activity count */
unsigned periodic_count; /* periodic activity count */
unsigned uframe_periodic_max; /* max periodic time per uframe */
struct timer_list watchdog;
unsigned long actions;
- unsigned periodic_stamp;
unsigned random_frame;
unsigned long next_statechange;
ktime_t last_periodic_enable;
dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
+ struct list_head intr_node; /* list of intr QHs */
struct ehci_qtd *dummy;
struct ehci_qh *unlink_next; /* next on unlink list */
unsigned unlink_cycle;
- unsigned stamp;
u8 needs_rescan; /* Dequeue during giveback */
u8 qh_state;