]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
usb: xhci-pci: Limit VIA VL805 DMA addressing to 36 bits
authorXincheng Zhang <zhangxincheng@ultrarisc.com>
Tue, 30 Jun 2026 01:26:48 +0000 (09:26 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 13 Jul 2026 05:10:11 +0000 (07:10 +0200)
The VIA VL805/806 xHCI controller advertises AC64, but fails to handle
DMA addresses at or above 0x1000000000. On systems with large amounts of
RAM, this can cause USB device failures when the controller is given DMA
addresses beyond its usable address width.

Do not use XHCI_NO_64BIT_SUPPORT for this controller. That quirk clears
the cached AC64 capability and limits DMA to 32 bits, causing unnecessary
bouncing for addresses between 4GiB and 64GiB and hiding the controller's
real AC64 capability from code that may need to distinguish register
access width from usable DMA address width.

Track the usable DMA address width separately from the AC64 capability.
Initialize the generic xhci->dma_mask_bits field to 64 and let PCI quirks
reduce it for controllers with narrower DMA support. Set VIA VL805/806 to
36 bits so the DMA API only hands it addresses in the range it can handle
while keeping HCCPARAMS1.AC64 visible.

Cc: stable <stable@kernel.org>
Signed-off-by: Xincheng Zhang <zhangxincheng@ultrarisc.com>
Link: https://patch.msgid.link/20260630-xhci-via-dma-fix-v3-1-690dcb8cf75a@ultrarisc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index 039c26b241d0850e2fb5f5334416f55df1c26efd..6b3fcba44b08d7aa471af829fe1b5918179dde79 100644 (file)
@@ -448,6 +448,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
        if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) {
                xhci->quirks |= XHCI_LPM_SUPPORT;
                xhci->quirks |= XHCI_TRB_OVERFETCH;
+               xhci->dma_mask_bits = 36;
        }
 
        if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
index f44ccee5fa07882d87c0efd40233c39feb543c40..091c82ca8ee295446ec668b178665dcd0e401cf3 100644 (file)
@@ -5466,6 +5466,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
        if (xhci->hci_version > 0x100)
                xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2);
 
+       xhci->dma_mask_bits = 64;
        xhci->max_slots = min(HCS_MAX_SLOTS(hcs_params1), MAX_HC_SLOTS);
        xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS);
        /* xhci-plat or xhci-pci might have set max_interrupters already */
@@ -5515,12 +5516,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
        if (xhci->quirks & XHCI_NO_64BIT_SUPPORT)
                xhci->hcc_params &= ~BIT(0);
 
-       /* Set dma_mask and coherent_dma_mask to 64-bits,
-        * if xHC supports 64-bit addressing */
+       /*
+        * Set dma_mask and coherent_dma_mask to 64-bits if xHC supports
+        * 64-bit addressing, unless a controller-specific quirk callback
+        * limits the usable address width.
+        */
        if ((xhci->hcc_params & HCC_64BIT_ADDR) &&
-                       !dma_set_mask(dev, DMA_BIT_MASK(64))) {
-               xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n");
-               dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+           !dma_set_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits))) {
+               xhci_dbg(xhci, "Enabling %u-bit DMA addresses.\n",
+                        xhci->dma_mask_bits);
+               dma_set_coherent_mask(dev, DMA_BIT_MASK(xhci->dma_mask_bits));
        } else {
                /*
                 * This is to avoid error in cases where a 32-bit USB
index d02046a573e45b14746c76eed3429a4d77ec2f7f..2d3941b5e1e3d717f87744567467a67054305cf0 100644 (file)
@@ -1526,6 +1526,7 @@ struct xhci_hcd {
        /* imod_interval in ns (I * 250ns) */
        u32             imod_interval;
        u32             page_size;
+       unsigned int    dma_mask_bits;
        /* MSI-X/MSI vectors */
        int             nvecs;
        /* optional clocks */