Commit 1c78f41bd8c8a7d300a8f6427134958f5bac7edc
Committed by
Priyanka Jain
1 parent
01398bb335
Exists in
smarc_8mq_lf_v2020.04
and in
4 other branches
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 |