Revert "Revert "xhci: add quirk for host controllers that don't update endpoint DCS""
authorDom Cobley <popcornmix@gmail.com>
Mon, 31 Jul 2023 12:47:10 +0000 (13:47 +0100)
committerDom Cobley <popcornmix@gmail.com>
Mon, 19 Feb 2024 11:31:30 +0000 (11:31 +0000)
This reverts commit 5bef4b3cb95a5b883dfec8b3ffc0d671323d55bb.

We don't agree with upstream revert so undo it.

drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-ring.c

index d6fc08e..b61e6d7 100644 (file)
@@ -479,8 +479,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                        pdev->device == 0x3432)
                xhci->quirks |= XHCI_BROKEN_STREAMS;
 
-       if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483)
+       if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) {
                xhci->quirks |= XHCI_LPM_SUPPORT;
+               xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS;
+       }
 
        if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
                pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI) {
index c410a98..571f24d 100644 (file)
@@ -626,8 +626,11 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
        struct xhci_ring *ep_ring;
        struct xhci_command *cmd;
        struct xhci_segment *new_seg;
+       struct xhci_segment *halted_seg = NULL;
        union xhci_trb *new_deq;
        int new_cycle;
+       union xhci_trb *halted_trb;
+       int index = 0;
        dma_addr_t addr;
        u64 hw_dequeue;
        bool cycle_found = false;
@@ -665,7 +668,27 @@ static int xhci_move_dequeue_past_td(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;
-       new_cycle = hw_dequeue & 0x1;
+
+       /*
+        * 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 &&
+           !(ep->ep_state & EP_HAS_STREAMS))
+               halted_seg = trb_in_td(xhci, td->start_seg,
+                                      td->first_trb, td->last_trb,
+                                      hw_dequeue & ~0xf, false);
+       if (halted_seg) {
+               index = ((dma_addr_t)(hw_dequeue & ~0xf) - halted_seg->dma) /
+                        sizeof(*halted_trb);
+               halted_trb = &halted_seg->trbs[index];
+               new_cycle = halted_trb->generic.field[3] & 0x1;
+               xhci_dbg(xhci, "Endpoint DCS = %d TRB index = %d cycle = %d\n",
+                        (u8)(hw_dequeue & 0x1), index, new_cycle);
+       } else {
+               new_cycle = hw_dequeue & 0x1;
+       }
 
        /*
         * We want to find the pointer, segment and cycle state of the new trb