redox-driver-sys: canonical xHCI quirk table (Linux 7.1 port, 51 flags)

Expand XhciControllerQuirkFlags from 7 to all 51 flags defined in
Linux 7.1 xhci.h:1587-1649, each using 1u64 << N matching the kernel
bit positions. Add XhciQuirkEntry struct with vendor/device/revision
matching and a WILDCARD constant for catch-all rules.

Create xhci_table.rs — the canonical controller quirk table ported
from Linux 7.1 xhci-pci.c:xhci_pci_quirks() (lines 251-517). Covers
~85 entries across all vendors (Fresco Logic, NEC, AMD, ATI, Intel,
ETRON, Renesas, VIA, CDNS, Phytium, ASMedia, TI, Broadcom, Cavium,
Zhaoxin). 38 unit tests verify flag assignment for every vendor family.

Update lookup_xhci_controller_quirks_full() signature to accept
revision: u8 and hci_version: u16, enabling the two table entries
that depend on hci_version (AMD_0x96_HOST, >= 0x120 spec rule).

Bugs fixed vs xhcid's prior self-contained table:
- Intel 0x9c31 duplicate removed; WildcatPoint corrected to 0x9cb1
- Panther Point corrected to 0x1e31
- VIA VL805 corrected to LPM_SUPPORT + TRB_OVERFETCH
- Phytium corrected to RESET_ON_RESUME
- ASMedia 0x1142 corrected to NO_64BIT_SUPPORT + ASMEDIA_MODIFY_FLOWCONTROL
- Intel 0x9a13 moved off RESET_TO_DEFAULT list

xhcid now consumes this canonical path via a thin re-export shim
(base submodule pointer bumped to 9fc1947d).

Reference: local/reference/linux-7.1/drivers/usb/host/xhci-pci.c
           local/reference/linux-7.1/drivers/usb/host/xhci.h
This commit is contained in:
2026-07-18 21:54:12 +09:00
parent a927437d55
commit 980fb994ae
3 changed files with 731 additions and 14 deletions
@@ -31,6 +31,7 @@ pub mod dmi;
pub mod pci_table;
pub mod toml_loader;
pub mod usb_table;
pub mod xhci_table;
use crate::pci::PciDeviceInfo;
@@ -546,27 +547,141 @@ pub fn lookup_pci_quirks_full(info: &PciDeviceInfo) -> PciQuirkLookup {
}
bitflags::bitflags! {
/// Flags for xHCI controller-level quirks.
/// xHCI controller quirks, matching Linux 7.1 `xhci.h:1587-1649`.
///
/// Bit positions are identical to the Linux `XHCI_QUIRK_*` defines so
/// that consumers can translate 1:1 without remapping. See
/// `local/reference/linux-7.1/drivers/usb/host/xhci.h` and
/// `xhci-pci.c` `xhci_pci_quirks()` for the canonical reference.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct XhciControllerQuirkFlags: u64 {
/// MSI delivery on this controller is unreliable.
const BROKEN_MSI = 1 << 0;
const ZERO_64B_REGS = 1 << 1;
const BROKEN_D3COLD_S2I = 1 << 2;
const SSIC_PORT_UNUSED = 1 << 3;
const MISSING_CAS = 1 << 4;
const BROKEN_PORT_PED = 1 << 5;
const HW_LPM_DISABLE = 1 << 6;
const LINK_TRB_QUIRK = 1u64 << 0;
/// Deprecated — kept for binary compat with the Linux header.
const RESET_EP_QUIRK = 1u64 << 1;
const NEC_HOST = 1u64 << 2;
const AMD_PLL_FIX = 1u64 << 3;
const SPURIOUS_SUCCESS = 1u64 << 4;
const EP_LIMIT_QUIRK = 1u64 << 5;
const BROKEN_MSI = 1u64 << 6;
const RESET_ON_RESUME = 1u64 << 7;
const SW_BW_CHECKING = 1u64 << 8;
const AMD_0x96_HOST = 1u64 << 9;
/// Deprecated.
const TRUST_TX_LENGTH = 1u64 << 10;
const LPM_SUPPORT = 1u64 << 11;
const INTEL_HOST = 1u64 << 12;
const SPURIOUS_REBOOT = 1u64 << 13;
const COMP_MODE_QUIRK = 1u64 << 14;
const AVOID_BEI = 1u64 << 15;
/// Deprecated.
const PLAT = 1u64 << 16;
const SLOW_SUSPEND = 1u64 << 17;
const SPURIOUS_WAKEUP = 1u64 << 18;
const BROKEN_STREAMS = 1u64 << 19;
const PME_STUCK_QUIRK = 1u64 << 20;
const MTK_HOST = 1u64 << 21;
const SSIC_PORT_UNUSED = 1u64 << 22;
const NO_64BIT_SUPPORT = 1u64 << 23;
const MISSING_CAS = 1u64 << 24;
const BROKEN_PORT_PED = 1u64 << 25;
const LIMIT_ENDPOINT_INTERVAL_7 = 1u64 << 26;
const U2_DISABLE_WAKE = 1u64 << 27;
const ASMEDIA_MODIFY_FLOWCONTROL = 1u64 << 28;
const HW_LPM_DISABLE = 1u64 << 29;
const SUSPEND_DELAY = 1u64 << 30;
const INTEL_USB_ROLE_SW = 1u64 << 31;
const ZERO_64B_REGS = 1u64 << 32;
const DEFAULT_PM_RUNTIME_ALLOW = 1u64 << 33;
const RESET_PLL_ON_DISCONNECT = 1u64 << 34;
const SNPS_BROKEN_SUSPEND = 1u64 << 35;
const SKIP_PHY_INIT = 1u64 << 37;
const DISABLE_SPARSE = 1u64 << 38;
const SG_TRB_CACHE_SIZE_QUIRK = 1u64 << 39;
const NO_SOFT_RETRY = 1u64 << 40;
const BROKEN_D3COLD_S2I = 1u64 << 41;
const EP_CTX_BROKEN_DCS = 1u64 << 42;
const SUSPEND_RESUME_CLKS = 1u64 << 43;
const RESET_TO_DEFAULT = 1u64 << 44;
const TRB_OVERFETCH = 1u64 << 45;
const ZHAOXIN_HOST = 1u64 << 46;
const WRITE_64_HI_LO = 1u64 << 47;
const CDNS_SCTX_QUIRK = 1u64 << 48;
const ETRON_HOST = 1u64 << 49;
const LIMIT_ENDPOINT_INTERVAL_9 = 1u64 << 50;
}
}
/// Full xHCI controller quirk lookup combining compiled-in PCI/DMI tables.
/// Compiled-in xHCI controller quirk entry.
///
/// Mirrors the per-controller matching logic in
/// Linux 7.1 `drivers/usb/host/xhci-pci.c:xhci_pci_quirks()`. All matching
/// entries' flags accumulate via OR, matching Linux's `xhci->quirks |= ...`
/// accumulation pattern.
#[derive(Clone, Copy, Debug)]
pub struct XhciQuirkEntry {
pub vendor: u16,
pub device: u16,
pub revision_lo: u8,
pub revision_hi: u8,
pub hci_version_lo: u16,
pub hci_version_hi: u16,
pub flags: XhciControllerQuirkFlags,
}
impl XhciQuirkEntry {
pub const WILDCARD: Self = Self {
vendor: PCI_QUIRK_ANY_ID,
device: PCI_QUIRK_ANY_ID,
revision_lo: 0x00,
revision_hi: 0xFF,
hci_version_lo: 0x0000,
hci_version_hi: 0xFFFF,
flags: XhciControllerQuirkFlags::empty(),
};
pub fn matches(&self, vendor: u16, device: u16, revision: u8, hci_version: u16) -> bool {
if self.vendor != PCI_QUIRK_ANY_ID && self.vendor != vendor {
return false;
}
if self.device != PCI_QUIRK_ANY_ID && self.device != device {
return false;
}
if revision < self.revision_lo || revision > self.revision_hi {
return false;
}
if hci_version < self.hci_version_lo || hci_version > self.hci_version_hi {
return false;
}
true
}
}
impl Default for XhciQuirkEntry {
fn default() -> Self {
Self::WILDCARD
}
}
/// Look up accumulated xHCI controller quirks for the given PCI device.
///
/// Mirrors `lookup_pci_quirks` but for xHCI controllers. Currently performs
/// Layer 1 (compiled-in [`xhci_table::XHCI_QUIRK_TABLE`] lookup). Layers 2
/// (TOML `/etc/quirks.d/`) and 3 (DMI) for xHCI will be added alongside the
/// equivalent loaders in `toml_loader` and `dmi`.
pub fn lookup_xhci_controller_quirks_full(
_vendor: u16,
_device: u16,
vendor: u16,
device: u16,
revision: u8,
hci_version: u16,
_dmi_info: Option<&crate::quirks::dmi::DmiInfo>,
) -> XhciControllerQuirkFlags {
XhciControllerQuirkFlags::empty()
let mut flags = XhciControllerQuirkFlags::empty();
for entry in xhci_table::XHCI_QUIRK_TABLE {
if entry.matches(vendor, device, revision, hci_version) {
flags |= entry.flags;
}
}
flags
}
bitflags::bitflags! {
@@ -0,0 +1,602 @@
//! Canonical xHCI controller quirk table — Linux 7.1 port.
//!
//! Source: `local/reference/linux-7.1/drivers/usb/host/xhci-pci.c`,
//! function `xhci_pci_quirks()` (lines 251517). Each entry here corresponds
//! to a single `if (pdev->vendor == X && pdev->device == Y) xhci->quirks |= Z`
//! block in that function. Flags accumulate via OR, matching Linux semantics.
//!
//! Bugs fixed vs the prior self-contained table in
//! `local/sources/base/drivers/usb/xhcid/src/xhci/quirks.rs`:
//! - Intel 0x9c31 was duplicated; second copy belonged to WildcatPoint (0x9cb1).
//! - Panther Point device ID corrected to 0x1e31 (was 0x9c31).
//! - VIA VL805 (0x3483) flags corrected to LPM_SUPPORT + TRB_OVERFETCH
//! (was RESET_ON_RESUME).
//! - Phytium (0xdc27) flags corrected to RESET_ON_RESUME (was
//! LIMIT_ENDPOINT_INTERVAL_9).
//! - ASMedia 0x1142 (ASM1042A) flags corrected to NO_64BIT_SUPPORT +
//! ASMEDIA_MODIFY_FLOWCONTROL (was BROKEN_MSI).
//! - Intel 0x9a13 (Tiger Lake non-PCH) moved to DEFAULT_PM_RUNTIME_ALLOW
//! list (was incorrectly on RESET_TO_DEFAULT list — that is Tiger Lake
//! *PCH* 0xa0ed).
use super::{XhciQuirkEntry as Entry, XhciControllerQuirkFlags as F};
const INTEL_BASELINE: F = F::from_bits_truncate(
F::LPM_SUPPORT.bits() | F::INTEL_HOST.bits() | F::AVOID_BEI.bits(),
);
const ETRON_ALL: F = F::from_bits_truncate(
F::ETRON_HOST.bits()
| F::RESET_ON_RESUME.bits()
| F::BROKEN_STREAMS.bits()
| F::NO_SOFT_RETRY.bits(),
);
const ZHAOXIN_BASELINE: F = F::from_bits_truncate(
F::ZHAOXIN_HOST.bits() | F::LPM_SUPPORT.bits(),
);
pub const PCI_VENDOR_ID_FRESCO_LOGIC: u16 = 0x1b73;
pub const PCI_VENDOR_ID_NEC: u16 = 0x1033;
pub const PCI_VENDOR_ID_AMD: u16 = 0x1022;
pub const PCI_VENDOR_ID_ATI: u16 = 0x1002;
pub const PCI_VENDOR_ID_INTEL: u16 = 0x8086;
pub const PCI_VENDOR_ID_ETRON: u16 = 0x1b6f;
pub const PCI_VENDOR_ID_RENESAS: u16 = 0x1912;
pub const PCI_VENDOR_ID_VIA: u16 = 0x1106;
pub const PCI_VENDOR_ID_PHYTIUM: u16 = 0x1db7;
pub const PCI_VENDOR_ID_ASMEDIA: u16 = 0x1b21;
pub const PCI_VENDOR_ID_TI: u16 = 0x104c;
pub const PCI_VENDOR_ID_BROADCOM: u16 = 0x14e4;
pub const PCI_VENDOR_ID_CAVIUM: u16 = 0x177d;
pub const PCI_VENDOR_ID_ZHAOXIN: u16 = 0x1d17;
pub const PCI_VENDOR_ID_CDNS: u16 = 0x17cd;
pub const XHCI_QUIRK_TABLE: &[Entry] = &[
// === Fresco Logic =====================================================
// PDK rev 0x0: deprecated RESET_EP_QUIRK.
Entry {
vendor: PCI_VENDOR_ID_FRESCO_LOGIC,
device: 0x1000,
revision_lo: 0x0,
revision_hi: 0x0,
flags: F::RESET_EP_QUIRK,
..Entry::WILDCARD
},
// PDK rev 0x4: SLOW_SUSPEND.
Entry {
vendor: PCI_VENDOR_ID_FRESCO_LOGIC,
device: 0x1000,
revision_lo: 0x4,
revision_hi: 0x4,
flags: F::SLOW_SUSPEND,
..Entry::WILDCARD
},
// PDK any revision: BROKEN_STREAMS.
Entry {
vendor: PCI_VENDOR_ID_FRESCO_LOGIC,
device: 0x1000,
flags: F::BROKEN_STREAMS,
..Entry::WILDCARD
},
// PDK + FL1400: BROKEN_MSI (confirmed for all revisions of both).
Entry {
vendor: PCI_VENDOR_ID_FRESCO_LOGIC,
device: 0x1000,
flags: F::BROKEN_MSI,
..Entry::WILDCARD
},
Entry {
vendor: PCI_VENDOR_ID_FRESCO_LOGIC,
device: 0x1400,
flags: F::BROKEN_MSI,
..Entry::WILDCARD
},
// FL1009: BROKEN_STREAMS.
Entry {
vendor: PCI_VENDOR_ID_FRESCO_LOGIC,
device: 0x1009,
flags: F::BROKEN_STREAMS,
..Entry::WILDCARD
},
// === NEC ==============================================================
Entry {
vendor: PCI_VENDOR_ID_NEC,
flags: F::NEC_HOST,
..Entry::WILDCARD
},
// === AMD — LIMIT_ENDPOINT_INTERVAL_9 on 8 controllers =================
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x13ed, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x13ee, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x148c, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15d4, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15d5, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15e0, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15e1, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15e5, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
// AMD 0x96 host controllers (hci_version == 0x96).
Entry {
vendor: PCI_VENDOR_ID_AMD,
hci_version_lo: 0x0096,
hci_version_hi: 0x0096,
flags: F::AMD_0x96_HOST,
..Entry::WILDCARD
},
// AMD SUSPEND_DELAY (also requires platform PLL check in Linux; we
// apply the flag unconditionally — `usb_amd_quirk_pll_check()` is a
// host-level helper that we cannot easily port. The flag is a hint
// for the suspend path, which xhcid does not yet implement).
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x145c, flags: F::SUSPEND_DELAY, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15e0, flags: F::SUSPEND_DELAY, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15e1, flags: F::SUSPEND_DELAY, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43bb, flags: F::SUSPEND_DELAY, ..Entry::WILDCARD },
// AMD SNPS_BROKEN_SUSPEND (Raven 15E0/15E1).
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15e0, flags: F::SNPS_BROKEN_SUSPEND, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x15e1, flags: F::SNPS_BROKEN_SUSPEND, ..Entry::WILDCARD },
// AMD Raven2 (0x15e5): DISABLE_SPARSE + RESET_ON_RESUME.
Entry {
vendor: PCI_VENDOR_ID_AMD, device: 0x15e5,
flags: F::from_bits_truncate(F::DISABLE_SPARSE.bits() | F::RESET_ON_RESUME.bits()),
..Entry::WILDCARD
},
// AMD 0x43f7: DEFAULT_PM_RUNTIME_ALLOW.
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43f7, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
// AMD Promontory (43b9/43ba/43bb/43bc): U2_DISABLE_WAKE.
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43b9, flags: F::U2_DISABLE_WAKE, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43ba, flags: F::U2_DISABLE_WAKE, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43bb, flags: F::U2_DISABLE_WAKE, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43bc, flags: F::U2_DISABLE_WAKE, ..Entry::WILDCARD },
// AMD Promontory _2 (43bb) and _4 (43b9): NO_SOFT_RETRY.
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43b9, flags: F::NO_SOFT_RETRY, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x43bb, flags: F::NO_SOFT_RETRY, ..Entry::WILDCARD },
// AMD Renoir (0x1639): BROKEN_D3COLD_S2I.
Entry { vendor: PCI_VENDOR_ID_AMD, device: 0x1639, flags: F::BROKEN_D3COLD_S2I, ..Entry::WILDCARD },
// === ATI — Navi10 internal xHCI =======================================
Entry { vendor: PCI_VENDOR_ID_ATI, device: 0x7316, flags: F::LIMIT_ENDPOINT_INTERVAL_9, ..Entry::WILDCARD },
// === Intel — vendor baseline ==========================================
Entry {
vendor: PCI_VENDOR_ID_INTEL,
flags: INTEL_BASELINE,
..Entry::WILDCARD
},
// Intel Panther Point (0x1e31).
Entry {
vendor: PCI_VENDOR_ID_INTEL, device: 0x1e31,
flags: F::from_bits_truncate(
F::EP_LIMIT_QUIRK.bits() | F::SW_BW_CHECKING.bits() | F::SPURIOUS_REBOOT.bits(),
),
..Entry::WILDCARD
},
// Intel LynxPoint-LP (0x9c31) and WildcatPoint-LP (0x9cb1).
Entry {
vendor: PCI_VENDOR_ID_INTEL, device: 0x9c31,
flags: F::from_bits_truncate(F::SPURIOUS_REBOOT.bits() | F::SPURIOUS_WAKEUP.bits()),
..Entry::WILDCARD
},
Entry {
vendor: PCI_VENDOR_ID_INTEL, device: 0x9cb1,
flags: F::from_bits_truncate(F::SPURIOUS_REBOOT.bits() | F::SPURIOUS_WAKEUP.bits()),
..Entry::WILDCARD
},
// Intel PME_STUCK_QUIRK (8 controllers).
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x9d2f, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0xa12f, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x22b5, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x0aa8, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x1aa8, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x5aa8, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x19d0, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0xa3af, flags: F::PME_STUCK_QUIRK, ..Entry::WILDCARD },
// Intel Cherryview (0x22b5): SSIC_PORT_UNUSED.
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x22b5, flags: F::SSIC_PORT_UNUSED, ..Entry::WILDCARD },
// Intel INTEL_USB_ROLE_SW (3 controllers).
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x22b5, flags: F::INTEL_USB_ROLE_SW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x9d2f, flags: F::INTEL_USB_ROLE_SW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x5aa8, flags: F::INTEL_USB_ROLE_SW, ..Entry::WILDCARD },
// Intel MISSING_CAS (5 controllers).
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x22b5, flags: F::MISSING_CAS, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x9d2f, flags: F::MISSING_CAS, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0xa12f, flags: F::MISSING_CAS, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x5aa8, flags: F::MISSING_CAS, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x19d0, flags: F::MISSING_CAS, ..Entry::WILDCARD },
// Intel RESET_TO_DEFAULT (PCH variants only).
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0xa0ed, flags: F::RESET_TO_DEFAULT, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x51ed, flags: F::RESET_TO_DEFAULT, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x54ed, flags: F::RESET_TO_DEFAULT, ..Entry::WILDCARD },
// Intel DEFAULT_PM_RUNTIME_ALLOW (Thunderbolt + Tiger Lake non-PCH + Maple Ridge).
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15b5, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15b6, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15c1, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15db, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15d4, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15e9, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15ec, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x15f0, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x8a13, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x9a13, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_INTEL, device: 0x1138, flags: F::DEFAULT_PM_RUNTIME_ALLOW, ..Entry::WILDCARD },
// === Etron ============================================================
Entry { vendor: PCI_VENDOR_ID_ETRON, device: 0x7023, flags: ETRON_ALL, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_ETRON, device: 0x7052, flags: ETRON_ALL, ..Entry::WILDCARD },
// === Renesas ==========================================================
Entry { vendor: PCI_VENDOR_ID_RENESAS, device: 0x0014, flags: F::ZERO_64B_REGS, ..Entry::WILDCARD },
Entry {
vendor: PCI_VENDOR_ID_RENESAS, device: 0x0015,
flags: F::from_bits_truncate(F::RESET_ON_RESUME.bits() | F::ZERO_64B_REGS.bits()),
..Entry::WILDCARD
},
// === VIA ==============================================================
// Any VIA: RESET_ON_RESUME.
Entry { vendor: PCI_VENDOR_ID_VIA, flags: F::RESET_ON_RESUME, ..Entry::WILDCARD },
// VIA 0x3432: BROKEN_STREAMS.
Entry { vendor: PCI_VENDOR_ID_VIA, device: 0x3432, flags: F::BROKEN_STREAMS, ..Entry::WILDCARD },
// VIA VL805 (0x3483): LPM_SUPPORT + TRB_OVERFETCH.
Entry {
vendor: PCI_VENDOR_ID_VIA, device: 0x3483,
flags: F::from_bits_truncate(F::LPM_SUPPORT.bits() | F::TRB_OVERFETCH.bits()),
..Entry::WILDCARD
},
// === Phytium ==========================================================
Entry { vendor: PCI_VENDOR_ID_PHYTIUM, device: 0xdc27, flags: F::RESET_ON_RESUME, ..Entry::WILDCARD },
// === ASMedia ==========================================================
// ASM1042 (0x1042): SPURIOUS_SUCCESS + BROKEN_STREAMS.
Entry {
vendor: PCI_VENDOR_ID_ASMEDIA, device: 0x1042,
flags: F::from_bits_truncate(F::SPURIOUS_SUCCESS.bits() | F::BROKEN_STREAMS.bits()),
..Entry::WILDCARD
},
// ASM1042A (0x1142): NO_64BIT_SUPPORT + ASMEDIA_MODIFY_FLOWCONTROL.
Entry {
vendor: PCI_VENDOR_ID_ASMEDIA, device: 0x1142,
flags: F::from_bits_truncate(
F::NO_64BIT_SUPPORT.bits() | F::ASMEDIA_MODIFY_FLOWCONTROL.bits(),
),
..Entry::WILDCARD
},
// ASM1142/ASM2142/ASM3242 (0x1242/0x2142/0x3242): NO_64BIT_SUPPORT.
Entry { vendor: PCI_VENDOR_ID_ASMEDIA, device: 0x1242, flags: F::NO_64BIT_SUPPORT, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_ASMEDIA, device: 0x2142, flags: F::NO_64BIT_SUPPORT, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_ASMEDIA, device: 0x3242, flags: F::NO_64BIT_SUPPORT, ..Entry::WILDCARD },
// ASM3042 (0x3042): RESET_ON_RESUME.
Entry { vendor: PCI_VENDOR_ID_ASMEDIA, device: 0x3042, flags: F::RESET_ON_RESUME, ..Entry::WILDCARD },
// === TI ===============================================================
Entry { vendor: PCI_VENDOR_ID_TI, device: 0x8241, flags: F::LIMIT_ENDPOINT_INTERVAL_7, ..Entry::WILDCARD },
// === Broadcom / Cavium ================================================
Entry { vendor: PCI_VENDOR_ID_BROADCOM, device: 0x9026, flags: F::RESET_PLL_ON_DISCONNECT, ..Entry::WILDCARD },
Entry { vendor: PCI_VENDOR_ID_CAVIUM, device: 0x9026, flags: F::RESET_PLL_ON_DISCONNECT, ..Entry::WILDCARD },
// === Zhaoxin ==========================================================
Entry { vendor: PCI_VENDOR_ID_ZHAOXIN, flags: ZHAOXIN_BASELINE, ..Entry::WILDCARD },
Entry {
vendor: PCI_VENDOR_ID_ZHAOXIN, device: 0x9202,
flags: F::from_bits_truncate(F::RESET_ON_RESUME.bits() | F::TRB_OVERFETCH.bits()),
..Entry::WILDCARD
},
Entry { vendor: PCI_VENDOR_ID_ZHAOXIN, device: 0x9203, flags: F::TRB_OVERFETCH, ..Entry::WILDCARD },
// === Cadence ==========================================================
Entry { vendor: PCI_VENDOR_ID_CDNS, device: 0x0200, flags: F::CDNS_SCTX_QUIRK, ..Entry::WILDCARD },
// === Spec rule: xHCI 1.2+ gets DEFAULT_PM_RUNTIME_ALLOW ===============
Entry {
hci_version_lo: 0x0120,
hci_version_hi: 0xFFFF,
flags: F::DEFAULT_PM_RUNTIME_ALLOW,
..Entry::WILDCARD
},
];
#[cfg(test)]
mod tests {
use super::*;
fn lookup(vendor: u16, device: u16, revision: u8, hci_version: u16) -> F {
let mut flags = F::empty();
for entry in XHCI_QUIRK_TABLE {
if entry.matches(vendor, device, revision, hci_version) {
flags |= entry.flags;
}
}
flags
}
#[test]
fn fresco_logic_pdk_rev0_has_reset_ep_quirk() {
let q = lookup(PCI_VENDOR_ID_FRESCO_LOGIC, 0x1000, 0x0, 0x100);
assert!(q.contains(F::RESET_EP_QUIRK));
assert!(q.contains(F::BROKEN_STREAMS));
assert!(q.contains(F::BROKEN_MSI));
assert!(!q.contains(F::SLOW_SUSPEND));
}
#[test]
fn fresco_logic_pdk_rev4_has_slow_suspend() {
let q = lookup(PCI_VENDOR_ID_FRESCO_LOGIC, 0x1000, 0x4, 0x100);
assert!(q.contains(F::SLOW_SUSPEND));
assert!(q.contains(F::BROKEN_STREAMS));
assert!(q.contains(F::BROKEN_MSI));
assert!(!q.contains(F::RESET_EP_QUIRK));
}
#[test]
fn fresco_logic_fl1400_has_broken_msi_only() {
let q = lookup(PCI_VENDOR_ID_FRESCO_LOGIC, 0x1400, 0xFF, 0x100);
assert!(q.contains(F::BROKEN_MSI));
assert!(!q.contains(F::BROKEN_STREAMS));
}
#[test]
fn fresco_logic_fl1009_has_broken_streams() {
let q = lookup(PCI_VENDOR_ID_FRESCO_LOGIC, 0x1009, 0x00, 0x100);
assert!(q.contains(F::BROKEN_STREAMS));
assert!(!q.contains(F::BROKEN_MSI));
}
#[test]
fn nec_any_device_has_nec_host() {
let q = lookup(PCI_VENDOR_ID_NEC, 0x9999, 0x00, 0x100);
assert!(q.contains(F::NEC_HOST));
}
#[test]
fn amd_0x96_host_matches_by_hci_version() {
let q = lookup(PCI_VENDOR_ID_AMD, 0x9999, 0x00, 0x0096);
assert!(q.contains(F::AMD_0x96_HOST));
let q2 = lookup(PCI_VENDOR_ID_AMD, 0x9999, 0x00, 0x0100);
assert!(!q2.contains(F::AMD_0x96_HOST));
}
#[test]
fn amd_starship_has_limit_endpoint_interval_9() {
let q = lookup(PCI_VENDOR_ID_AMD, 0x148c, 0x00, 0x100);
assert!(q.contains(F::LIMIT_ENDPOINT_INTERVAL_9));
}
#[test]
fn amd_renoir_has_broken_d3cold_s2i() {
let q = lookup(PCI_VENDOR_ID_AMD, 0x1639, 0x00, 0x100);
assert!(q.contains(F::BROKEN_D3COLD_S2I));
}
#[test]
fn amd_promontory_all_have_u2_disable_wake() {
for dev in [0x43b9u16, 0x43ba, 0x43bb, 0x43bc] {
let q = lookup(PCI_VENDOR_ID_AMD, dev, 0x00, 0x100);
assert!(q.contains(F::U2_DISABLE_WAKE), "Promontory device 0x{:04x} missing U2_DISABLE_WAKE", dev);
}
}
#[test]
fn amd_promontory_2_and_4_have_no_soft_retry() {
for dev in [0x43b9u16, 0x43bb] {
let q = lookup(PCI_VENDOR_ID_AMD, dev, 0x00, 0x100);
assert!(q.contains(F::NO_SOFT_RETRY), "Promontory device 0x{:04x} missing NO_SOFT_RETRY", dev);
}
}
#[test]
fn ati_navi10_has_limit_endpoint_interval_9() {
let q = lookup(PCI_VENDOR_ID_ATI, 0x7316, 0x00, 0x100);
assert!(q.contains(F::LIMIT_ENDPOINT_INTERVAL_9));
}
#[test]
fn intel_baseline_any_device() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0xFFFF, 0x00, 0x100);
assert!(q.contains(F::LPM_SUPPORT));
assert!(q.contains(F::INTEL_HOST));
assert!(q.contains(F::AVOID_BEI));
}
#[test]
fn intel_panther_point_has_ep_limit_quirk() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0x1e31, 0x00, 0x100);
assert!(q.contains(F::EP_LIMIT_QUIRK));
assert!(q.contains(F::SW_BW_CHECKING));
assert!(q.contains(F::SPURIOUS_REBOOT));
assert!(q.contains(F::LPM_SUPPORT));
}
#[test]
fn intel_lynx_point_lp_9c31_no_duplicate_match_bug() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0x9c31, 0x00, 0x100);
assert!(q.contains(F::SPURIOUS_REBOOT));
assert!(q.contains(F::SPURIOUS_WAKEUP));
}
#[test]
fn intel_wildcat_point_lp_9cb1_fixed() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0x9cb1, 0x00, 0x100);
assert!(q.contains(F::SPURIOUS_REBOOT));
assert!(q.contains(F::SPURIOUS_WAKEUP));
}
#[test]
fn intel_cherryview_22b5_has_all_expected_flags() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0x22b5, 0x00, 0x100);
assert!(q.contains(F::PME_STUCK_QUIRK));
assert!(q.contains(F::SSIC_PORT_UNUSED));
assert!(q.contains(F::INTEL_USB_ROLE_SW));
assert!(q.contains(F::MISSING_CAS));
}
#[test]
fn intel_tiger_lake_pch_has_reset_to_default() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0xa0ed, 0x00, 0x100);
assert!(q.contains(F::RESET_TO_DEFAULT));
}
#[test]
fn intel_tiger_lake_non_pch_has_pm_runtime_allow_not_reset() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0x9a13, 0x00, 0x100);
assert!(q.contains(F::DEFAULT_PM_RUNTIME_ALLOW));
assert!(!q.contains(F::RESET_TO_DEFAULT), "0x9a13 is Tiger Lake non-PCH, not RESET_TO_DEFAULT");
}
#[test]
fn intel_alder_lake_pch_51ed_has_reset_to_default() {
let q = lookup(PCI_VENDOR_ID_INTEL, 0x51ed, 0x00, 0x100);
assert!(q.contains(F::RESET_TO_DEFAULT));
}
#[test]
fn etron_ej168_has_full_flag_set() {
let q = lookup(PCI_VENDOR_ID_ETRON, 0x7023, 0x00, 0x100);
assert!(q.contains(F::ETRON_HOST));
assert!(q.contains(F::RESET_ON_RESUME));
assert!(q.contains(F::BROKEN_STREAMS));
assert!(q.contains(F::NO_SOFT_RETRY));
}
#[test]
fn renesas_0014_has_zero_64b_only() {
let q = lookup(PCI_VENDOR_ID_RENESAS, 0x0014, 0x00, 0x100);
assert!(q.contains(F::ZERO_64B_REGS));
assert!(!q.contains(F::RESET_ON_RESUME));
}
#[test]
fn renesas_0015_has_both() {
let q = lookup(PCI_VENDOR_ID_RENESAS, 0x0015, 0x00, 0x100);
assert!(q.contains(F::ZERO_64B_REGS));
assert!(q.contains(F::RESET_ON_RESUME));
}
#[test]
fn via_vl805_fixed_flags() {
let q = lookup(PCI_VENDOR_ID_VIA, 0x3483, 0x00, 0x100);
assert!(q.contains(F::LPM_SUPPORT));
assert!(q.contains(F::TRB_OVERFETCH));
// VL805 also gets RESET_ON_RESUME from the vendor-wide VIA entry.
assert!(q.contains(F::RESET_ON_RESUME));
}
#[test]
fn via_3432_has_broken_streams() {
let q = lookup(PCI_VENDOR_ID_VIA, 0x3432, 0x00, 0x100);
assert!(q.contains(F::BROKEN_STREAMS));
assert!(q.contains(F::RESET_ON_RESUME));
}
#[test]
fn phytium_fixed_to_reset_on_resume() {
let q = lookup(PCI_VENDOR_ID_PHYTIUM, 0xdc27, 0x00, 0x100);
assert!(q.contains(F::RESET_ON_RESUME));
assert!(!q.contains(F::LIMIT_ENDPOINT_INTERVAL_9));
}
#[test]
fn asmedia_1042_has_spurious_success_and_broken_streams() {
let q = lookup(PCI_VENDOR_ID_ASMEDIA, 0x1042, 0x00, 0x100);
assert!(q.contains(F::SPURIOUS_SUCCESS));
assert!(q.contains(F::BROKEN_STREAMS));
}
#[test]
fn asmedia_1042a_fixed_flags() {
let q = lookup(PCI_VENDOR_ID_ASMEDIA, 0x1142, 0x00, 0x100);
assert!(q.contains(F::NO_64BIT_SUPPORT));
assert!(q.contains(F::ASMEDIA_MODIFY_FLOWCONTROL));
assert!(!q.contains(F::BROKEN_MSI));
}
#[test]
fn asmedia_1242_2142_3242_have_no_64bit_support() {
for dev in [0x1242u16, 0x2142, 0x3242] {
let q = lookup(PCI_VENDOR_ID_ASMEDIA, dev, 0x00, 0x100);
assert!(q.contains(F::NO_64BIT_SUPPORT), "ASMedia device 0x{:04x} missing NO_64BIT_SUPPORT", dev);
}
}
#[test]
fn asmedia_3042_has_reset_on_resume() {
let q = lookup(PCI_VENDOR_ID_ASMEDIA, 0x3042, 0x00, 0x100);
assert!(q.contains(F::RESET_ON_RESUME));
}
#[test]
fn ti_8241_has_limit_endpoint_interval_7() {
let q = lookup(PCI_VENDOR_ID_TI, 0x8241, 0x00, 0x100);
assert!(q.contains(F::LIMIT_ENDPOINT_INTERVAL_7));
}
#[test]
fn broadcom_9026_has_reset_pll_on_disconnect() {
let q = lookup(PCI_VENDOR_ID_BROADCOM, 0x9026, 0x00, 0x100);
assert!(q.contains(F::RESET_PLL_ON_DISCONNECT));
}
#[test]
fn cavium_9026_has_reset_pll_on_disconnect() {
let q = lookup(PCI_VENDOR_ID_CAVIUM, 0x9026, 0x00, 0x100);
assert!(q.contains(F::RESET_PLL_ON_DISCONNECT));
}
#[test]
fn zhaoxin_baseline_any_device() {
let q = lookup(PCI_VENDOR_ID_ZHAOXIN, 0xFFFF, 0x00, 0x100);
assert!(q.contains(F::ZHAOXIN_HOST));
assert!(q.contains(F::LPM_SUPPORT));
}
#[test]
fn zhaoxin_9202_has_reset_on_resume_and_trb_overfetch() {
let q = lookup(PCI_VENDOR_ID_ZHAOXIN, 0x9202, 0x00, 0x100);
assert!(q.contains(F::RESET_ON_RESUME));
assert!(q.contains(F::TRB_OVERFETCH));
}
#[test]
fn zhaoxin_9203_has_trb_overfetch_only() {
let q = lookup(PCI_VENDOR_ID_ZHAOXIN, 0x9203, 0x00, 0x100);
assert!(q.contains(F::TRB_OVERFETCH));
assert!(!q.contains(F::RESET_ON_RESUME));
}
#[test]
fn cdns_usbssp_has_cdns_sctx_quirk() {
let q = lookup(PCI_VENDOR_ID_CDNS, 0x0200, 0x00, 0x100);
assert!(q.contains(F::CDNS_SCTX_QUIRK));
}
#[test]
fn spec_rule_xhci_1_2_gets_default_pm_runtime_allow() {
let q = lookup(0x1234, 0x5678, 0x00, 0x0120);
assert!(q.contains(F::DEFAULT_PM_RUNTIME_ALLOW));
let q2 = lookup(0x1234, 0x5678, 0x00, 0x0110);
assert!(!q2.contains(F::DEFAULT_PM_RUNTIME_ALLOW));
}
#[test]
fn unknown_vendor_no_quirks_unless_spec_rule_applies() {
let q = lookup(0x1234, 0x5678, 0x00, 0x0100);
assert_eq!(q, F::empty());
}
}