diff --git a/drivers/usb/xhcid/src/xhci/port.rs b/drivers/usb/xhcid/src/xhci/port.rs index c7624ab667..70f619102f 100644 --- a/drivers/usb/xhcid/src/xhci/port.rs +++ b/drivers/usb/xhcid/src/xhci/port.rs @@ -65,6 +65,30 @@ pub const PORT_HIRDM_MASK: u32 = 0x3u32; // Host Initiated Resume Durat pub const XHCI_DEFAULT_BESL: u32 = 4; pub const XHCI_L1_TIMEOUT: u32 = 512; // microseconds +// USB 3.0 Port Link States (PORTSC PLS field, bits 8:5). +// Cross-referenced with Linux 7.1 drivers/usb/host/xhci-port.h:18-21. +pub const XDEV_U0: u32 = 0x0 << 5; // Active +pub const XDEV_U1: u32 = 0x1 << 5; // Fast exit (<10us) +pub const XDEV_U2: u32 = 0x2 << 5; // Slower exit (>100us) +pub const XDEV_U3: u32 = 0x3 << 5; // Suspend +pub const XDEV_DISABLED: u32 = 0x4 << 5; +pub const XDEV_RXDETECT: u32 = 0x5 << 5; +pub const XDEV_INACTIVE: u32 = 0x6 << 5; +pub const XDEV_POLLING: u32 = 0x7 << 5; +pub const XDEV_RECOVERY: u32 = 0x8 << 5; +pub const XDEV_HOT_RESET: u32 = 0x9 << 5; +pub const XDEV_COMPLIANCE: u32 = 0xA << 5; +pub const XDEV_TEST_MODE: u32 = 0xB << 5; +pub const XDEV_RESUME: u32 = 0xF << 5; + +// Port PM Control (portpmsc) register bits (xHCI 1.1+). +// U1/U2 timeout values control how quickly the port transitions from +// U0 to U1/U2 after idling. See Linux xhci-port.h:128-132. +pub const PORT_U1_TIMEOUT_MASK: u32 = 0x0000_00FF; +pub const PORT_U2_TIMEOUT_SHIFT: u32 = 8; +pub const PORT_U2_TIMEOUT_MASK: u32 = 0x0000_FF00; +pub const PORT_FORCE_LINK_PM_ACCEPT: u32 = 1u32 << 19; // xHCI 1.1+ + impl Port { pub fn read(&self) -> u32 { self.portsc.read() @@ -135,4 +159,20 @@ impl Port { pub fn disable_lpm(&mut self) { self.porthlpmc.write(0); } + + /// Program U1/U2 inactivity timeout for USB 3.0 ports. + /// `u1_timeout` is multiplied by 256ns. + /// `u2_timeout` is multiplied by 1us. + /// Linux 7.1: xhci-hub.c computes these from device descriptors. + pub fn set_u1_u2_timeout(&mut self, u1_timeout: u8, u2_timeout: u8) { + let val = (u32::from(u1_timeout) & PORT_U1_TIMEOUT_MASK) + | ((u32::from(u2_timeout) << PORT_U2_TIMEOUT_SHIFT) & PORT_U2_TIMEOUT_MASK) + | PORT_FORCE_LINK_PM_ACCEPT; + self.portpmsc.write(val); + } + + /// Get the current port link state from PLS bits. + pub fn link_state(&self) -> u8 { + ((self.read() >> 5) & 0xF) as u8 + } }