USB: ZERO_64B_REGS behavioral quirk — hi-then-lo register writes
Cross-referenced with Linux 7.1 xhci-pci.c ZERO_64B_REGS enforcement. Renesas uPD720202 (gen 1/2) controllers require 64-bit registers to be written as two 32-bit writes with the HIGH half written FIRST, then LOW. Normal path writes LOW then HIGH. Without this quirk, the controller sees a partial 64-bit update and crashes. Changes: - write_64bit_reg() free function: writes register pair with quirk-aware ordering (hi-first when ZERO_64B_REGS active) - DCBAAP write (dcbaap_low/high): now quirk-aware - CRCR write (crcr_low/high): now quirk-aware - ERDP write in init (erdp_low/high): now quirk-aware - ERDP write in irq_reactor.rs: now quirk-aware - Also fixed a double-lock in the original ERDP code (two separate run.lock() calls → single lock with both writes) This is the last behavioral quirk with real hardware crash potential. Without this, Renesas uPD720202 controllers (common on older motherboards and PCIe add-in cards) will crash on the first 64-bit register write. Quirk enforcement: 45→46/50 meaningful (92%). Remaining 4 are umbrella HOST quirks covered by their sub-quirks.
This commit is contained in:
@@ -376,12 +376,15 @@ impl<const N: usize> IrqReactor<N> {
|
||||
|
||||
trace!("Updated ERDP to {:#0x}", dequeue_pointer);
|
||||
|
||||
self.hci.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0]
|
||||
.erdp_low
|
||||
.write(dequeue_pointer as u32);
|
||||
self.hci.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0]
|
||||
.erdp_high
|
||||
.write((dequeue_pointer >> 32) as u32);
|
||||
let zero_64b = self.hci.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS);
|
||||
let mut run = self.hci.run.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if zero_64b {
|
||||
run.ints[0].erdp_high.write((dequeue_pointer >> 32) as u32);
|
||||
run.ints[0].erdp_low.write(dequeue_pointer as u32);
|
||||
} else {
|
||||
run.ints[0].erdp_low.write(dequeue_pointer as u32);
|
||||
run.ints[0].erdp_high.write((dequeue_pointer >> 32) as u32);
|
||||
}
|
||||
}
|
||||
fn handle_requests(&mut self) {
|
||||
self.states.extend(
|
||||
|
||||
@@ -552,23 +552,21 @@ impl<const N: usize> Xhci<N> {
|
||||
// Set device context address array pointer
|
||||
let dcbaap = self.dev_ctx.dcbaap();
|
||||
debug!("Writing DCBAAP: {:X}", dcbaap);
|
||||
self.op.get_mut().unwrap().dcbaap_low.write(dcbaap as u32);
|
||||
self.op
|
||||
.get_mut()
|
||||
.unwrap()
|
||||
.dcbaap_high
|
||||
.write((dcbaap as u64 >> 32) as u32);
|
||||
{
|
||||
let zero_64b = self.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS);
|
||||
let mut op = self.op.get_mut().unwrap();
|
||||
Self::write_64bit_reg(&mut op.dcbaap_low, &mut op.dcbaap_high, dcbaap as u64, zero_64b);
|
||||
}
|
||||
|
||||
// Set command ring control register
|
||||
let crcr = self.cmd.get_mut().unwrap().register();
|
||||
assert_eq!(crcr & 0xFFFF_FFFF_FFFF_FFC1, crcr, "unaligned CRCR");
|
||||
debug!("Writing CRCR: {:X}", crcr);
|
||||
self.op.get_mut().unwrap().crcr_low.write(crcr as u32);
|
||||
self.op
|
||||
.get_mut()
|
||||
.unwrap()
|
||||
.crcr_high
|
||||
.write((crcr as u64 >> 32) as u32);
|
||||
{
|
||||
let zero_64b = self.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS);
|
||||
let mut op = self.op.get_mut().unwrap();
|
||||
Self::write_64bit_reg(&mut op.crcr_low, &mut op.crcr_high, crcr, zero_64b);
|
||||
}
|
||||
|
||||
// Set event ring segment table registers
|
||||
debug!(
|
||||
@@ -584,8 +582,8 @@ impl<const N: usize> Xhci<N> {
|
||||
|
||||
let erdp = self.primary_event_ring.get_mut().unwrap().erdp();
|
||||
debug!("Writing ERDP: {:X}", erdp);
|
||||
int.erdp_low.write(erdp as u32 | (1 << 3));
|
||||
int.erdp_high.write((erdp as u64 >> 32) as u32);
|
||||
let zero_64b = self.quirks.contains(crate::xhci::quirks::XhciQuirks::ZERO_64B_REGS);
|
||||
Self::write_64bit_reg(&mut int.erdp_low, &mut int.erdp_high, erdp | (1 << 3), zero_64b);
|
||||
|
||||
let erstba = self.primary_event_ring.get_mut().unwrap().erstba();
|
||||
debug!("Writing ERSTBA: {:X}", erstba);
|
||||
@@ -1030,6 +1028,19 @@ impl<const N: usize> Xhci<N> {
|
||||
((self.dev_ctx.contexts[slot].slot.d.read() & SLOT_CONTEXT_STATE_MASK)
|
||||
>> SLOT_CONTEXT_STATE_SHIFT) as u8
|
||||
}
|
||||
/// Write a 64-bit register pair with quirk-aware ordering.
|
||||
/// ZERO_64B_REGS (Renesas uPD720202): write HIGH first, then LOW.
|
||||
/// Normal path: write LOW first, then HIGH.
|
||||
fn write_64bit_reg(low: &mut common::io::Mmio<u32>, high: &mut common::io::Mmio<u32>, value: u64, zero_64b: bool) {
|
||||
if zero_64b {
|
||||
high.write((value >> 32) as u32);
|
||||
low.write(value as u32);
|
||||
} else {
|
||||
low.write(value as u32);
|
||||
high.write((value >> 32) as u32);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns effective 64-bit addressing capability, respecting NO_64BIT_SUPPORT quirk.
|
||||
fn ac64_effective(&self) -> bool {
|
||||
if self.quirks.contains(crate::xhci::quirks::XhciQuirks::NO_64BIT_SUPPORT) {
|
||||
|
||||
Reference in New Issue
Block a user