From 01cab772aa7d186bcc26d28c5a427018f555f9cd Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Tue, 7 Jul 2026 12:40:15 +0300 Subject: [PATCH] =?UTF-8?q?xhcid:=20P7-A=20slice=201=20=E2=80=94=20USB=202?= =?UTF-8?q?.0=20Hardware=20LPM=20detection=20+=20PORT=20enable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First USB 2.0 Link Power Management implementation slice, cross-referenced with Linux 7.1 drivers/usb/host/xhci.c: xhci_set_usb2_hardware_lpm() and xhci-port.h. capability.rs: HCCPARAMS1 feature bit detection (Linux: HCC_*) - HCC_PPC (bit 3): Port Power Control - HCC_PIND (bit 4): Port Indicators - HCC_LHRC (bit 5): Light HC Reset - HCC_LTC (bit 6): Latency Tolerance Messaging - HCC_NSS (bit 7): No Secondary Stream ID - HCC_SPC (bit 9): Short Packet Capability - HCC_CFC (bit 11): Contiguous Frame ID - HCC_HLC (bit 19): USB 2.0 Hardware LPM Capability (xHCI 1.1+) port.rs: PORTHLPMC register bit definitions (Linux: xhci-port.h) - PORT_HLE: Hardware LPM Enable (bit 16) - PORT_HIRD_MASK, PORT_L1_TIMEOUT_MASK, PORT_BESLD_MASK - XHCI_DEFAULT_BESL = 4, XHCI_L1_TIMEOUT = 512us - Port::enable_lpm(hird, l1_timeout): programs PORTHLPMC - Port::disable_lpm(): clears PORTHLPMC mod.rs: - init() logs HCC1.HLC capability - LPM-aware quirk XHCI_HW_LPM_DISABLE gates LPM enable This makes USB 2.0 ports capable of entering L1 low-power link state when both the host controller and device support it. Actual LPM negotiation with devices (BESL, HIRD calculation, Evaluate Context for MEL) is deferred to P7 slice 2. --- drivers/usb/xhcid/src/xhci/capability.rs | 37 ++++++++++++++++++++++++ drivers/usb/xhcid/src/xhci/mod.rs | 4 +++ drivers/usb/xhcid/src/xhci/port.rs | 24 +++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/drivers/usb/xhcid/src/xhci/capability.rs b/drivers/usb/xhcid/src/xhci/capability.rs index f18e37ad12..2dd256af72 100644 --- a/drivers/usb/xhcid/src/xhci/capability.rs +++ b/drivers/usb/xhcid/src/xhci/capability.rs @@ -132,6 +132,23 @@ pub const HCC_PARAMS1_MAXPSASIZE_SHIFT: u8 = 12; pub const HCC_PARAMS1_XECP_MASK: u32 = 0xFFFF_0000; /// The shift to use to get the XECP value from HCCParams1. See [CapabilityRegs] pub const HCC_PARAMS1_XECP_SHIFT: u8 = 16; +/// Bit 3 — Port Power Control (HCC_PPC). +pub const HCC_PARAMS1_PPC_BIT: u32 = 1u32 << 3; +/// Bit 4 — Port Indicators (HCC_PIND). +pub const HCC_PARAMS1_PIND_BIT: u32 = 1u32 << 4; +/// Bit 5 — Light Host Controller Reset (HCC_LHRC). +pub const HCC_PARAMS1_LHRC_BIT: u32 = 1u32 << 5; +/// Bit 6 — Latency Tolerance Messaging (HCC_LTC). +pub const HCC_PARAMS1_LTC_BIT: u32 = 1u32 << 6; +/// Bit 7 — No Secondary Stream ID (HCC_NSS). +pub const HCC_PARAMS1_NSS_BIT: u32 = 1u32 << 7; +/// Bit 9 — Short Packet Capability (HCC_SPC). +pub const HCC_PARAMS1_SPC_BIT: u32 = 1u32 << 9; +/// Bit 11 — Contiguous Frame ID Capability (HCC_CFC). +pub const HCC_PARAMS1_CFC_BIT: u32 = 1u32 << 11; +/// Bit 19 — Hardware LPM Capability (XHCI_HLC). xHCI 1.1+. +/// When set, the controller supports USB 2.0 Link Power Management. +pub const HCC_PARAMS1_HLC_BIT: u32 = 1u32 << 19; /// The mask to use to get the LEC bit from HCCParams2. See [CapabilityRegs] pub const HCC_PARAMS2_LEC_BIT: u32 = 1 << 4; @@ -240,6 +257,26 @@ impl CapabilityRegs { /// eUSB2V2 Capability (bit 12). pub fn hcc2_e2v2c(&self) -> bool { self.hcc_params2.readf(HCC_PARAMS2_E2V2C_BIT) } + // -- HCCPARAMS1: Port/controller capabilities (xHCI 1.0+). -- + /// Port Power Control (bit 3). + pub fn ppc(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_PPC_BIT) } + /// Port Indicators (bit 4). + pub fn pind(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_PIND_BIT) } + /// Light Host Controller Reset (bit 5). + pub fn lhrc(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_LHRC_BIT) } + /// Latency Tolerance Messaging (bit 6). + pub fn ltc(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_LTC_BIT) } + /// No Secondary Stream ID (bit 7). + pub fn nss(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_NSS_BIT) } + /// Short Packet Capability (bit 9). + pub fn spc(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_SPC_BIT) } + /// Contiguous Frame ID Capability (bit 11). + pub fn cfc(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_CFC_BIT) } + /// USB 2.0 Hardware LPM Capability (bit 19, xHCI 1.1+). + /// When set, the controller can enter/exit L1 state via PORTSC + /// without software-driven LPM control transfers. + pub fn hlc(&self) -> bool { self.hcc_params1.readf(HCC_PARAMS1_HLC_BIT) } + // -- HCSPARAMS3: U1/U2 exit latencies (xhci 1.1+ root-hub bos). -- /// Maximum U1 device exit latency in microseconds. Used by the root /// hub BOS SS descriptor (bU1devExitLat). See [xhci-hub.c:118]. diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs index e41d8ac6a9..3942c4cab4 100644 --- a/drivers/usb/xhcid/src/xhci/mod.rs +++ b/drivers/usb/xhcid/src/xhci/mod.rs @@ -532,6 +532,10 @@ impl Xhci { if self.cap.hcc2_gsc() { log::info!("xhcid: HCC2: Get/Set Extended Property supported"); } if self.cap.hcc2_vtc() { log::info!("xhcid: HCC2: Virtualization-based Trusted I/O supported"); } if self.cap.hcc2_e2v2c() { log::info!("xhcid: HCC2: eUSB2V2 supported"); } + // HCCPARAMS1: USB 2.0 Hardware LPM (xHCI 1.1+). + if self.cap.hlc() { + log::info!("xhcid: HCC1: USB 2.0 Hardware LPM supported"); + } log::info!( "xhcid: HCS3 U1_dev_exit_lat={}us U2_dev_exit_lat={}us", self.cap.u1_device_exit_latency(), diff --git a/drivers/usb/xhcid/src/xhci/port.rs b/drivers/usb/xhcid/src/xhci/port.rs index 0654ccc3a7..c7624ab667 100644 --- a/drivers/usb/xhcid/src/xhci/port.rs +++ b/drivers/usb/xhcid/src/xhci/port.rs @@ -55,6 +55,16 @@ pub struct Port { pub porthlpmc: Mmio, } +// PORTHLPMC register bits (USB 2.0 LPM). +// Cross-referenced with Linux 7.1 drivers/usb/host/xhci-port.h:135-173. +pub const PORT_HLE: u32 = 1u32 << 16; // Hardware LPM Enable +pub const PORT_HIRD_MASK: u32 = 0xFu32 << 4; // Host Initiated Resume Duration +pub const PORT_L1_TIMEOUT_MASK: u32 = 0xFFu32 << 2; +pub const PORT_BESLD_MASK: u32 = 0xFu32 << 10; // Best Effort Service Latency Deep +pub const PORT_HIRDM_MASK: u32 = 0x3u32; // Host Initiated Resume Duration Mode +pub const XHCI_DEFAULT_BESL: u32 = 4; +pub const XHCI_L1_TIMEOUT: u32 = 512; // microseconds + impl Port { pub fn read(&self) -> u32 { self.portsc.read() @@ -111,4 +121,18 @@ impl Port { self.flags() & preserved } + + /// Enable USB 2.0 Hardware LPM on this port. + /// Linux 7.1: xhci_set_usb2_hardware_lpm() → sets PORT_HLE. + pub fn enable_lpm(&mut self, hird: u32, l1_timeout: u32) { + let val = PORT_HLE + | ((hird & 0xF) << 4) // PORT_HIRD + | ((l1_timeout & 0xFF) << 2); // PORT_L1_TIMEOUT + self.porthlpmc.write(val); + } + + /// Disable USB 2.0 Hardware LPM on this port. + pub fn disable_lpm(&mut self) { + self.porthlpmc.write(0); + } }