Commit 2a62f1e1 authored by David Brownell's avatar David Brownell Committed by Deepak Saxena

[PATCH] USB: EHCI updates (mostly periodic schedule scanning)

[USB] EHCI updates

Update periodic schedule scanning
	- fix shutdown sometimes-hangs (Bernd Porr)
	- resolve the "whole frame at once" FIXME
	- try harder to avoid rescanning
	- don't delegate interrupt qh handling to a buggy helper routine
	- describe more of the relevant iso tuning parameters

One-liners:
	- URB_NO_INTERRUPT hint affects ISO
	- show correct data toggle in sysfs debug files
	- FIXME is resolved by Linus' 20msec delay
parent d9bb5003
...@@ -367,7 +367,7 @@ static void qh_lines ( ...@@ -367,7 +367,7 @@ static void qh_lines (
scratch, cpu_to_le32p (&qh->hw_info2), scratch, cpu_to_le32p (&qh->hw_info2),
cpu_to_le32p (&qh->hw_token), mark, cpu_to_le32p (&qh->hw_token), mark,
(__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token) (__constant_cpu_to_le32 (QTD_TOGGLE) & qh->hw_token)
? "data0" : "data1", ? "data1" : "data0",
(cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f); (cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f);
size -= temp; size -= temp;
next += temp; next += temp;
......
...@@ -113,7 +113,7 @@ ehci_hub_descriptor ( ...@@ -113,7 +113,7 @@ ehci_hub_descriptor (
u16 temp; u16 temp;
desc->bDescriptorType = 0x29; desc->bDescriptorType = 0x29;
desc->bPwrOn2PwrGood = 10; /* FIXME: f(system power) */ desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
desc->bHubContrCurrent = 0; desc->bHubContrCurrent = 0;
desc->bNbrPorts = ports; desc->bNbrPorts = ports;
......
...@@ -490,33 +490,6 @@ static int intr_submit ( ...@@ -490,33 +490,6 @@ static int intr_submit (
return status; return status;
} }
static unsigned
intr_complete (
struct ehci_hcd *ehci,
unsigned frame,
struct ehci_qh *qh,
struct pt_regs *regs
) {
unsigned count;
/* nothing to report? */
if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
!= 0))
return 0;
if (unlikely (list_empty (&qh->qtd_list))) {
dbg ("intr qh %p no TDs?", qh);
return 0;
}
/* handle any completions */
count = qh_completions (ehci, qh, regs);
if (unlikely (list_empty (&qh->qtd_list)))
intr_deschedule (ehci, qh, 0);
return count;
}
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static inline struct ehci_iso_stream * static inline struct ehci_iso_stream *
...@@ -718,7 +691,8 @@ itd_sched_init ( ...@@ -718,7 +691,8 @@ itd_sched_init (
trans = EHCI_ISOC_ACTIVE; trans = EHCI_ISOC_ACTIVE;
trans |= buf & 0x0fff; trans |= buf & 0x0fff;
if (unlikely ((i + 1) == urb->number_of_packets)) if (unlikely (((i + 1) == urb->number_of_packets))
&& !(urb->transfer_flags & URB_NO_INTERRUPT))
trans |= EHCI_ITD_IOC; trans |= EHCI_ITD_IOC;
trans |= length << 16; trans |= length << 16;
uframe->transaction = cpu_to_le32 (trans); uframe->transaction = cpu_to_le32 (trans);
...@@ -809,7 +783,10 @@ itd_urb_transaction ( ...@@ -809,7 +783,10 @@ itd_urb_transaction (
* periodic schedule slots. (Affected by TUNE_FLS, which defaults to * periodic schedule slots. (Affected by TUNE_FLS, which defaults to
* "as small as possible" to be cache-friendlier.) That limits the size * "as small as possible" to be cache-friendlier.) That limits the size
* transfers you can stream reliably; avoid more than 64 msec per urb. * transfers you can stream reliably; avoid more than 64 msec per urb.
* Also avoid queue depths of less than the system's worst irq latency. * Also avoid queue depths of less than ehci's worst irq latency (affected
* by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter,
* and other factors); or more than about 230 msec total (for portability,
* given EHCI_TUNE_FLS and the slop). Or, write a smarter scheduler!
*/ */
#define SCHEDULE_SLOP 10 /* frames */ #define SCHEDULE_SLOP 10 /* frames */
...@@ -1233,7 +1210,7 @@ static void ...@@ -1233,7 +1210,7 @@ static void
scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
{ {
unsigned frame, clock, now_uframe, mod; unsigned frame, clock, now_uframe, mod;
unsigned count = 0; unsigned modified;
mod = ehci->periodic_size << 3; mod = ehci->periodic_size << 3;
...@@ -1244,47 +1221,50 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -1244,47 +1221,50 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
*/ */
now_uframe = ehci->next_uframe; now_uframe = ehci->next_uframe;
if (HCD_IS_RUNNING (ehci->hcd.state)) if (HCD_IS_RUNNING (ehci->hcd.state))
clock = readl (&ehci->regs->frame_index) % mod; clock = readl (&ehci->regs->frame_index);
else else
clock = now_uframe + mod - 1; clock = now_uframe + mod - 1;
clock %= mod;
for (;;) { for (;;) {
union ehci_shadow q, *q_p; union ehci_shadow q, *q_p;
u32 type, *hw_p; u32 type, *hw_p;
unsigned uframes; unsigned uframes;
/* don't scan past the live uframe */
frame = now_uframe >> 3; frame = now_uframe >> 3;
restart: if (frame == (clock >> 3))
/* scan schedule to _before_ current frame index */
if ((frame == (clock >> 3))
&& HCD_IS_RUNNING (ehci->hcd.state))
uframes = now_uframe & 0x07; uframes = now_uframe & 0x07;
else else {
/* safe to scan the whole frame at once */
now_uframe |= 0x07;
uframes = 8; uframes = 8;
}
restart:
/* scan each element in frame's queue for completions */
q_p = &ehci->pshadow [frame]; q_p = &ehci->pshadow [frame];
hw_p = &ehci->periodic [frame]; hw_p = &ehci->periodic [frame];
q.ptr = q_p->ptr; q.ptr = q_p->ptr;
type = Q_NEXT_TYPE (*hw_p); type = Q_NEXT_TYPE (*hw_p);
modified = 0;
/* scan each element in frame's queue for completions */
while (q.ptr != 0) { while (q.ptr != 0) {
int last;
unsigned uf; unsigned uf;
union ehci_shadow temp; union ehci_shadow temp;
switch (type) { switch (type) {
case Q_TYPE_QH: case Q_TYPE_QH:
last = (q.qh->hw_next == EHCI_LIST_END); /* handle any completions */
temp = q.qh->qh_next; temp.qh = qh_get (q.qh);
type = Q_NEXT_TYPE (q.qh->hw_next); type = Q_NEXT_TYPE (q.qh->hw_next);
count += intr_complete (ehci, frame, q = q.qh->qh_next;
qh_get (q.qh), regs); modified = qh_completions (ehci, temp.qh, regs);
qh_put (ehci, q.qh); if (unlikely (list_empty (&temp.qh->qtd_list)))
q = temp; intr_deschedule (ehci, temp.qh, 0);
qh_put (ehci, temp.qh);
break; break;
case Q_TYPE_FSTN: case Q_TYPE_FSTN:
last = (q.fstn->hw_next == EHCI_LIST_END);
/* for "save place" FSTNs, look at QH entries /* for "save place" FSTNs, look at QH entries
* in the previous frame for completions. * in the previous frame for completions.
*/ */
...@@ -1295,8 +1275,6 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -1295,8 +1275,6 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
q = q.fstn->fstn_next; q = q.fstn->fstn_next;
break; break;
case Q_TYPE_ITD: case Q_TYPE_ITD:
last = (q.itd->hw_next == EHCI_LIST_END);
/* skip itds for later in the frame */ /* skip itds for later in the frame */
rmb (); rmb ();
for (uf = uframes; uf < 8; uf++) { for (uf = uframes; uf < 8; uf++) {
...@@ -1317,31 +1295,24 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -1317,31 +1295,24 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
*/ */
*q_p = q.itd->itd_next; *q_p = q.itd->itd_next;
*hw_p = q.itd->hw_next; *hw_p = q.itd->hw_next;
type = Q_NEXT_TYPE (q.itd->hw_next);
wmb(); wmb();
modified = itd_complete (ehci, q.itd, regs);
/* always rescan here; simpler */ q = *q_p;
count += itd_complete (ehci, q.itd, regs); break;
goto restart;
#ifdef have_split_iso #ifdef have_split_iso
case Q_TYPE_SITD: case Q_TYPE_SITD:
last = (q.sitd->hw_next == EHCI_LIST_END); // nyet!
sitd_complete (ehci, q.sitd);
type = Q_NEXT_TYPE (q.sitd->hw_next);
// FIXME unlink SITD after split completes
q = q.sitd->sitd_next;
break;
#endif /* have_split_iso */ #endif /* have_split_iso */
default: default:
dbg ("corrupt type %d frame %d shadow %p", dbg ("corrupt type %d frame %d shadow %p",
type, frame, q.ptr); type, frame, q.ptr);
// BUG (); // BUG ();
last = 1;
q.ptr = 0; q.ptr = 0;
} }
/* did completion remove an interior q entry? */ /* assume completion callbacks modify the queue */
if (unlikely (q.ptr == 0 && !last)) if (unlikely (modified))
goto restart; goto restart;
} }
...@@ -1368,9 +1339,6 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs) ...@@ -1368,9 +1339,6 @@ scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
/* rescan the rest of this frame, then ... */ /* rescan the rest of this frame, then ... */
clock = now; clock = now;
} else { } else {
/* FIXME sometimes we can scan the next frame
* right away, not always inching up on it ...
*/
now_uframe++; now_uframe++;
now_uframe %= mod; now_uframe %= mod;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment