Skip to content

Commit

Permalink
usb: chipidea: udc: using MultO at TD as real mult value for ISO-TX
Browse files Browse the repository at this point in the history
We have met a bug that the high bandwidth ISO-TX transfer has failed
at the last packet if it is less than 1024, the TD status shows it
is "Transaction Error".

The root cause of this problem is: the mult value at qh is not correct
for current TD's transfer length. We use TD list to queue un-transfer
TDs, and change mult for new adding TDs. If new adding TDs transfer length
less than 1024, but the queued un-transfer TDs transfer length is larger
than 1024, the transfer error will occur, and vice versa.
Usually, this problem occurs at the last packet, and the first packet for
new frame.

We fixed this problem by setting Mult at QH as the largest value (3), and
set MultO (Multiplier Override) at TD according to every transfer length.
It can cover both hardware version less than 2.3 (the real mult is MultO
if it is not 0) and 2.3+ (the real mult is min(qh.mult, td.multo)).

Since the MultO bits are only existed at TX TD, we keep the ISO-RX behavior
unchanged.

For stable tree: 3.11+.

Cc: stable <stable@vger.kernel.org>
Cc: Michael Grzeschik <m.grzeschik@pengutronix.de>
Reported-by: Matthieu Vanin <b47495@freescale.com>
Tested-by: Matthieu Vanin <b47495@freescale.com>
Signed-off-by: Peter Chen <peter.chen@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Peter Chen authored and Greg Kroah-Hartman committed Jan 13, 2014
1 parent 5332ff1 commit 2fc5a7d
Showing 1 changed file with 17 additions and 2 deletions.
19 changes: 17 additions & 2 deletions drivers/usb/chipidea/udc.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,14 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES));
node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES);
node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE);
if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX) {
u32 mul = hwreq->req.length / hwep->ep.maxpacket;

if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
node->ptr->token |= mul << __ffs(TD_MULTO);
}

temp = (u32) (hwreq->req.dma + hwreq->req.actual);
if (length) {
Expand Down Expand Up @@ -515,10 +523,11 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
hwep->qh.ptr->td.token &=
cpu_to_le32(~(TD_STATUS_HALTED|TD_STATUS_ACTIVE));

if (hwep->type == USB_ENDPOINT_XFER_ISOC) {
if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == RX) {
u32 mul = hwreq->req.length / hwep->ep.maxpacket;

if (hwreq->req.length % hwep->ep.maxpacket)
if (hwreq->req.length == 0
|| hwreq->req.length % hwep->ep.maxpacket)
mul++;
hwep->qh.ptr->cap |= mul << __ffs(QH_MULT);
}
Expand Down Expand Up @@ -1173,6 +1182,12 @@ static int ep_enable(struct usb_ep *ep,
if (hwep->num)
cap |= QH_ZLT;
cap |= (hwep->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT;
/*
* For ISO-TX, we set mult at QH as the largest value, and use
* MultO at TD as real mult value.
*/
if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX)
cap |= 3 << __ffs(QH_MULT);

hwep->qh.ptr->cap = cpu_to_le32(cap);

Expand Down

0 comments on commit 2fc5a7d

Please sign in to comment.