From a538fd26f82b101cb6fb963042f3242768e628d4 Mon Sep 17 00:00:00 2001 From: Jonathan Bell Date: Wed, 16 Feb 2022 14:31:02 +0000 Subject: [PATCH] usb: xhci: add a quirk for Superspeed bulk OUT transfers on VL805 The VL805 has a bug in its internal FIFO space accounting that results in bulk OUT babble if a TRB in a large multi-element TD has a data buffer size that is larger than and not a multiple of wMaxPacketSize for the endpoint. If the downstream USB3.0 link is congested, or latency is increased through an intermediate hub, then the VL805 enters a suspected FIFO overflow condition and transmits repeated, garbled data to the endpoint. TDs with TRBs of exact multiples of wMaxPacketSize and smaller than wMaxPacketSize appear to be handled correctly, so split buffers at a 1024-byte length boundary and put the remainder in a separate smaller TRB. Signed-off-by: Jonathan Bell --- drivers/usb/host/xhci-pci.c | 1 + drivers/usb/host/xhci-ring.c | 21 +++++++++++++++++++-- drivers/usb/host/xhci.h | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 7f14f3b9b98ab..ccc6a29a081dc 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -293,6 +293,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; xhci->quirks |= XHCI_AVOID_DQ_ON_LINK; xhci->quirks |= XHCI_VLI_TRB_CACHE_BUG; + xhci->quirks |= XHCI_VLI_SS_BULK_OUT_BUG; } if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 545387a0679a9..29c311263ff98 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3414,14 +3414,15 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, unsigned int num_trbs; unsigned int start_cycle, num_sgs = 0; unsigned int enqd_len, block_len, trb_buff_len, full_len; - int sent_len, ret; - u32 field, length_field, remainder; + int sent_len, ret, vli_quirk = 0; + u32 field, length_field, remainder, maxpacket; u64 addr, send_addr; ring = xhci_urb_to_transfer_ring(xhci, urb); if (!ring) return -EINVAL; + maxpacket = usb_endpoint_maxp(&urb->ep->desc); full_len = urb->transfer_buffer_length; /* If we have scatter/gather list, we use it. */ if (urb->num_sgs) { @@ -3458,6 +3459,17 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, start_cycle = ring->cycle_state; send_addr = addr; + if (xhci->quirks & XHCI_VLI_SS_BULK_OUT_BUG && + !usb_urb_dir_in(urb) && urb->dev->speed >= USB_SPEED_SUPER) { + /* + * VL805 - superspeed bulk OUT traffic can cause + * an internal fifo overflow if the TRB buffer is larger + * than wMaxPacket and the length is not an integer + * multiple of wMaxPacket. + */ + vli_quirk = 1; + } + /* Queue the TRBs, even if they are zero-length */ for (enqd_len = 0; first_trb || enqd_len < full_len; enqd_len += trb_buff_len) { @@ -3470,6 +3482,11 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (enqd_len + trb_buff_len > full_len) trb_buff_len = full_len - enqd_len; + if (vli_quirk && trb_buff_len > maxpacket) { + /* SS bulk wMaxPacket is 1024B */ + remainder = trb_buff_len & (maxpacket - 1); + trb_buff_len -= remainder; + } /* Don't change the cycle bit of the first TRB until later */ if (first_trb) { first_trb = false; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index eae8ada4be46d..f6b68029f6cc4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1888,6 +1888,7 @@ struct xhci_hcd { #define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(41) #define XHCI_AVOID_DQ_ON_LINK BIT_ULL(42) #define XHCI_VLI_TRB_CACHE_BUG BIT_ULL(43) +#define XHCI_VLI_SS_BULK_OUT_BUG BIT_ULL(44) unsigned int num_active_eps; unsigned int limit_active_eps;