Skip to content

Commit

Permalink
usb: xhci: add a quirk for Superspeed bulk OUT transfers on VL805
Browse files Browse the repository at this point in the history
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 <jonathan@raspberrypi.com>
  • Loading branch information
P33M authored and pelwell committed Feb 17, 2022
1 parent b0272c6 commit a538fd2
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 2 deletions.
1 change: 1 addition & 0 deletions drivers/usb/host/xhci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down
21 changes: 19 additions & 2 deletions drivers/usb/host/xhci-ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

2 comments on commit a538fd2

@TrylobitBroccoli
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can I find a tutorial how to install this patch?

@pelwell
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already installed and enabled in all our kernel branches from rpi-5.10.y onwards.

Please sign in to comment.