From 431fa59e49986465570f13d9685cd574b1827c00 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Tue, 7 Jul 2026 12:52:54 +0300 Subject: [PATCH] =?UTF-8?q?xhcid:=20P7-C=20slice=201=20=E2=80=94=20port=20?= =?UTF-8?q?suspend/resume=20via=20link-state=20write?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the actual port suspend/resume path using the USB 3.0 link state definitions, cross-referenced with Linux 7.1 xhci-hub.c: xhci_set_link_state(). port.rs: - Port::set_link_state(state): writes PLS + PORT_LINK_STROBE after clearing all RW1CS/RW1S bits to neutral - Port::suspend(usb3): transitions to XDEV_U3 - Port::resume(): transitions to XDEV_U0 mod.rs: - Xhci::suspend_port(port_id): detects USB 3.0 vs USB 2.0 from port speed field, calls Port::suspend() - Xhci::resume_port(port_id): calls Port::resume() Each operation locks ports, validates the port index, and logs the transition at info level. This means the xhci controller can now transition individual USB 3.0 root-hub ports to U3 (suspend) and back to U0 (resume), which is the core mechanism for USB power management. The autosuspend timer that triggers these transitions automatically is P7-C slice 2. --- drivers/usb/xhcid/src/xhci/mod.rs | 26 ++++++++++++++++++++++++++ drivers/usb/xhcid/src/xhci/port.rs | 24 ++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs index 3942c4cab4..7e6f80d014 100644 --- a/drivers/usb/xhcid/src/xhci/mod.rs +++ b/drivers/usb/xhcid/src/xhci/mod.rs @@ -747,6 +747,32 @@ impl Xhci { Ok(()) } + /// Suspend a port to U3 link state. + /// Linux 7.1: xhci-hub.c → xhci_set_link_state(port, XDEV_U3). + pub fn suspend_port(&self, port_id: PortId) -> Result<()> { + let mut ports = self.ports.lock().unwrap_or_else(|e| e.into_inner()); + let port = ports.get_mut(port_id.root_hub_port_index()) + .ok_or_else(|| Error::new(EINVAL))?; + // Detect USB 3.0 protocol speed from the extended capabilities. + // The port speed field (bits 13:10) gives the current link speed. + let speed = port.speed(); + let usb3 = speed >= 4; // SuperSpeed (4) or SuperSpeedPlus (5+) + log::info!("xhcid: suspend port {} to U3 (USB3={})", port_id, usb3); + port.suspend(usb3); + Ok(()) + } + + /// Resume a port from U3 back to U0. + /// Linux 7.1: xhci-hub.c → xhci_set_link_state(port, XDEV_U0). + pub fn resume_port(&self, port_id: PortId) -> Result<()> { + let mut ports = self.ports.lock().unwrap_or_else(|e| e.into_inner()); + let port = ports.get_mut(port_id.root_hub_port_index()) + .ok_or_else(|| Error::new(EINVAL))?; + log::info!("xhcid: resume port {} to U0", port_id); + port.resume(); + Ok(()) + } + pub fn setup_scratchpads(&mut self) -> Result<()> { let buf_count = self.cap.max_scratchpad_bufs(); diff --git a/drivers/usb/xhcid/src/xhci/port.rs b/drivers/usb/xhcid/src/xhci/port.rs index 70f619102f..4b82f9c9f7 100644 --- a/drivers/usb/xhcid/src/xhci/port.rs +++ b/drivers/usb/xhcid/src/xhci/port.rs @@ -175,4 +175,28 @@ impl Port { pub fn link_state(&self) -> u8 { ((self.read() >> 5) & 0xF) as u8 } + + /// Transition the port to a new link state. + /// Linux 7.1: xhci_set_link_state() in xhci-hub.c. + /// Writes PLS and sets PORT_LINK_STROBE (LWS) to commit the + /// transition. All RW1CS/RW1S bits are cleared to neutral + /// before writing. + pub fn set_link_state(&mut self, link_state: u32) { + let neutral = self.flags_preserved(); + let val = (neutral & !(PortFlags::PLS_0 | PortFlags::PLS_1 | PortFlags::PLS_2 | PortFlags::PLS_3)).bits() + | link_state + | PortFlags::LWS.bits(); + self.portsc.write(val); + } + + /// Suspend the port to U3 (USB 3.0) or U3-equivalent for USB 2. + /// Linux 7.1: xhci-hub.c → xhci_set_link_state(xhci, port, XDEV_U3). + pub fn suspend(&mut self, usb3: bool) { + self.set_link_state(if usb3 { XDEV_U3 } else { XDEV_U3 }); + } + + /// Resume the port back to U0. + pub fn resume(&mut self) { + self.set_link_state(XDEV_U0); + } }