X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fxhci-ring.c;h=545387a0679a9200e238c387e16fa672ec41becb;hb=a4f1ad46f6623e73b1b18d6b4402dc45d579efc9;hp=76389c0dda8bcd6fc89cb9cddb2ac63eb8d5989b;hpb=e4f2aee6612e56c2a9a5da6131ccd80e57d5075b;p=platform%2Fkernel%2Flinux-rpi.git diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 76389c0..545387a 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -87,15 +87,16 @@ static bool trb_is_link(union xhci_trb *trb) return TRB_TYPE_LINK_LE32(trb->link.control); } -static bool last_trb_on_seg(struct xhci_segment *seg, union xhci_trb *trb) +static bool last_trb_on_seg(struct xhci_segment *seg, + unsigned int trbs_per_seg, union xhci_trb *trb) { - return trb == &seg->trbs[TRBS_PER_SEGMENT - 1]; + return trb == &seg->trbs[trbs_per_seg - 1]; } static bool last_trb_on_ring(struct xhci_ring *ring, struct xhci_segment *seg, union xhci_trb *trb) { - return last_trb_on_seg(seg, trb) && (seg->next == ring->first_seg); + return last_trb_on_seg(seg, ring->trbs_per_seg, trb) && (seg->next == ring->first_seg); } static bool link_trb_toggles_cycle(union xhci_trb *trb) @@ -157,7 +158,8 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) { /* event ring doesn't have link trbs, check for last trb */ if (ring->type == TYPE_EVENT) { - if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) { + if (!last_trb_on_seg(ring->deq_seg, ring->trbs_per_seg, + ring->dequeue)) { ring->dequeue++; goto out; } @@ -265,6 +267,12 @@ static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) { + /* + * If the ring has a single segment the dequeue segment + * never changes, so don't use it as measure of free space. + */ + if (ring->num_segs == 1) + return ring->num_trbs_free >= num_trbs; num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs; if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg) return 0; @@ -666,6 +674,16 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, } while (!cycle_found || !td_last_trb_found); + /* + * Quirk: the xHC does not correctly parse link TRBs if the HW Dequeue + * pointer is set to one. Advance to the next TRB (and next segment). + */ + if (xhci->quirks & XHCI_AVOID_DQ_ON_LINK && trb_is_link(new_deq)) { + if (link_trb_toggles_cycle(new_deq)) + state->new_cycle_state ^= 0x1; + next_trb(xhci, ep_ring, &new_seg, &new_deq); + } + state->new_deq_seg = new_seg; state->new_deq_ptr = new_deq; @@ -2966,7 +2984,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) * that clears the EHB. */ while (xhci_handle_event(xhci) > 0) { - if (event_loop++ < TRBS_PER_SEGMENT / 2) + if (event_loop++ < xhci->event_ring->trbs_per_seg / 2) continue; xhci_update_erst_dequeue(xhci, event_ring_deq); event_loop = 0; @@ -4258,9 +4276,9 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, } ep = &xhci->devs[slot_id]->eps[ep_index]; if ((ep->ep_state & SET_DEQ_PENDING)) { - xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n"); - xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n"); - return; + xhci_warn(xhci, "WARN A Set TR Deq Ptr command is pending for slot %u ep %u\n", + slot_id, ep_index); + ep->ep_state &= ~SET_DEQ_PENDING; } /* This function gets called from contexts where it cannot sleep */