diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index ac0d7a684fae23..3348011e12e691 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -223,8 +223,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_BROKEN_STREAMS; if (pdev->vendor == PCI_VENDOR_ID_VIA && - pdev->device == 0x3483) + pdev->device == 0x3483) { xhci->quirks |= XHCI_LPM_SUPPORT; + xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS; + } if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && pdev->device == 0x1042) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index f896a00662efe3..fbf438b34cbad7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -520,7 +520,10 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, struct xhci_virt_ep *ep = &dev->eps[ep_index]; struct xhci_ring *ep_ring; struct xhci_segment *new_seg; + struct xhci_segment *halted_seg = NULL; union xhci_trb *new_deq; + union xhci_trb *halted_trb; + int index = 0; dma_addr_t addr; u64 hw_dequeue; bool cycle_found = false; @@ -541,7 +544,27 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id); new_seg = ep_ring->deq_seg; new_deq = ep_ring->dequeue; - state->new_cycle_state = hw_dequeue & 0x1; + + if (!(ep->ep_state & EP_HAS_STREAMS)) + halted_seg = trb_in_td(xhci, cur_td->start_seg, + cur_td->first_trb, cur_td->last_trb, + hw_dequeue & ~0xf, false); + /* + * Quirk: xHC write-back of the DCS field in the hardware dequeue + * pointer is wrong - use the cycle state of the TRB pointed to by + * the dequeue pointer. + */ + if (xhci->quirks & XHCI_EP_CTX_BROKEN_DCS && halted_seg) { + index = ((dma_addr_t)(hw_dequeue & ~0xf) - halted_seg->dma) / + sizeof(*halted_trb); + halted_trb = &halted_seg->trbs[index]; + state->new_cycle_state = halted_trb->generic.field[3] & 0x1; + xhci_dbg(xhci, "Endpoint DCS = %d TRB index = %d cycle = %d\n", + (u8)(hw_dequeue & 0x1), index, + state->new_cycle_state); + } else { + state->new_cycle_state = hw_dequeue & 0x1; + } state->stream_id = stream_id; /* diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 761b341d27b076..3a6c451777e7dc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1865,6 +1865,7 @@ struct xhci_hcd { #define XHCI_ZERO_64B_REGS BIT_ULL(32) #define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34) #define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35) +#define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(36) unsigned int num_active_eps; unsigned int limit_active_eps;