summaryrefslogtreecommitdiff
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorRan Wang <ran.wang_1@nxp.com>2020-11-18 10:49:02 +0300
committerMarek Vasut <marex@denx.de>2020-12-16 12:27:09 +0300
commit621ed49d3a2ea3c45be1cf774bef48439bd566f3 (patch)
treef9a0dc1a115cbc6cedbde140dc3aa88af356ae2d /drivers/usb/host/xhci-ring.c
parent56f02f0ae85da8bb2dca66c7816dbb1429f92072 (diff)
downloadu-boot-621ed49d3a2ea3c45be1cf774bef48439bd566f3.tar.xz
usb: xhci: fix lack of short packet event trb handling
For bulk IN transfer, the codes will set ISP flag to request event TRB being generated by xHC for the case of short packet. So when encountering buffer-cross-64K-boundary (which we will divide payload and enqueuqe more than 1 transfer TRB), and the first TRB ends up with a short packet condition it will trigger an short packet code transfer event per that flag and cause more than 1 event TRB generated for this transfer. However, current codes will only handle the first transfer event TRB then mark current transfer completed, causing next transfer failure due to event TRB mis-match. Such issue has been observed on some Layerscape platforms (LS1028A, LS1088A, etc) with USB ethernet device. This patch adds a loop to make sure the event TRB for last transfer TRB has been handled in time. Signed-off-by: Ran Wang <ran.wang_1@nxp.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r--drivers/usb/host/xhci-ring.c18
1 files changed, 15 insertions, 3 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 13065d7ca9..d708fc928b 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -580,10 +580,13 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
int ret;
u32 trb_fields[4];
u64 val_64 = virt_to_phys(buffer);
+ void *last_transfer_trb_addr;
+ int available_length;
debug("dev=%p, pipe=%lx, buffer=%p, length=%d\n",
udev, pipe, buffer, length);
+ available_length = length;
ep_index = usb_pipe_ep_index(pipe);
virt_dev = ctrl->devs[slot_id];
@@ -697,7 +700,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
trb_fields[2] = length_field;
trb_fields[3] = field | TRB_TYPE(TRB_NORMAL);
- queue_trb(ctrl, ring, (num_trbs > 1), trb_fields);
+ last_transfer_trb_addr = queue_trb(ctrl, ring, (num_trbs > 1), trb_fields);
--num_trbs;
@@ -710,6 +713,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
giveback_first_trb(udev, ep_index, start_cycle, start_trb);
+again:
event = xhci_wait_for_event(ctrl, TRB_TRANSFER);
if (!event) {
debug("XHCI bulk transfer timed out, aborting...\n");
@@ -718,12 +722,20 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe,
udev->act_len = 0;
return -ETIMEDOUT;
}
- field = le32_to_cpu(event->trans_event.flags);
+ if ((uintptr_t)(le64_to_cpu(event->trans_event.buffer))
+ != (uintptr_t)last_transfer_trb_addr) {
+ available_length -=
+ (int)EVENT_TRB_LEN(le32_to_cpu(event->trans_event.transfer_len));
+ xhci_acknowledge_event(ctrl);
+ goto again;
+ }
+
+ field = le32_to_cpu(event->trans_event.flags);
BUG_ON(TRB_TO_SLOT_ID(field) != slot_id);
BUG_ON(TRB_TO_EP_INDEX(field) != ep_index);
- record_transfer_result(udev, event, length);
+ record_transfer_result(udev, event, available_length);
xhci_acknowledge_event(ctrl);
xhci_inval_cache((uintptr_t)buffer, length);