diff options
Diffstat (limited to 'drivers/usb/host/ehci-sched.c')
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 152 |
1 files changed, 84 insertions, 68 deletions
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 80d99bce2b38..8a8e08a51ba3 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -119,7 +119,8 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe) q = &q->fstn->fstn_next; break; case Q_TYPE_ITD: - usecs += q->itd->usecs [uframe]; + if (q->itd->hw_transaction[uframe]) + usecs += q->itd->stream->usecs; hw_p = &q->itd->hw_next; q = &q->itd->itd_next; break; @@ -211,7 +212,7 @@ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) * low/fullspeed transfer can "carry over" from one uframe to the next, * since the TT just performs downstream transfers in sequence. * - * For example two seperate 100 usec transfers can start in the same uframe, + * For example two separate 100 usec transfers can start in the same uframe, * and the second one would "carry over" 75 usecs into the next uframe. */ static void @@ -1536,7 +1537,6 @@ itd_link_urb ( uframe = next_uframe & 0x07; frame = next_uframe >> 3; - itd->usecs [uframe] = stream->usecs; itd_patch(ehci, itd, iso_sched, packet, uframe); next_uframe += stream->interval; @@ -1565,6 +1565,16 @@ itd_link_urb ( #define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR) +/* Process and recycle a completed ITD. Return true iff its urb completed, + * and hence its completion callback probably added things to the hardware + * schedule. + * + * Note that we carefully avoid recycling this descriptor until after any + * completion callback runs, so that it won't be reused quickly. That is, + * assuming (a) no more than two urbs per frame on this endpoint, and also + * (b) only this endpoint's completions submit URBs. It seems some silicon + * corrupts things if you reuse completed descriptors very quickly... + */ static unsigned itd_complete ( struct ehci_hcd *ehci, @@ -1577,6 +1587,7 @@ itd_complete ( int urb_index = -1; struct ehci_iso_stream *stream = itd->stream; struct usb_device *dev; + unsigned retval = false; /* for each uframe with a packet */ for (uframe = 0; uframe < 8; uframe++) { @@ -1610,30 +1621,21 @@ itd_complete ( } } - usb_put_urb (urb); - itd->urb = NULL; - itd->stream = NULL; - list_move (&itd->itd_list, &stream->free_list); - iso_stream_put (ehci, stream); - /* handle completion now? */ if (likely ((urb_index + 1) != urb->number_of_packets)) - return 0; + goto done; /* ASSERT: it's really the last itd for this urb list_for_each_entry (itd, &stream->td_list, itd_list) BUG_ON (itd->urb == urb); */ - /* give urb back to the driver ... can be out-of-order */ + /* give urb back to the driver; completion often (re)submits */ dev = urb->dev; ehci_urb_done(ehci, urb, 0); + retval = true; urb = NULL; - - /* defer stopping schedule; completion can submit */ ehci->periodic_sched--; - if (unlikely (!ehci->periodic_sched)) - (void) disable_periodic (ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (unlikely (list_empty (&stream->td_list))) { @@ -1645,8 +1647,15 @@ itd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); + /* OK to recycle this ITD now that its completion callback ran. */ +done: + usb_put_urb(urb); + itd->urb = NULL; + itd->stream = NULL; + list_move(&itd->itd_list, &stream->free_list); + iso_stream_put(ehci, stream); - return 1; + return retval; } /*-------------------------------------------------------------------------*/ @@ -1712,8 +1721,6 @@ done: return status; } -#ifdef CONFIG_USB_EHCI_SPLIT_ISO - /*-------------------------------------------------------------------------*/ /* @@ -1950,6 +1957,16 @@ sitd_link_urb ( #define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \ | SITD_STS_XACT | SITD_STS_MMF) +/* Process and recycle a completed SITD. Return true iff its urb completed, + * and hence its completion callback probably added things to the hardware + * schedule. + * + * Note that we carefully avoid recycling this descriptor until after any + * completion callback runs, so that it won't be reused quickly. That is, + * assuming (a) no more than two urbs per frame on this endpoint, and also + * (b) only this endpoint's completions submit URBs. It seems some silicon + * corrupts things if you reuse completed descriptors very quickly... + */ static unsigned sitd_complete ( struct ehci_hcd *ehci, @@ -1961,6 +1978,7 @@ sitd_complete ( int urb_index = -1; struct ehci_iso_stream *stream = sitd->stream; struct usb_device *dev; + unsigned retval = false; urb_index = sitd->index; desc = &urb->iso_frame_desc [urb_index]; @@ -1981,32 +1999,23 @@ sitd_complete ( desc->status = 0; desc->actual_length = desc->length - SITD_LENGTH (t); } - - usb_put_urb (urb); - sitd->urb = NULL; - sitd->stream = NULL; - list_move (&sitd->sitd_list, &stream->free_list); stream->depth -= stream->interval << 3; - iso_stream_put (ehci, stream); /* handle completion now? */ if ((urb_index + 1) != urb->number_of_packets) - return 0; + goto done; /* ASSERT: it's really the last sitd for this urb list_for_each_entry (sitd, &stream->td_list, sitd_list) BUG_ON (sitd->urb == urb); */ - /* give urb back to the driver */ + /* give urb back to the driver; completion often (re)submits */ dev = urb->dev; ehci_urb_done(ehci, urb, 0); + retval = true; urb = NULL; - - /* defer stopping schedule; completion can submit */ ehci->periodic_sched--; - if (!ehci->periodic_sched) - (void) disable_periodic (ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; if (list_empty (&stream->td_list)) { @@ -2018,8 +2027,15 @@ sitd_complete ( (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); } iso_stream_put (ehci, stream); + /* OK to recycle this SITD now that its completion callback ran. */ +done: + usb_put_urb(urb); + sitd->urb = NULL; + sitd->stream = NULL; + list_move(&sitd->sitd_list, &stream->free_list); + iso_stream_put(ehci, stream); - return 1; + return retval; } @@ -2082,26 +2098,6 @@ done: return status; } -#else - -static inline int -sitd_submit (struct ehci_hcd *ehci, struct urb *urb, gfp_t mem_flags) -{ - ehci_dbg (ehci, "split iso support is disabled\n"); - return -ENOSYS; -} - -static inline unsigned -sitd_complete ( - struct ehci_hcd *ehci, - struct ehci_sitd *sitd -) { - ehci_err (ehci, "sitd_complete %p?\n", sitd); - return 0; -} - -#endif /* USB_EHCI_SPLIT_ISO */ - /*-------------------------------------------------------------------------*/ static void @@ -2127,17 +2123,9 @@ scan_periodic (struct ehci_hcd *ehci) for (;;) { union ehci_shadow q, *q_p; __hc32 type, *hw_p; - unsigned uframes; + unsigned incomplete = false; - /* don't scan past the live uframe */ frame = now_uframe >> 3; - if (frame == (clock >> 3)) - uframes = now_uframe & 0x07; - else { - /* safe to scan the whole frame at once */ - now_uframe |= 0x07; - uframes = 8; - } restart: /* scan each element in frame's queue for completions */ @@ -2175,12 +2163,15 @@ restart: q = q.fstn->fstn_next; break; case Q_TYPE_ITD: - /* skip itds for later in the frame */ + /* If this ITD is still active, leave it for + * later processing ... check the next entry. + */ rmb (); - for (uf = live ? uframes : 8; uf < 8; uf++) { + for (uf = 0; uf < 8 && live; uf++) { if (0 == (q.itd->hw_transaction [uf] & ITD_ACTIVE(ehci))) continue; + incomplete = true; q_p = &q.itd->itd_next; hw_p = &q.itd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2188,10 +2179,12 @@ restart: q = *q_p; break; } - if (uf != 8) + if (uf < 8 && live) break; - /* this one's ready ... HC won't cache the + /* Take finished ITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. HC won't cache the * pointer for much longer, if at all. */ *q_p = q.itd->itd_next; @@ -2202,8 +2195,12 @@ restart: q = *q_p; break; case Q_TYPE_SITD: + /* If this SITD is still active, leave it for + * later processing ... check the next entry. + */ if ((q.sitd->hw_results & SITD_ACTIVE(ehci)) && live) { + incomplete = true; q_p = &q.sitd->sitd_next; hw_p = &q.sitd->hw_next; type = Q_NEXT_TYPE(ehci, @@ -2211,6 +2208,11 @@ restart: q = *q_p; break; } + + /* Take finished SITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. + */ *q_p = q.sitd->sitd_next; *hw_p = q.sitd->hw_next; type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); @@ -2226,11 +2228,24 @@ restart: } /* assume completion callbacks modify the queue */ - if (unlikely (modified)) - goto restart; + if (unlikely (modified)) { + if (likely(ehci->periodic_sched > 0)) + goto restart; + /* maybe we can short-circuit this scan! */ + disable_periodic(ehci); + now_uframe = clock; + break; + } } - /* stop when we catch up to the HC */ + /* If we can tell we caught up to the hardware, stop now. + * We can't advance our scan without collecting the ISO + * transfers that are still pending in this frame. + */ + if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + ehci->next_uframe = now_uframe; + break; + } // FIXME: this assumes we won't get lapped when // latencies climb; that should be rare, but... @@ -2243,7 +2258,8 @@ restart: if (now_uframe == clock) { unsigned now; - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; now = ehci_readl(ehci, &ehci->regs->frame_index) % mod; |