Skip to content

Commit 1c0e94d

Browse files
pelwellpopcornmix
authored andcommitted
xhci: add quirk for host controllers that don't update endpoint DCS
Seen on a VLI VL805 PCIe to USB controller. For non-stream endpoints at least, if the xHC halts on a particular TRB due to an error then the DCS field in the Out Endpoint Context maintained by the hardware is not updated with the current cycle state. Using the quirk XHCI_EP_CTX_BROKEN_DCS and instead fetch the DCS bit from the TRB that the xHC stopped on. [ bjorn: rebased to v5.14-rc2 ] Link: #3060 Cc: [email protected] Signed-off-by: Jonathan Bell <[email protected]> Signed-off-by: Bjørn Mork <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 22877a1 commit 1c0e94d

File tree

2 files changed

+23
-1
lines changed

2 files changed

+23
-1
lines changed

drivers/usb/host/xhci-pci.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
423423
if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) {
424424
xhci->quirks |= XHCI_LPM_SUPPORT;
425425
xhci->quirks |= XHCI_TRB_OVERFETCH;
426+
xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS;
426427
}
427428

428429
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&

drivers/usb/host/xhci-ring.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -638,8 +638,11 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
638638
struct xhci_ring *ep_ring;
639639
struct xhci_command *cmd;
640640
struct xhci_segment *new_seg;
641+
struct xhci_segment *halted_seg = NULL;
641642
union xhci_trb *new_deq;
642643
int new_cycle;
644+
union xhci_trb *halted_trb;
645+
int index = 0;
643646
dma_addr_t addr;
644647
u64 hw_dequeue;
645648
bool cycle_found = false;
@@ -658,7 +661,25 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
658661
hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
659662
new_seg = ep_ring->deq_seg;
660663
new_deq = ep_ring->dequeue;
661-
new_cycle = hw_dequeue & 0x1;
664+
665+
/*
666+
* Quirk: xHC write-back of the DCS field in the hardware dequeue
667+
* pointer is wrong - use the cycle state of the TRB pointed to by
668+
* the dequeue pointer.
669+
*/
670+
if (xhci->quirks & XHCI_EP_CTX_BROKEN_DCS &&
671+
!(ep->ep_state & EP_HAS_STREAMS))
672+
halted_seg = trb_in_td(xhci, td, hw_dequeue & ~0xf, false);
673+
if (halted_seg) {
674+
index = ((dma_addr_t)(hw_dequeue & ~0xf) - halted_seg->dma) /
675+
sizeof(*halted_trb);
676+
halted_trb = &halted_seg->trbs[index];
677+
new_cycle = halted_trb->generic.field[3] & 0x1;
678+
xhci_dbg(xhci, "Endpoint DCS = %d TRB index = %d cycle = %d\n",
679+
(u8)(hw_dequeue & 0x1), index, new_cycle);
680+
} else {
681+
new_cycle = hw_dequeue & 0x1;
682+
}
662683

663684
/*
664685
* We want to find the pointer, segment and cycle state of the new trb

0 commit comments

Comments
 (0)