Commit 1c78f41bd8c8a7d300a8f6427134958f5bac7edc

Authored by Ran Wang
Committed by Priyanka Jain
1 parent 01398bb335

usb: xhci: fix event trb handling missed

In functiion xhci_bulk_tx(), when buffer cross 64KB boundary, it will
send request in more than 1 Transfer TRB by chaining them, but then handle
only 1 event TRB to mark request completed.

However, on Layerscape platforms (LS1028A, LS1088A, etc), we observe xhci
controller will generated more than 1 event TRB sometimes, this cause that
function mishandle event TRB in next round call, then system hang due to
BUG() checking.

This patch adds a loop to make sure the event TRB for last Transfer TRB has
to be handled in time.

Signed-off-by: Ran Wang <ran.wang_1@nxp.com>

Showing 1 changed file with 14 additions and 3 deletions Side-by-side Diff

drivers/usb/host/xhci-ring.c
... ... @@ -576,10 +576,13 @@
576 576 int ret;
577 577 u32 trb_fields[4];
578 578 u64 val_64 = (uintptr_t)buffer;
  579 + void *last_transfer_trb_addr;
  580 + int available_length;
579 581  
580 582 debug("dev=%p, pipe=%lx, buffer=%p, length=%d\n",
581 583 udev, pipe, buffer, length);
582 584  
  585 + available_length = length;
583 586 ep_index = usb_pipe_ep_index(pipe);
584 587 virt_dev = ctrl->devs[slot_id];
585 588  
... ... @@ -699,7 +702,7 @@
699 702 trb_fields[2] = length_field;
700 703 trb_fields[3] = field | (TRB_NORMAL << TRB_TYPE_SHIFT);
701 704  
702   - queue_trb(ctrl, ring, (num_trbs > 1), trb_fields);
  705 + last_transfer_trb_addr = queue_trb(ctrl, ring, (num_trbs > 1), trb_fields);
703 706  
704 707 --num_trbs;
705 708  
... ... @@ -712,6 +715,7 @@
712 715  
713 716 giveback_first_trb(udev, ep_index, start_cycle, start_trb);
714 717  
  718 +again:
715 719 event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
716 720 if (!event) {
717 721 debug("XHCI bulk transfer timed out, aborting...\n");
718 722  
719 723  
... ... @@ -720,14 +724,21 @@
720 724 udev->act_len = 0;
721 725 return -ETIMEDOUT;
722 726 }
723   - field = le32_to_cpu(event->trans_event.flags);
724 727  
  728 + if ((void *)event->trans_event.buffer != last_transfer_trb_addr) {
  729 + available_length -=
  730 + (int)EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len));
  731 + xhci_acknowledge_event(ctrl);
  732 + goto again;
  733 + }
  734 +
  735 + field = le32_to_cpu(event->trans_event.flags);
725 736 BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
726 737 BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
727 738 BUG_ON(*(void **)(uintptr_t)le64_to_cpu(event->trans_event.buffer) -
728 739 buffer > (size_t)length);
729 740  
730   - record_transfer_result(udev, event, length);
  741 + record_transfer_result(udev, event, available_length);
731 742 xhci_acknowledge_event(ctrl);
732 743 xhci_inval_cache((uintptr_t)buffer, length);
733 744