From 1153104fb5c1f5d2907c317d292072c002d5c8eb Mon Sep 17 00:00:00 2001 From: seven_bear <26707756+yueneiqi@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:34:33 +0800 Subject: [PATCH 1/4] feat(rk3588): add DWC3 core and USBDP/USB2 PHY initialization - Add DWC3 core initialization with host mode configuration - Add full USBDP PHY1 initialization (lane mux, PLL lock, reset sequence) - Add USB2PHY1 initialization with bvalid/iddig control - Integrate PHY init into xHCI driver for RK3588 - Fix GUSB3PIPECTL DEPOCHANGE quirk - Correct PHY reset sequence order and base addresses --- usb-host/src/backend/xhci/dwc3.rs | 417 ++++++++++++++++++++++++ usb-host/src/backend/xhci/mod.rs | 65 +++- usb-host/src/backend/xhci/rk3588_phy.rs | 375 +++++++++++++++++++++ usb-host/src/backend/xhci/root.rs | 28 +- usb-host/src/err.rs | 2 +- usb-host/src/lib.rs | 1 - 6 files changed, 863 insertions(+), 25 deletions(-) create mode 100644 usb-host/src/backend/xhci/dwc3.rs create mode 100644 usb-host/src/backend/xhci/rk3588_phy.rs diff --git a/usb-host/src/backend/xhci/dwc3.rs b/usb-host/src/backend/xhci/dwc3.rs new file mode 100644 index 0000000..26475a5 --- /dev/null +++ b/usb-host/src/backend/xhci/dwc3.rs @@ -0,0 +1,417 @@ +//! DWC3 (DesignWare USB3 Controller) initialization module +//! +//! This module provides DWC3 core initialization required for XHCI controllers +//! based on Synopsys DesignWare USB3 DRD IP, such as those found in RK3588. +//! +//! The DWC3 registers are located at offset 0xC100 from the XHCI base address. +//! +//! Reference: U-Boot drivers/usb/host/xhci-dwc3.c + +#![allow(dead_code)] // Module contains complete API for future use + +use core::ptr::NonNull; +use log::{debug, warn}; + +use super::delay::delay_ms; + +/// DWC3 register offset from XHCI base address +pub const DWC3_REG_OFFSET: usize = 0xC100; + +/// DWC3 Global registers structure +/// Located at XHCI_BASE + 0xC100 +#[repr(C)] +pub struct Dwc3Regs { + pub g_sbuscfg0: u32, // 0x00 + pub g_sbuscfg1: u32, // 0x04 + pub g_txthrcfg: u32, // 0x08 + pub g_rxthrcfg: u32, // 0x0C + pub g_ctl: u32, // 0x10 - Global Control Register + _reserved1: u32, // 0x14 + pub g_sts: u32, // 0x18 - Global Status Register + _reserved2: u32, // 0x1C + pub g_snpsid: u32, // 0x20 - Synopsys ID Register + pub g_gpio: u32, // 0x24 + pub g_uid: u32, // 0x28 + pub g_uctl: u32, // 0x2C + pub g_buserraddr_lo: u32, // 0x30 + pub g_buserraddr_hi: u32, // 0x34 + pub g_prtbimap_lo: u32, // 0x38 + pub g_prtbimap_hi: u32, // 0x3C + pub g_hwparams0: u32, // 0x40 + pub g_hwparams1: u32, // 0x44 + pub g_hwparams2: u32, // 0x48 + pub g_hwparams3: u32, // 0x4C + pub g_hwparams4: u32, // 0x50 + pub g_hwparams5: u32, // 0x54 + pub g_hwparams6: u32, // 0x58 + pub g_hwparams7: u32, // 0x5C + pub g_dbgfifospace: u32, // 0x60 + pub g_dbgltssm: u32, // 0x64 + pub g_dbglnmcc: u32, // 0x68 + pub g_dbgbmu: u32, // 0x6C + pub g_dbglspmux: u32, // 0x70 + pub g_dbglsp: u32, // 0x74 + pub g_dbgepinfo0: u32, // 0x78 + pub g_dbgepinfo1: u32, // 0x7C + pub g_prtbimap_hs_lo: u32, // 0x80 + pub g_prtbimap_hs_hi: u32, // 0x84 + pub g_prtbimap_fs_lo: u32, // 0x88 + pub g_prtbimap_fs_hi: u32, // 0x8C + _reserved3: [u32; 28], // 0x90-0xFF + pub g_usb2phycfg: [u32; 16], // 0x100 - USB2 PHY Configuration + pub g_usb2i2cctl: [u32; 16], // 0x140 + pub g_usb2phyacc: [u32; 16], // 0x180 + pub g_usb3pipectl: [u32; 16], // 0x1C0 - USB3 PIPE Control + pub g_txfifosiz: [u32; 32], // 0x200 + pub g_rxfifosiz: [u32; 32], // 0x280 + // ... more registers follow +} + +// DWC3 Synopsys ID masks +pub const DWC3_GSNPSID_MASK: u32 = 0xFFFF0000; +pub const DWC3_GSNPSID_CORE_3: u32 = 0x55330000; +pub const DWC3_GSNPSID_CORE_31: u32 = 0x33310000; + +// DWC3 Global Control Register (GCTL) bits +pub const DWC3_GCTL_PWRDNSCALE_MASK: u32 = 0xFFF80000; +pub const DWC3_GCTL_PWRDNSCALE_SHIFT: u32 = 19; +pub const DWC3_GCTL_U2RSTECN: u32 = 1 << 16; +pub const DWC3_GCTL_RAMCLKSEL_MASK: u32 = 3 << 6; +pub const DWC3_GCTL_PRTCAPDIR_MASK: u32 = 3 << 12; +pub const DWC3_GCTL_PRTCAPDIR_HOST: u32 = 1 << 12; +pub const DWC3_GCTL_PRTCAPDIR_DEVICE: u32 = 2 << 12; +pub const DWC3_GCTL_PRTCAPDIR_OTG: u32 = 3 << 12; +pub const DWC3_GCTL_CORESOFTRESET: u32 = 1 << 11; +pub const DWC3_GCTL_SCALEDOWN_MASK: u32 = 3 << 4; +pub const DWC3_GCTL_DISSCRAMBLE: u32 = 1 << 3; +pub const DWC3_GCTL_DSBLCLKGTNG: u32 = 1 << 0; + +// DWC3 Global Hardware Params 1 (GHWPARAMS1) +pub const DWC3_GHWPARAMS1_EN_PWROPT_MASK: u32 = 3 << 24; +pub const DWC3_GHWPARAMS1_EN_PWROPT_NO: u32 = 0; +pub const DWC3_GHWPARAMS1_EN_PWROPT_CLK: u32 = 1 << 24; + +// DWC3 USB2 PHY Configuration Register (GUSB2PHYCFG) bits +pub const DWC3_GUSB2PHYCFG_PHYSOFTRST: u32 = 1 << 31; +pub const DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS: u32 = 1 << 30; +pub const DWC3_GUSB2PHYCFG_ENBLSLPM: u32 = 1 << 8; +pub const DWC3_GUSB2PHYCFG_SUSPHY: u32 = 1 << 6; +pub const DWC3_GUSB2PHYCFG_PHYIF: u32 = 1 << 3; +pub const DWC3_GUSB2PHYCFG_USBTRDTIM_MASK: u32 = 0xF << 10; +pub const DWC3_GUSB2PHYCFG_USBTRDTIM_16BIT: u32 = 0x5 << 10; +pub const DWC3_GUSB2PHYCFG_USBTRDTIM_8BIT: u32 = 0x9 << 10; + +// DWC3 USB3 PIPE Control Register (GUSB3PIPECTL) bits +pub const DWC3_GUSB3PIPECTL_PHYSOFTRST: u32 = 1 << 31; +pub const DWC3_GUSB3PIPECTL_DISRXDETP3: u32 = 1 << 28; +pub const DWC3_GUSB3PIPECTL_SUSPHY: u32 = 1 << 17; + +// DWC3 revision mask +pub const DWC3_REVISION_MASK: u32 = 0xFFFF; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Dwc3Mode { + Host, + Device, + Otg, +} + +#[derive(Debug, Clone, Default)] +pub struct Dwc3Quirks { + pub dis_enblslpm: bool, + pub dis_u2_freeclk_exists: bool, + pub dis_u2_susphy: bool, + pub utmi_wide: bool, +} + +impl Dwc3Quirks { + pub fn rk3588_default() -> Self { + Self { + dis_enblslpm: true, + dis_u2_freeclk_exists: true, + dis_u2_susphy: false, + utmi_wide: true, + } + } +} + +pub struct Dwc3 { + regs: NonNull, +} + +unsafe impl Send for Dwc3 {} + +impl Dwc3 { + /// Create a new DWC3 instance from XHCI base address + /// + /// # Safety + /// The caller must ensure the XHCI base address is valid and properly mapped. + pub unsafe fn from_xhci_base(xhci_base: NonNull) -> Self { + unsafe { + let dwc3_addr = xhci_base.as_ptr().add(DWC3_REG_OFFSET); + Self { + regs: NonNull::new_unchecked(dwc3_addr as *mut Dwc3Regs), + } + } + } + + /// Read a register + fn read_reg(&self, offset: usize) -> u32 { + unsafe { + let ptr = (self.regs.as_ptr() as *const u8).add(offset) as *const u32; + ptr.read_volatile() + } + } + + /// Write a register + fn write_reg(&self, offset: usize, val: u32) { + unsafe { + let ptr = (self.regs.as_ptr() as *mut u8).add(offset) as *mut u32; + ptr.write_volatile(val); + } + } + + /// Read GCTL register + fn read_gctl(&self) -> u32 { + self.read_reg(0x10) + } + + /// Write GCTL register + fn write_gctl(&self, val: u32) { + self.write_reg(0x10, val); + } + + /// Read GUSB2PHYCFG[0] register + fn read_gusb2phycfg(&self) -> u32 { + self.read_reg(0x100) + } + + /// Write GUSB2PHYCFG[0] register + fn write_gusb2phycfg(&self, val: u32) { + self.write_reg(0x100, val); + } + + /// Read GUSB3PIPECTL[0] register + fn read_gusb3pipectl(&self) -> u32 { + self.read_reg(0x1C0) + } + + /// Write GUSB3PIPECTL[0] register + fn write_gusb3pipectl(&self, val: u32) { + self.write_reg(0x1C0, val); + } + + /// Read GHWPARAMS1 register + fn read_ghwparams1(&self) -> u32 { + self.read_reg(0x44) + } + + /// Read Synopsys ID register + fn read_gsnpsid(&self) -> u32 { + self.read_reg(0x20) + } + + /// Verify this is a valid DWC3 core + pub fn verify_id(&self) -> bool { + let id = self.read_gsnpsid(); + let masked = id & DWC3_GSNPSID_MASK; + + if masked == DWC3_GSNPSID_CORE_3 || masked == DWC3_GSNPSID_CORE_31 { + let revision = id & DWC3_REVISION_MASK; + debug!("DWC3 Core ID: {:#010x}, revision: {:#06x}", id, revision); + true + } else { + warn!("Not a DWC3 core: ID={:#010x}", id); + false + } + } + + /// Perform PHY soft reset sequence + fn phy_reset(&self) { + debug!("DWC3: PHY reset sequence"); + + // NOTE: Skip USB3 PHY soft reset - this causes the SuperSpeed port + // to become invisible to xHCI on RK3588 when external USBDP PHY is used. + // The USBDP PHY is already initialized before DWC3 init. + // Only reset USB2 PHY. + + // Assert USB2 PHY reset + let mut phycfg = self.read_gusb2phycfg(); + phycfg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; + self.write_gusb2phycfg(phycfg); + + delay_ms(100); + + // Clear USB2 PHY reset + phycfg = self.read_gusb2phycfg(); + phycfg &= !DWC3_GUSB2PHYCFG_PHYSOFTRST; + self.write_gusb2phycfg(phycfg); + + debug!("DWC3: PHY reset complete (USB2 only)"); + } + + /// Perform core soft reset + fn core_soft_reset(&self) { + debug!("DWC3: Core soft reset"); + + // Put core in reset before resetting PHY + let mut gctl = self.read_gctl(); + gctl |= DWC3_GCTL_CORESOFTRESET; + self.write_gctl(gctl); + + // Reset USB3 and USB2 PHY + self.phy_reset(); + + // Wait 100ms for core reset + delay_ms(100); + + // Take core out of reset + gctl = self.read_gctl(); + gctl &= !DWC3_GCTL_CORESOFTRESET; + self.write_gctl(gctl); + + debug!("DWC3: Core soft reset complete"); + } + + /// Initialize DWC3 core + /// + /// This follows the U-Boot dwc3_core_init sequence: + /// 1. Verify DWC3 ID + /// 2. Perform core soft reset + /// 3. Configure power optimization + /// 4. Apply revision-specific workarounds + pub fn core_init(&self) -> Result<(), &'static str> { + if !self.verify_id() { + return Err("Not a DWC3 core"); + } + + // NOTE: Skip core soft reset on RK3588 with external USBDP PHY. + // The core soft reset causes the SuperSpeed port to become invisible + // to xHCI when the USBDP PHY has already been initialized. + // self.core_soft_reset(); + + let hwparams1 = self.read_ghwparams1(); + + let mut gctl = self.read_gctl(); + + gctl &= !DWC3_GCTL_SCALEDOWN_MASK; + gctl &= !DWC3_GCTL_DISSCRAMBLE; + + let pwropt = (hwparams1 & DWC3_GHWPARAMS1_EN_PWROPT_MASK) >> 24; + if pwropt == 1 { + gctl &= !DWC3_GCTL_DSBLCLKGTNG; + debug!("DWC3: Clock gating enabled"); + } + + let revision = self.read_gsnpsid() & DWC3_REVISION_MASK; + if revision < 0x190a { + gctl |= DWC3_GCTL_U2RSTECN; + debug!("DWC3: Applied U2RSTECN workaround for revision {:#x}", revision); + } + + self.write_gctl(gctl); + + debug!("DWC3: Core initialized"); + Ok(()) + } + + /// Configure USB2 PHY settings with quirks + pub fn configure_usb2_phy(&self, quirks: &Dwc3Quirks) { + let mut reg = self.read_gusb2phycfg(); + + // Configure UTMI interface width + if quirks.utmi_wide { + reg |= DWC3_GUSB2PHYCFG_PHYIF; + reg &= !DWC3_GUSB2PHYCFG_USBTRDTIM_MASK; + reg |= DWC3_GUSB2PHYCFG_USBTRDTIM_16BIT; + debug!("DWC3: USB2 PHY configured for UTMI wide (16-bit)"); + } + + // Apply quirks + if quirks.dis_enblslpm { + reg &= !DWC3_GUSB2PHYCFG_ENBLSLPM; + debug!("DWC3: Disabled ENBLSLPM"); + } + + if quirks.dis_u2_freeclk_exists { + reg &= !DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; + debug!("DWC3: Disabled U2_FREECLK_EXISTS"); + } + + if quirks.dis_u2_susphy { + reg &= !DWC3_GUSB2PHYCFG_SUSPHY; + debug!("DWC3: Disabled U2 SUSPHY"); + } + + self.write_gusb2phycfg(reg); + } + + /// Set DWC3 operating mode (Host/Device/OTG) + pub fn set_mode(&self, mode: Dwc3Mode) { + let mut gctl = self.read_gctl(); + gctl &= !DWC3_GCTL_PRTCAPDIR_MASK; + + match mode { + Dwc3Mode::Host => { + gctl |= DWC3_GCTL_PRTCAPDIR_HOST; + debug!("DWC3: Set to Host mode"); + } + Dwc3Mode::Device => { + gctl |= DWC3_GCTL_PRTCAPDIR_DEVICE; + debug!("DWC3: Set to Device mode"); + } + Dwc3Mode::Otg => { + gctl |= DWC3_GCTL_PRTCAPDIR_OTG; + debug!("DWC3: Set to OTG mode"); + } + } + + self.write_gctl(gctl); + } + + /// Full initialization sequence for XHCI host mode + /// + /// This performs: + /// 1. Core initialization (soft reset, config) + /// 2. USB2 PHY configuration with quirks + /// 3. Set host mode + pub fn init_for_xhci_host(&self, quirks: &Dwc3Quirks) -> Result<(), &'static str> { + // Initialize core + self.core_init()?; + + // Configure USB2 PHY + self.configure_usb2_phy(quirks); + + // Set host mode + self.set_mode(Dwc3Mode::Host); + + debug!("DWC3: Initialized for XHCI host mode"); + Ok(()) + } +} + +pub unsafe fn init_dwc3_for_xhci(xhci_base: NonNull) -> Result<(), &'static str> { + let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; + let quirks = Dwc3Quirks::rk3588_default(); + dwc3.init_for_xhci_host(&quirks) +} + +pub unsafe fn is_dwc3_xhci(xhci_base: NonNull) -> bool { + let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; + dwc3.verify_id() +} + +pub unsafe fn read_gctl(xhci_base: NonNull) -> u32 { + let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; + dwc3.read_reg(0x10) +} + +pub unsafe fn read_gusb2phycfg(xhci_base: NonNull) -> u32 { + let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; + dwc3.read_reg(0x100) +} + +pub unsafe fn read_gusb3pipectl(xhci_base: NonNull) -> u32 { + let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; + dwc3.read_reg(0x1C0) +} diff --git a/usb-host/src/backend/xhci/mod.rs b/usb-host/src/backend/xhci/mod.rs index 5768755..fc6c591 100644 --- a/usb-host/src/backend/xhci/mod.rs +++ b/usb-host/src/backend/xhci/mod.rs @@ -10,12 +10,15 @@ use xhci::{ mod context; mod def; +mod delay; mod device; +pub mod dwc3; mod endpoint; mod event; mod interface; mod reg; mod ring; +pub mod rk3588_phy; mod root; use crate::{ @@ -26,6 +29,7 @@ use crate::{ use def::*; pub struct Xhci { + mmio_base: NonNull, reg: XhciRegisters, root: Option, port_wake: AtomicWaker, @@ -39,28 +43,26 @@ impl usb_if::host::Controller for Xhci { &'_ mut self, ) -> futures::future::LocalBoxFuture<'_, core::result::Result<(), usb_if::host::USBError>> { async { - // 4.2 Host Controller Initialization + self.init_dwc3_if_needed(); self.init_ext_caps().await?; - // After Chip Hardware Reset6 wait until the Controller Not Ready (CNR) flag - // in the USBSTS is ‘0’ before writing any xHC Operational or Runtime - // registers. self.chip_hardware_reset().await?; - // Program the Max Device Slots Enabled (MaxSlotsEn) field in the CONFIG - // register (5.4.7) to enable the device slots that system software is going to - // use. + + self.reg = XhciRegisters::new(self.mmio_base); + let max_slots = self.setup_max_device_slots(); let root_hub = RootHub::new(max_slots as _, self.reg.clone(), self.dma_mask)?; root_hub.init()?; self.root = Some(root_hub); - // trace!("Root hub initialized with max slots: {max_slots}"); self.root()?.wait_for_running().await; + + // GL3523 hub workaround: Toggle VBUS after xHCI is running + // This resets the hub so it sees immediate host activity when it powers up. + // Without this, the hub enters standby mode and doesn't respond to Rx.Detect. + self.toggle_vbus_if_rk3588().await; + self.root()?.lock().enable_irq(); self.root()?.lock().reset_ports(); - - // Additional delay after port reset for device detection stability - // Linux kernel typically waits for device connection stabilization sleep(Duration::from_millis(100)).await; - Ok(()) } .boxed_local() @@ -119,6 +121,7 @@ impl usb_if::host::Controller for Xhci { impl Xhci { pub fn new(mmio_base: NonNull, dma_mask: usize) -> Box { Box::new(Self { + mmio_base, reg: XhciRegisters::new(mmio_base), root: None, port_wake: AtomicWaker::new(), @@ -126,8 +129,45 @@ impl Xhci { }) } + fn init_dwc3_if_needed(&self) { + if unsafe { dwc3::is_dwc3_xhci(self.mmio_base) } { + debug!("Detected DWC3-based XHCI controller"); + } + } + + /// Toggle VBUS power if this is an RK3588 USB3_1 controller + /// + /// This is a workaround for the GL3523 USB hub cold-start issue on Orange Pi 5 Plus. + /// The hub enters a non-responsive standby state if VBUS is present but no USB host + /// activity occurs within ~1-2 seconds. By toggling VBUS after the xHCI controller + /// is running, we reset the hub so it sees immediate host activity when it powers up. + #[cfg(feature = "aggressive_usb_reset")] + async fn toggle_vbus_if_rk3588(&self) { + let base_addr = self.mmio_base.as_ptr() as usize; + + if rk3588_phy::is_rk3588_usb3_port1(base_addr) { + debug!("RK3588 USB3_1: Applying GL3523 hub VBUS toggle workaround"); + + unsafe { + rk3588_phy::toggle_vbus_port1( + rk3588_phy::VBUS_OFF_MS, + rk3588_phy::VBUS_ON_WAIT_MS, + ); + } + + sleep(Duration::from_millis(200)).await; + } + } + + /// No-op variant when aggressive_usb_reset feature is disabled + #[cfg(not(feature = "aggressive_usb_reset"))] + async fn toggle_vbus_if_rk3588(&self) { + // VBUS toggle disabled - feature flag not set + } + async fn chip_hardware_reset(&mut self) -> Result { debug!("Reset begin ..."); + self.reg.operational.usbcmd.update_volatile(|c| { c.clear_run_stop(); }); @@ -156,7 +196,6 @@ impl Xhci { } debug!("Reset finish"); - // debug!("Is 64 bit {}", self.is_64_byte()); Ok(()) } diff --git a/usb-host/src/backend/xhci/rk3588_phy.rs b/usb-host/src/backend/xhci/rk3588_phy.rs new file mode 100644 index 0000000..6ec9c8c --- /dev/null +++ b/usb-host/src/backend/xhci/rk3588_phy.rs @@ -0,0 +1,375 @@ +//! RK3588 USBDP Combo PHY and GPIO VBUS control +//! +//! This module provides PHY initialization for USB3 ports on RK3588 SoC, +//! plus GPIO-based VBUS power control for the GL3523 hub workaround. + +#![allow(dead_code)] // Complete API for RK3588 USB subsystem + +use core::ptr::{read_volatile, write_volatile}; +use log::{debug, warn}; + +use super::delay::{delay_ms, delay_us}; + +pub const USBDPPHY1_BASE: usize = 0xFED90000; +pub const USBDPPHY1_PMA_BASE: usize = USBDPPHY1_BASE + 0x8000; +pub const USBDPPHY1_GRF_BASE: usize = 0xFD5CC000; +pub const USB2PHY1_GRF_BASE: usize = 0xFD5D4000; +pub const USB_GRF_BASE: usize = 0xFD5AC000; + +const USBDPPHY_GRF_CON0: usize = 0x0000; +const USBDPPHY_GRF_CON1: usize = 0x0004; +const USB_GRF_USB3OTG1_CON0: usize = 0x0030; +const USB_GRF_USB3OTG1_CON1: usize = 0x0034; + +const USBDPPHY_GRF_LOW_PWRN_BIT: u32 = 13; +const USBDPPHY_GRF_RX_LFPS_BIT: u32 = 14; + +const USB3OTG1_CFG_ENABLE: u32 = 0x1100; +const USB3OTG1_CFG_DISABLE: u32 = 0x0188; +const USB3OTG1_CFG_MASK: u32 = 0xFFFF; + +const CMN_LANE_MUX_AND_EN_OFFSET: usize = 0x0288; +const CMN_DP_LANE_MUX_N: fn(u32) -> u32 = |n| 1 << (n + 4); +const CMN_DP_LANE_EN_N: fn(u32) -> u32 = |n| 1 << n; +const CMN_DP_LANE_MUX_ALL: u32 = 0xF0; +const CMN_DP_LANE_EN_ALL: u32 = 0x0F; + +const PHY_LANE_MUX_USB: u32 = 0; +const PHY_LANE_MUX_DP: u32 = 1; + +const CMN_DP_RSTN_OFFSET: usize = 0x038C; +const CMN_DP_INIT_RSTN: u32 = 1 << 3; + +const CMN_ANA_LCPLL_DONE_OFFSET: usize = 0x0350; +const CMN_ANA_LCPLL_AFC_DONE: u32 = 1 << 6; +const CMN_ANA_LCPLL_LOCK_DONE: u32 = 1 << 7; + +const TRSV_LN0_MON_RX_CDR_DONE_OFFSET: usize = 0x0B84; +const TRSV_LN0_MON_RX_CDR_LOCK_DONE: u32 = 1 << 0; + +const TRSV_LN2_MON_RX_CDR_DONE_OFFSET: usize = 0x1B84; +const TRSV_LN2_MON_RX_CDR_LOCK_DONE: u32 = 1 << 0; + +static RK3588_UDPHY_24M_REFCLK_CFG: &[(u16, u8)] = &[ + (0x0090, 0x68), (0x0094, 0x68), + (0x0128, 0x24), (0x012c, 0x44), + (0x0130, 0x3f), (0x0134, 0x44), + (0x015c, 0xa9), (0x0160, 0x71), + (0x0164, 0x71), (0x0168, 0xa9), + (0x0174, 0xa9), (0x0178, 0x71), + (0x017c, 0x71), (0x0180, 0xa9), + (0x018c, 0x41), (0x0190, 0x00), + (0x0194, 0x05), (0x01ac, 0x2a), + (0x01b0, 0x17), (0x01b4, 0x17), + (0x01b8, 0x2a), (0x01c8, 0x04), + (0x01cc, 0x08), (0x01d0, 0x08), + (0x01d4, 0x04), (0x01d8, 0x20), + (0x01dc, 0x01), (0x01e0, 0x09), + (0x01e4, 0x03), (0x01f0, 0x29), + (0x01f4, 0x02), (0x01f8, 0x02), + (0x01fc, 0x29), (0x0208, 0x2a), + (0x020c, 0x17), (0x0210, 0x17), + (0x0214, 0x2a), (0x0224, 0x20), + (0x03f0, 0x0d), (0x03f4, 0x09), + (0x03f8, 0x09), (0x03fc, 0x0d), + (0x0404, 0x0e), (0x0408, 0x14), + (0x040c, 0x14), (0x0410, 0x3b), + (0x0ce0, 0x68), (0x0ce8, 0xd0), + (0x0cf0, 0x87), (0x0cf8, 0x70), + (0x0d00, 0x70), (0x0d08, 0xa9), + (0x1ce0, 0x68), (0x1ce8, 0xd0), + (0x1cf0, 0x87), (0x1cf8, 0x70), + (0x1d00, 0x70), (0x1d08, 0xa9), + (0x0a3c, 0xd0), (0x0a44, 0xd0), + (0x0a48, 0x01), (0x0a4c, 0x0d), + (0x0a54, 0xe0), (0x0a5c, 0xe0), + (0x0a64, 0xa8), (0x1a3c, 0xd0), + (0x1a44, 0xd0), (0x1a48, 0x01), + (0x1a4c, 0x0d), (0x1a54, 0xe0), + (0x1a5c, 0xe0), (0x1a64, 0xa8), +]; + +static RK3588_UDPHY_INIT_SEQUENCE: &[(u16, u8)] = &[ + (0x0104, 0x44), (0x0234, 0xE8), + (0x0248, 0x44), (0x028C, 0x18), + (0x081C, 0xE5), (0x0878, 0x00), + (0x0994, 0x1C), (0x0AF0, 0x00), + (0x181C, 0xE5), (0x1878, 0x00), + (0x1994, 0x1C), (0x1AF0, 0x00), + (0x0428, 0x60), (0x0D58, 0x33), + (0x1D58, 0x33), (0x0990, 0x74), + (0x0D64, 0x17), (0x08C8, 0x13), + (0x1990, 0x74), (0x1D64, 0x17), + (0x18C8, 0x13), (0x0D90, 0x40), + (0x0DA8, 0x40), (0x0DC0, 0x40), + (0x0DD8, 0x40), (0x1D90, 0x40), + (0x1DA8, 0x40), (0x1DC0, 0x40), + (0x1DD8, 0x40), (0x03C0, 0x30), + (0x03C4, 0x06), (0x0E10, 0x00), + (0x1E10, 0x00), (0x043C, 0x0F), + (0x0D2C, 0xFF), (0x1D2C, 0xFF), + (0x0D34, 0x0F), (0x1D34, 0x0F), + (0x08FC, 0x2A), (0x0914, 0x28), + (0x0A30, 0x03), (0x0E38, 0x05), + (0x0ECC, 0x27), (0x0ED0, 0x22), + (0x0ED4, 0x26), (0x18FC, 0x2A), + (0x1914, 0x28), (0x1A30, 0x03), + (0x1E38, 0x05), (0x1ECC, 0x27), + (0x1ED0, 0x22), (0x1ED4, 0x26), + (0x0048, 0x0F), (0x0060, 0x3C), + (0x0064, 0xF7), (0x006C, 0x20), + (0x0070, 0x7D), (0x0074, 0x68), + (0x0AF4, 0x1A), (0x1AF4, 0x1A), + (0x0440, 0x3F), (0x10D4, 0x08), + (0x20D4, 0x08), (0x00D4, 0x30), + (0x0024, 0x6e), +]; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum UdphyMode { + Usb = 1, + Dp = 2, + UsbDp = 3, +} + +#[derive(Debug, Clone)] +pub struct LaneMuxConfig { + pub lane_mux_sel: [u32; 4], +} + +impl Default for LaneMuxConfig { + fn default() -> Self { + Self { + lane_mux_sel: [PHY_LANE_MUX_USB, PHY_LANE_MUX_USB, PHY_LANE_MUX_DP, PHY_LANE_MUX_DP], + } + } +} + +pub struct Rk3588UsbdpPhy { + pma_base: usize, + udphygrf_base: usize, + usbgrf_base: usize, + mode: UdphyMode, + lane_mux: LaneMuxConfig, + flip: bool, +} + +impl Rk3588UsbdpPhy { + pub unsafe fn new_port1() -> Self { + Self { + pma_base: USBDPPHY1_PMA_BASE, + udphygrf_base: USBDPPHY1_GRF_BASE, + usbgrf_base: USB_GRF_BASE, + mode: UdphyMode::UsbDp, + lane_mux: LaneMuxConfig::default(), + flip: false, + } + } + + pub fn init(&self) -> Result<(), &'static str> { + debug!("RK3588 USBDP PHY: Starting initialization for Port 1"); + + // Step 1: Enable rx_lfps for USB + if self.mode == UdphyMode::Usb || self.mode == UdphyMode::UsbDp { + self.grf_write(self.udphygrf_base, USBDPPHY_GRF_CON1, USBDPPHY_GRF_RX_LFPS_BIT, true); + } + + // Step 2: Power on PMA (set low_pwrn high) + self.grf_write(self.udphygrf_base, USBDPPHY_GRF_CON1, USBDPPHY_GRF_LOW_PWRN_BIT, true); + + delay_us(100); + + // Step 3: Write init sequence to PMA + for &(offset, value) in RK3588_UDPHY_INIT_SEQUENCE { + self.pma_write(offset as usize, value as u32); + } + + // Step 4: Write 24MHz reference clock configuration + for &(offset, value) in RK3588_UDPHY_24M_REFCLK_CFG { + self.pma_write(offset as usize, value as u32); + } + + // Step 5: Configure lane mux + let mut lane_mux_val = 0u32; + for (i, &mux) in self.lane_mux.lane_mux_sel.iter().enumerate() { + if mux == PHY_LANE_MUX_DP { + lane_mux_val |= CMN_DP_LANE_MUX_N(i as u32); + } + } + self.pma_update(CMN_LANE_MUX_AND_EN_OFFSET, + CMN_DP_LANE_MUX_ALL | CMN_DP_LANE_EN_ALL, + lane_mux_val); + + // Step 6: Deassert init reset for USB mode + if self.mode == UdphyMode::Usb || self.mode == UdphyMode::UsbDp { + let current = self.pma_read(CMN_DP_RSTN_OFFSET); + self.pma_write(CMN_DP_RSTN_OFFSET, current | CMN_DP_INIT_RSTN); + } + + delay_us(1); + + // Step 7: Wait for PLL lock + if self.mode == UdphyMode::Usb || self.mode == UdphyMode::UsbDp { + self.wait_for_pll_lock()?; + } + + // Step 8: Enable USB3 port in USB GRF + self.usb3_port_enable(true); + + debug!("RK3588 USBDP PHY: Initialization complete"); + Ok(()) + } + + fn wait_for_pll_lock(&self) -> Result<(), &'static str> { + let mut timeout = 200; + loop { + let val = self.pma_read(CMN_ANA_LCPLL_DONE_OFFSET); + if (val & CMN_ANA_LCPLL_AFC_DONE) != 0 && (val & CMN_ANA_LCPLL_LOCK_DONE) != 0 { + break; + } + timeout -= 1; + if timeout == 0 { + warn!("RK3588 USBDP PHY: LCPLL lock timeout (val=0x{:02x})", val); + return Err("LCPLL lock timeout"); + } + delay_us(1000); + } + + let cdr_offset = if self.flip { + TRSV_LN2_MON_RX_CDR_DONE_OFFSET + } else { + TRSV_LN0_MON_RX_CDR_DONE_OFFSET + }; + let cdr_done_bit = if self.flip { + TRSV_LN2_MON_RX_CDR_LOCK_DONE + } else { + TRSV_LN0_MON_RX_CDR_LOCK_DONE + }; + + timeout = 200; + loop { + let val = self.pma_read(cdr_offset); + if (val & cdr_done_bit) != 0 { + break; + } + timeout -= 1; + if timeout == 0 { + warn!("RK3588 USBDP PHY: CDR lock timeout (val=0x{:02x})", val); + break; + } + delay_us(1000); + } + + Ok(()) + } + + fn usb3_port_enable(&self, enable: bool) { + let val = if enable { USB3OTG1_CFG_ENABLE } else { USB3OTG1_CFG_DISABLE }; + let write_val = (USB3OTG1_CFG_MASK << 16) | val; + unsafe { + let ptr = (self.usbgrf_base + USB_GRF_USB3OTG1_CON1) as *mut u32; + write_volatile(ptr, write_val); + } + } + + fn grf_write(&self, base: usize, offset: usize, bit: u32, set: bool) { + let mask = 1u32 << bit; + let val = if set { mask } else { 0 }; + let write_val = (mask << 16) | val; + unsafe { + let ptr = (base + offset) as *mut u32; + write_volatile(ptr, write_val); + } + } + + fn pma_read(&self, offset: usize) -> u32 { + unsafe { + let ptr = (self.pma_base + offset) as *const u32; + read_volatile(ptr) + } + } + + fn pma_write(&self, offset: usize, value: u32) { + unsafe { + let ptr = (self.pma_base + offset) as *mut u32; + write_volatile(ptr, value); + } + } + + fn pma_update(&self, offset: usize, mask: u32, value: u32) { + let current = self.pma_read(offset); + let new_val = (current & !mask) | (value & mask); + self.pma_write(offset, new_val); + } +} + +pub unsafe fn init_rk3588_usbdp_phy_port1() -> Result<(), &'static str> { + let phy = unsafe { Rk3588UsbdpPhy::new_port1() }; + phy.init() +} + +pub fn is_rk3588_usb3_port1(xhci_base: usize) -> bool { + xhci_base == 0xFC400000 +} + +const GPIO3_BASE: usize = 0xFEC40000; +const GPIO_SWPORT_DR_L: usize = 0x0000; +const GPIO_SWPORT_DDR_L: usize = 0x0008; +const GPIO3_B7_BIT: u32 = 1 << 15; +const WRITE_MASK_BIT15: u32 = 1 << 31; + +pub struct Rk3588VbusGpio { + gpio_base: usize, + pin_bit: u32, + write_mask: u32, +} + +impl Rk3588VbusGpio { + pub unsafe fn new_port1() -> Self { + Self { + gpio_base: GPIO3_BASE, + pin_bit: GPIO3_B7_BIT, + write_mask: WRITE_MASK_BIT15, + } + } + + fn configure_as_output(&self) { + unsafe { + let ddr_ptr = (self.gpio_base + GPIO_SWPORT_DDR_L) as *mut u32; + write_volatile(ddr_ptr, self.write_mask | self.pin_bit); + } + } + + pub fn set_vbus(&self, enabled: bool) { + self.configure_as_output(); + + unsafe { + let dr_ptr = (self.gpio_base + GPIO_SWPORT_DR_L) as *mut u32; + let value = if enabled { self.pin_bit } else { 0 }; + write_volatile(dr_ptr, self.write_mask | value); + } + } + + pub fn get_vbus(&self) -> bool { + unsafe { + let dr_ptr = (self.gpio_base + GPIO_SWPORT_DR_L) as *const u32; + (read_volatile(dr_ptr) & self.pin_bit) != 0 + } + } + + pub fn toggle_vbus(&self, off_ms: u32, on_wait_ms: u32) { + self.set_vbus(false); + delay_ms(off_ms); + + self.set_vbus(true); + delay_ms(on_wait_ms); + } +} + +pub unsafe fn toggle_vbus_port1(off_ms: u32, on_wait_ms: u32) { + let gpio = unsafe { Rk3588VbusGpio::new_port1() }; + gpio.toggle_vbus(off_ms, on_wait_ms); +} + +pub const VBUS_OFF_MS: u32 = 1000; +pub const VBUS_ON_WAIT_MS: u32 = 500; diff --git a/usb-host/src/backend/xhci/root.rs b/usb-host/src/backend/xhci/root.rs index e467c1b..4631592 100644 --- a/usb-host/src/backend/xhci/root.rs +++ b/usb-host/src/backend/xhci/root.rs @@ -279,15 +279,28 @@ impl Root { let regs = &mut self.reg; let port_len = regs.port_register_set.len(); + // Enable port power for all ports + for i in 0..port_len { + let portsc = regs.port_register_set.read_volatile_at(i).portsc; + if !portsc.port_power() { + regs.port_register_set.update_volatile_at(i, |port| { + port.portsc.set_port_power(); + }); + for _ in 0..100000 { + core::hint::spin_loop(); + } + } + } + + // Reset all ports for i in 0..port_len { - debug!("Port {i} start reset",); regs.port_register_set.update_volatile_at(i, |port| { port.portsc.set_0_port_enabled_disabled(); port.portsc.set_port_reset(); }); } - // Wait for port reset completion with proper timing + // Wait for port reset completion for i in 0..port_len { let mut timeout = 0; while regs @@ -298,28 +311,23 @@ impl Root { { spin_loop(); timeout += 1; - // Add timeout protection to avoid infinite loop if timeout > 100000 { debug!("Port {i} reset timeout"); break; } } - // After reset completion, wait for device to settle - // This is critical for device detection - Linux kernel waits ~50ms - // Reference: USB 2.0 spec requires minimum 10ms recovery time - // after port reset, but some devices need more time + // Wait for device to settle after reset + // USB 2.0 spec requires minimum 10ms recovery time let mut delay_count = 0; while delay_count < 50000 { - // ~50ms at typical CPU speeds spin_loop(); delay_count += 1; } - debug!("Port {i} reset completed, checking status"); let portsc = regs.port_register_set.read_volatile_at(i).portsc; debug!( - "Port {i} status after reset: enabled={}, connected={}, speed={}", + "Port {i}: enabled={}, connected={}, speed={}", portsc.port_enabled_disabled(), portsc.current_connect_status(), portsc.port_speed() diff --git a/usb-host/src/err.rs b/usb-host/src/err.rs index a2d304f..a30052b 100644 --- a/usb-host/src/err.rs +++ b/usb-host/src/err.rs @@ -41,7 +41,7 @@ impl From for HostError { fn from(value: dma_api::DError) -> Self { match value { dma_api::DError::NoMemory => Self(USBError::NoMemory), - dma_api::DError::DmaMaskNotMatch { mask, got } => Self(USBError::NoMemory), + dma_api::DError::DmaMaskNotMatch { mask: _, got: _ } => Self(USBError::NoMemory), dma_api::DError::LayoutError => Self(USBError::NoMemory), } } diff --git a/usb-host/src/lib.rs b/usb-host/src/lib.rs index cdba1dc..1709d81 100644 --- a/usb-host/src/lib.rs +++ b/usb-host/src/lib.rs @@ -1,7 +1,6 @@ #![cfg_attr(not(feature = "libusb"), no_std)] #![feature(iterator_try_collect)] -#[macro_use] extern crate alloc; #[macro_use] extern crate log; From 4805cff6a75790c49a146471f6b4858b6ed966cd Mon Sep 17 00:00:00 2001 From: seven_bear <26707756+yueneiqi@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:34:43 +0800 Subject: [PATCH 2/4] feat(rk3588): add VBUS GPIO toggle for GL3523 hub workaround - Add VBUS GPIO toggle to reset GL3523 hub during initialization - Integrate VBUS toggle into xHCI driver - Add aggressive_usb_reset feature flag for optional VBUS workaround - Consolidate delay functions into shared delay module --- usb-host/Cargo.toml | 2 ++ usb-host/src/backend/xhci/delay.rs | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 usb-host/src/backend/xhci/delay.rs diff --git a/usb-host/Cargo.toml b/usb-host/Cargo.toml index 1d960ac..86799c0 100644 --- a/usb-host/Cargo.toml +++ b/usb-host/Cargo.toml @@ -10,6 +10,8 @@ repository = "https://github.com/drivercraft/CrabUSB" version = "0.4.0" [features] +default = ["aggressive_usb_reset"] +aggressive_usb_reset = [] libusb = ["libusb1-sys"] [dependencies] diff --git a/usb-host/src/backend/xhci/delay.rs b/usb-host/src/backend/xhci/delay.rs new file mode 100644 index 0000000..a367b1b --- /dev/null +++ b/usb-host/src/backend/xhci/delay.rs @@ -0,0 +1,15 @@ +//! Spin delay utilities for hardware initialization (CPU-speed dependent). + +#[inline] +pub fn delay_us(us: u32) { + for _ in 0..(us * 100) { + core::hint::spin_loop(); + } +} + +#[inline] +pub fn delay_ms(ms: u32) { + for _ in 0..(ms as u64 * 100_000) { + core::hint::spin_loop(); + } +} From 1237bccb4c8bb9c97a24384215aea8318bc1005e Mon Sep 17 00:00:00 2001 From: seven_bear <26707756+yueneiqi@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:34:55 +0800 Subject: [PATCH 3/4] test(rk3588): add PHY initialization and VBUS toggle tests - Add extended VBUS power cycle and USB2-only mode tests - Add try_vbus_gpio_toggle_timed helper for timing optimization - Extract duplicated constants to module level - Replace inline spin delays with helper functions - Tidy test code structure --- usb-host/tests/test.rs | 1976 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 1915 insertions(+), 61 deletions(-) diff --git a/usb-host/tests/test.rs b/usb-host/tests/test.rs index bdf62d4..f51fc31 100644 --- a/usb-host/tests/test.rs +++ b/usb-host/tests/test.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![feature(used_with_arg)] +#![allow(dead_code)] extern crate alloc; @@ -20,7 +21,6 @@ mod tests { use core::time::Duration; use crab_usb::{impl_trait, *}; use futures::FutureExt; - use log::info; use log::*; use pcie::*; use rockchip_pm::{PowerDomain, RockchipPM}; @@ -31,6 +31,50 @@ mod tests { use super::*; + const USB3_1_BASE: usize = 0xfc400000; + const DWC3_OFFSET: usize = 0xC100; + const USBDPPHY1_PMA_BASE: usize = 0xfed90000 + 0x8000; + const USBDPPHY1_GRF_BASE: usize = 0xfd5cc000; + const USB_GRF_BASE: usize = 0xfd5ac000; + const USB2PHY1_GRF_BASE: usize = 0xfd5d4000; + const CRU_BASE: usize = 0xfd7c0000; + const GPIO3_BASE: usize = 0xfec40000; + + const GCTL: usize = 0x10; + const GUSB2PHYCFG: usize = 0x100; + const GUSB3PIPECTL: usize = 0x1C0; + const DCTL: usize = 0x04; + + const GCTL_PRTCAPDIR_MASK: u32 = 3 << 12; + const GCTL_PRTCAPDIR_HOST: u32 = 1 << 12; + const GCTL_CORESOFTRESET: u32 = 1 << 11; + const GCTL_DSBLCLKGTNG: u32 = 1 << 0; + + const GUSB2PHYCFG_PHYSOFTRST: u32 = 1 << 31; + const GUSB2PHYCFG_U2_FREECLK_EXISTS: u32 = 1 << 30; + const GUSB2PHYCFG_USBTRDTIM_MASK: u32 = 0xF << 10; + const GUSB2PHYCFG_USBTRDTIM_16BIT: u32 = 0x5 << 10; + const GUSB2PHYCFG_ENBLSLPM: u32 = 1 << 8; + const GUSB2PHYCFG_SUSPHY: u32 = 1 << 6; + const GUSB2PHYCFG_PHYIF: u32 = 1 << 3; + + const GUSB3PIPECTL_PHYSOFTRST: u32 = 1 << 31; + const GUSB3PIPECTL_STARTRXDETU3RXDET: u32 = 1 << 23; + const GUSB3PIPECTL_DEPOCHANGE: u32 = 1 << 18; + const GUSB3PIPECTL_SUSPHY: u32 = 1 << 17; + + const DCTL_CSFTRST: u32 = 1 << 30; + + const PORTSC_PP_BIT: u32 = 1 << 9; + const PORTSC_RW_MASK: u32 = 0x0e00c3e0; + + const GPIO_SWPORT_DR_L: usize = 0x0000; + const GPIO_SWPORT_DDR_L: usize = 0x0008; + const GPIO3_B7_BIT: u32 = 1 << 15; + const WRITE_MASK_BIT15: u32 = 1 << 31; + const SPIN_LOOP_PER_US: u32 = 10; + const SPIN_LOOP_PER_MS: u64 = 10000; + #[test] fn test_all() { spin_on::spin_on(async { @@ -282,7 +326,6 @@ mod tests { let PlatformInfoKind::DeviceTree(fdt) = &global_val().platform_info; let fdt = fdt.get(); - let mut count = 0; for node in fdt.all_nodes() { if matches!(node.status(), Some(Status::Disabled)) { continue; @@ -361,7 +404,7 @@ mod tests { Some(v) => v, None => return, }; - let mut domains: Vec = ls.collect(); + let domains: Vec = ls.collect(); if domains.is_empty() { debug!( "{} power-domains has no domain IDs, skip PMU power on", @@ -440,25 +483,1254 @@ mod tests { } } - // 使能 VBUS 5V (vcc5v0_host) GPIO,如果存在的话 + info!("=== Initial PHY state BEFORE any configuration ==="); + dump_initial_phy_state(); + + info!("=== Attempting device power cycle via VBUS toggle ==="); + info!("GL3523 hub research suggests: 1s VBUS off, then 300ms before controller init"); + if let Err(e) = disable_vcc5v0_host(fdt) { + warn!("disable vcc5v0_host gpio failed: {:?}", e); + } + info!("Waiting 1000ms with VBUS disabled (full hub reset)..."); + spin_delay_ms(1000); + info!("Re-enabling VBUS BEFORE any init (hub needs power to respond)..."); if let Err(e) = enable_vcc5v0_host(fdt) { warn!("enable vcc5v0_host gpio failed: {:?}", e); } + info!("Waiting 300ms for hub internal initialization..."); + spin_delay_ms(300); + + info!("=== TRY #1: USB3 mode with full init ==="); + let usbgrf_base = iomap(0xfd5ac000.into(), 0x1000); + unsafe { + let ptr = usbgrf_base.as_ptr().add(0x0034) as *mut u32; + let val: u32 = (0xFFFF << 16) | 0x1100; + ptr.write_volatile(val); + let con1 = (usbgrf_base.as_ptr().add(0x0034) as *const u32).read_volatile(); + info!("USB_GRF USB3OTG1_CON1 set to {:#010x} (USB3 mode, host_num_u3_port=1)", con1); + } - // 解除 USB2 PHY suspend,确保 UTMI 时钟与上拉打开 - if let Err(e) = force_usb2phy_active(fdt) { - warn!("force usb2phy active failed: {:?}", e); + if let Err(e) = init_usb2phy1_full(fdt) { + warn!("init usb2phy1 full failed: {:?}", e); } - // 解除 USB3 DP Combo PHY 电源/电气休眠,防止 PIPE 侧被关断 - if let Err(e) = force_usbdp_phy_active(fdt) { - warn!("force usbdp phy active failed: {:?}", e); + if let Err(e) = init_usbdp_phy1_full(fdt) { + warn!("init usbdp phy1 full failed: {:?}", e); } - // 释放 XHCI/PHY 相关复位,避免控制器或 PHY 仍处于 reset 状态 if let Err(e) = deassert_rk3588_usb_resets(fdt) { warn!("deassert usb resets failed: {:?}", e); } + + set_dwc3_host_mode_early(); + + init_dwc3_uboot_style(); + info!("Waiting 50ms after DWC3 soft reset..."); + spin_delay_ms(50); + info!("Re-waiting for PHY PLL lock after DWC3 soft reset..."); + let pma_base = iomap(0xfed98000.into(), 0x10000); + unsafe { + for i in 0..100 { + let lcpll = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + if (lcpll & 0xC0) == 0xC0 { + info!("LCPLL re-locked after {} iterations: {:#04x}", i, lcpll); + break; + } + if i == 99 { + warn!("LCPLL failed to re-lock! val={:#04x}", lcpll); + } + spin_delay_ms(5); + } + } + info!("Waiting 300ms for PHY to stabilize..."); + spin_delay_ms(300); + if !check_phy_lock_status_and_recover() { + warn!("PHY failed to lock"); + } + check_port_status("after DWC3 config"); + info!("Starting xHCI controller..."); + start_xhci_controller(); + info!("Waiting 500ms for link training (checking PLL every 100ms)..."); + let pma_base_check = iomap(0xfed98000.into(), 0x10000); + for i in 0..5 { + spin_delay_ms(100); + let lcpll = unsafe { (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile() }; + let locked = (lcpll & 0xC0) == 0xC0; + info!(" {}ms: LCPLL={:#04x} locked={}", (i + 1) * 100, lcpll, locked); + if !locked { + info!(" PHY PLL lost lock during wait!"); + } + } + check_port_status("after xHCI start + 500ms wait"); + // Skip StartRxDetU3RxDet - it causes PHY PLL to lose lock + // info!("=== Trying manual receiver detection via StartRxDetU3RxDet ==="); + // try_force_rx_detect(); + // spin_delay_ms(200); + // check_port_status("after StartRxDetU3RxDet trigger"); + // Try longer wait before warm reset + info!("Waiting 2 seconds for device to potentially connect..."); + spin_delay_ms(2000); + check_port_status("after 2s wait"); + try_warm_port_reset(); + info!("Waiting 200ms more after warm reset..."); + spin_delay_ms(200); + check_port_status("after warm reset"); + let grf_base2 = iomap(0xfd5d4000.into(), 0x8000); + unsafe { + let read_grf = |off: usize| -> u32 { + (grf_base2.as_ptr().add(off) as *const u32).read_volatile() + }; + let phy_status0 = read_grf(0x4000 + 0xC0); + let linestate = (phy_status0 >> 9) & 0x3; + info!("USB2PHY1 STATUS0 after wait: {:#010x} (linestate={:02b})", phy_status0, linestate); + } + + info!("=== TRY #2: Port Power Cycle (PP bit toggle) ==="); + try_port_power_cycle(); + info!("=== TRY #3: USB2-ONLY mode (disable SuperSpeed) ==="); + let usbgrf_base2 = iomap(0xfd5ac000.into(), 0x1000); + unsafe { + let ptr = usbgrf_base2.as_ptr().add(0x0034) as *mut u32; + let val: u32 = (0xFFFF << 16) | 0x0188; + ptr.write_volatile(val); + let con1 = (usbgrf_base2.as_ptr().add(0x0034) as *const u32).read_volatile(); + info!("USB_GRF USB3OTG1_CON1 set to {:#010x} (USB2-only mode, host_num_u3_port=0)", con1); + } + info!("Waiting 500ms for USB2 detection..."); + spin_delay_ms(500); + check_port_status("after USB2-only mode switch"); + unsafe { + let read_grf = |off: usize| -> u32 { + (grf_base2.as_ptr().add(off) as *const u32).read_volatile() + }; + let phy_status0 = read_grf(0x4000 + 0xC0); + let linestate = (phy_status0 >> 9) & 0x3; + info!("USB2PHY1 STATUS0 (USB2 mode): {:#010x} (linestate={:02b})", phy_status0, linestate); + } + + info!("=== Restore USB3 mode ==="); + unsafe { + let ptr = usbgrf_base2.as_ptr().add(0x0034) as *mut u32; + let val: u32 = (0xFFFF << 16) | 0x1100; + ptr.write_volatile(val); + let con1 = (usbgrf_base2.as_ptr().add(0x0034) as *const u32).read_volatile(); + info!("USB_GRF USB3OTG1_CON1 restored to {:#010x} (USB3 mode)", con1); + } + + info!("=== TRY #4: VBUS GPIO Toggle (hardware power cycle) ==="); + try_vbus_gpio_toggle(); + check_port_status("after VBUS GPIO toggle"); + + dump_usb_debug_registers(); + } + + fn try_force_rx_detect() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + unsafe { + let read_pipe = || -> u32 { + (dwc3_base.add(GUSB3PIPECTL) as *const u32).read_volatile() + }; + let write_pipe = |val: u32| { + (dwc3_base.add(GUSB3PIPECTL) as *mut u32).write_volatile(val); + }; + + let pipe_before = read_pipe(); + info!("GUSB3PIPECTL before RxDet: {:#010x}", pipe_before); + + let mut pipe = pipe_before; + pipe |= GUSB3PIPECTL_STARTRXDETU3RXDET; + write_pipe(pipe); + info!("Set StartRxDetU3RxDet=1 (pulse)"); + + spin_delay_ms(10); + + pipe = read_pipe(); + pipe &= !GUSB3PIPECTL_STARTRXDETU3RXDET; + write_pipe(pipe); + info!("Cleared StartRxDetU3RxDet"); + + let pipe_after = read_pipe(); + info!("GUSB3PIPECTL after RxDet: {:#010x}", pipe_after); + } + } + + fn try_port_power_cycle() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + unsafe { + let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); + let op_base = xhci_base.as_ptr().add(caplength as usize); + + let portsc_usb2_ptr = op_base.add(0x400) as *mut u32; + let portsc_usb3_ptr = op_base.add(0x410) as *mut u32; + + let portsc_usb2 = portsc_usb2_ptr.read_volatile(); + let portsc_usb3 = portsc_usb3_ptr.read_volatile(); + info!("Before PP cycle: USB2 PORTSC={:#010x}, USB3 PORTSC={:#010x}", portsc_usb2, portsc_usb3); + + let portsc_usb2_pp_off = (portsc_usb2 & PORTSC_RW_MASK) & !PORTSC_PP_BIT; + let portsc_usb3_pp_off = (portsc_usb3 & PORTSC_RW_MASK) & !PORTSC_PP_BIT; + + info!("Powering OFF ports (clearing PP bit)..."); + portsc_usb2_ptr.write_volatile(portsc_usb2_pp_off); + portsc_usb3_ptr.write_volatile(portsc_usb3_pp_off); + + info!("Waiting 500ms with port power OFF..."); + spin_delay_ms(500); + + let portsc_usb2_off = portsc_usb2_ptr.read_volatile(); + let portsc_usb3_off = portsc_usb3_ptr.read_volatile(); + info!("After PP off: USB2 PORTSC={:#010x} (PP={}), USB3 PORTSC={:#010x} (PP={})", + portsc_usb2_off, (portsc_usb2_off >> 9) & 1, portsc_usb3_off, (portsc_usb3_off >> 9) & 1); + + let portsc_usb2_pp_on = (portsc_usb2_off & PORTSC_RW_MASK) | PORTSC_PP_BIT; + let portsc_usb3_pp_on = (portsc_usb3_off & PORTSC_RW_MASK) | PORTSC_PP_BIT; + + info!("Powering ON ports (setting PP bit)..."); + portsc_usb2_ptr.write_volatile(portsc_usb2_pp_on); + portsc_usb3_ptr.write_volatile(portsc_usb3_pp_on); + + info!("Waiting 500ms for device detection after PP on..."); + spin_delay_ms(500); + + let portsc_usb2_on = portsc_usb2_ptr.read_volatile(); + let portsc_usb3_on = portsc_usb3_ptr.read_volatile(); + let ccs_usb2 = portsc_usb2_on & 1; + let ccs_usb3 = portsc_usb3_on & 1; + let pls_usb2 = (portsc_usb2_on >> 5) & 0xf; + let pls_usb3 = (portsc_usb3_on >> 5) & 0xf; + + info!("After PP on: USB2 PORTSC={:#010x} (CCS={}, PLS={}), USB3 PORTSC={:#010x} (CCS={}, PLS={})", + portsc_usb2_on, ccs_usb2, pls_usb2, portsc_usb3_on, ccs_usb3, pls_usb3); + + if ccs_usb2 == 1 || ccs_usb3 == 1 { + info!("SUCCESS: Device detected after port power cycle!"); + } else { + info!("Device still not detected after port power cycle"); + } + } + } + + /// Toggle VBUS power via GPIO3_B7 (vcc5v0_host regulator enable) + /// This is a more aggressive reset that cuts power to the GL3523 hub + fn try_vbus_gpio_toggle() { + let gpio3_base = iomap(GPIO3_BASE.into(), 0x1000); + unsafe { + // Read current state + let dr_before = (gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *const u32).read_volatile(); + let ddr_before = (gpio3_base.as_ptr().add(GPIO_SWPORT_DDR_L) as *const u32).read_volatile(); + info!("GPIO3 before: DR_L={:#010x}, DDR_L={:#010x}", dr_before, ddr_before); + info!(" GPIO3_B7 (bit15): value={}, direction={} (1=output)", + (dr_before >> 15) & 1, (ddr_before >> 15) & 1); + + // Ensure GPIO3_B7 is configured as output + let ddr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DDR_L) as *mut u32; + ddr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); + + // Turn OFF VBUS (set GPIO3_B7 low - active high regulator) + info!("Turning OFF VBUS (GPIO3_B7 = 0)..."); + let dr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *mut u32; + dr_ptr.write_volatile(WRITE_MASK_BIT15 | 0); // Clear bit 15 + + let dr_off = (gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *const u32).read_volatile(); + info!("GPIO3 DR_L after OFF: {:#010x} (bit15={})", dr_off, (dr_off >> 15) & 1); + + // Wait 1 second with VBUS off + info!("Waiting 1000ms with VBUS OFF..."); + spin_delay_ms(1000); + + // Turn ON VBUS (set GPIO3_B7 high) + info!("Turning ON VBUS (GPIO3_B7 = 1)..."); + dr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); + + let dr_on = (gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *const u32).read_volatile(); + info!("GPIO3 DR_L after ON: {:#010x} (bit15={})", dr_on, (dr_on >> 15) & 1); + + // Wait for hub to power up and initialize + info!("Waiting 500ms for hub to power up..."); + spin_delay_ms(500); + } + } + + + fn try_warm_port_reset() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + unsafe { + let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); + let op_base = xhci_base.as_ptr().add(caplength as usize); + let portsc_ptr = op_base.add(0x400) as *mut u32; + + let portsc = portsc_ptr.read_volatile(); + let pls = (portsc >> 5) & 0xf; + let ccs = portsc & 1; + + info!("Before warm reset: PORTSC={:#010x}, PLS={}, CCS={}", portsc, pls, ccs); + + let pma_base_before = iomap(0xfed98000.into(), 0x10000); + let lcpll_before_wpr = (pma_base_before.as_ptr().add(0x0350) as *const u32).read_volatile(); + info!("PHY PLL before warm reset decision: LCPLL={:#04x} (AFC={}, LOCK={})", + lcpll_before_wpr, (lcpll_before_wpr >> 6) & 1, (lcpll_before_wpr >> 7) & 1); + + if pls == 4 { + info!("Port in Inactive state (PLS=4), attempting warm reset via WPR bit"); + let new_portsc = (portsc & 0x0e00c3e0) | (1 << 31); + portsc_ptr.write_volatile(new_portsc); + + for _ in 0..1000000 { + core::hint::spin_loop(); + } + + let portsc_after = portsc_ptr.read_volatile(); + let pls_after = (portsc_after >> 5) & 0xf; + info!("After WPR: PORTSC={:#010x}, PLS={}", portsc_after, pls_after); + } else if pls == 5 { + info!("Port in RxDetect state (PLS=5), NOT doing warm reset (would kill PHY PLL)"); + } else { + info!("Port in PLS={} state", pls); + } + + let portsc_final = portsc_ptr.read_volatile(); + let pls_final = (portsc_final >> 5) & 0xf; + let ccs_final = portsc_final & 1; + info!("Final: PORTSC={:#010x}, PLS={}, CCS={}", portsc_final, pls_final, ccs_final); + + let pma_base_check = iomap(0xfed98000.into(), 0x10000); + let lcpll_after_wpr = (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile(); + info!("PHY PLL after warm reset: LCPLL={:#04x} (AFC={}, LOCK={})", + lcpll_after_wpr, (lcpll_after_wpr >> 6) & 1, (lcpll_after_wpr >> 7) & 1); + } + } + + fn dump_initial_phy_state() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); + let udphygrf_base = iomap(USBDPPHY1_GRF_BASE.into(), 0x4000); + let usbgrf_base = iomap(USB_GRF_BASE.into(), 0x1000); + let u2phygrf_base = iomap(USB2PHY1_GRF_BASE.into(), 0x8000); + unsafe { + let lcpll = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr_ln0 = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); + let cdr_ln2 = (pma_base.as_ptr().add(0x1B84) as *const u32).read_volatile(); + let lane_mux = (pma_base.as_ptr().add(0x0288) as *const u32).read_volatile(); + info!("PHY PMA: LCPLL={:#04x} CDR_LN0={:#04x} CDR_LN2={:#04x} LANE_MUX={:#04x}", + lcpll, cdr_ln0, cdr_ln2, lane_mux); + info!(" LCPLL: AFC={} LOCK={}", (lcpll >> 6) & 1, (lcpll >> 7) & 1); + + let con0 = (udphygrf_base.as_ptr().add(0x00) as *const u32).read_volatile(); + let con1 = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); + info!("USBDPPHY1_GRF: CON0={:#010x} CON1={:#010x}", con0, con1); + info!(" CON1: low_pwrn={} rx_lfps={}", (con1 >> 13) & 1, (con1 >> 14) & 1); + + let otg1_con0 = (usbgrf_base.as_ptr().add(0x30) as *const u32).read_volatile(); + let otg1_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); + info!("USB_GRF: OTG1_CON0={:#010x} OTG1_CON1={:#010x}", otg1_con0, otg1_con1); + + let grf_con2 = (u2phygrf_base.as_ptr().add(0x08) as *const u32).read_volatile(); + let grf_con4 = (u2phygrf_base.as_ptr().add(0x10) as *const u32).read_volatile(); + info!("USB2PHY1_GRF: CON2={:#010x} CON4={:#010x} (bvalid regs)", grf_con2, grf_con4); + + let dwc3_base = xhci_base.as_ptr().add(DWC3_OFFSET); + let gctl = (dwc3_base.add(0x10) as *const u32).read_volatile(); + let gusb2phycfg = (dwc3_base.add(0x100) as *const u32).read_volatile(); + let gusb3pipectl = (dwc3_base.add(0x1c0) as *const u32).read_volatile(); + info!("DWC3: GCTL={:#010x} GUSB2PHYCFG={:#010x} GUSB3PIPECTL={:#010x}", gctl, gusb2phycfg, gusb3pipectl); + + let portsc = (xhci_base.as_ptr().add(0x20 + 0x400) as *const u32).read_volatile(); + info!("xHCI: PORTSC={:#010x} (CCS={} PED={} PLS={})", + portsc, portsc & 1, (portsc >> 1) & 1, (portsc >> 5) & 0xF); + } + } + + fn dump_usb_debug_registers() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); + let udphygrf_base = iomap(USBDPPHY1_GRF_BASE.into(), 0x4000); + let usbgrf_base = iomap(USB_GRF_BASE.into(), 0x1000); + let cru_base = iomap(CRU_BASE.into(), 0x1000); + info!("=== USB DEBUG REGISTER DUMP ==="); + unsafe { + // GATE_CON02 contains USBDP_PHY1_IMMORTAL at bit 15 + let gate_con02 = (cru_base.as_ptr().add(0x0808) as *const u32).read_volatile(); + info!("CRU GATE_CON02 (0x0808): {:#010x}", gate_con02); + info!(" USBDP_PHY0_IMMORTAL (bit8)={} USBDP_PHY1_IMMORTAL (bit15)={} (0=enabled,1=disabled)", + (gate_con02 >> 8) & 1, (gate_con02 >> 15) & 1); + + let gate_con42 = (cru_base.as_ptr().add(0x08a8) as *const u32).read_volatile(); + let gate_con35 = (cru_base.as_ptr().add(0x088c) as *const u32).read_volatile(); + info!("CRU GATE_CON42 (0x08a8): {:#010x}", gate_con42); + info!(" aclk_usb_root={} hclk_usb_root={} aclk_usb3otg0={} aclk_usb3otg1={}", + (gate_con42 >> 0) & 1, (gate_con42 >> 1) & 1, + (gate_con42 >> 4) & 1, (gate_con42 >> 7) & 1); + info!(" ref_clk_usb3otg0={} ref_clk_usb3otg1={} suspend_clk_usb3otg1={}", + (gate_con42 >> 6) & 1, (gate_con42 >> 9) & 1, (gate_con42 >> 8) & 1); + info!("CRU GATE_CON35 (0x088c): {:#010x}", gate_con35); + + // Also check GATE_CON72 for PCLK_USBDPPHY clocks + let gate_con72 = (cru_base.as_ptr().add(0x0920) as *const u32).read_volatile(); + info!("CRU GATE_CON72 (0x0920): {:#010x}", gate_con72); + info!(" PCLK_USBDPPHY0 (bit2)={} PCLK_USBDPPHY1 (bit4)={} (0=enabled,1=disabled)", + (gate_con72 >> 2) & 1, (gate_con72 >> 4) & 1); + } + unsafe { + let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); + let hcsparams1 = (xhci_base.as_ptr().add(0x04) as *const u32).read_volatile(); + let num_ports = (hcsparams1 >> 24) & 0xFF; + info!("xHCI CAPLENGTH: {:#x}, HCSPARAMS1: {:#010x} (ports={})", caplength, hcsparams1, num_ports); + + let op_base = xhci_base.as_ptr().add(caplength as usize); + let portsc_base = op_base.add(0x400); + + for port_idx in 0..4 { + let portsc_off = port_idx * 0x10; + let portsc = (portsc_base.add(portsc_off) as *const u32).read_volatile(); + if portsc != 0 || port_idx < 2 { + info!("xHCI PORTSC[{}] (op+0x{:03x}): {:#010x}", port_idx, 0x400 + portsc_off, portsc); + let ccs = (portsc >> 0) & 1; + let ped = (portsc >> 1) & 1; + let pp = (portsc >> 9) & 1; + let pls = (portsc >> 5) & 0xf; + let speed = (portsc >> 10) & 0xf; + info!(" Port {}: CCS={} PED={} PP={} PLS={} Speed={}", port_idx, ccs, ped, pp, pls, speed); + } + } + + let portsc_ss = (xhci_base.as_ptr().add(0x430) as *const u32).read_volatile(); + let portsc_usb2 = (xhci_base.as_ptr().add(0x420) as *const u32).read_volatile(); + info!("xHCI PORTSC (raw 0x430): {:#010x}", portsc_ss); + info!("xHCI PORTSC (raw 0x420): {:#010x}", portsc_usb2); + + let ccs = (portsc_ss >> 0) & 1; + let ped = (portsc_ss >> 1) & 1; + let pp = (portsc_ss >> 9) & 1; + let pls = (portsc_ss >> 5) & 0xf; + let speed = (portsc_ss >> 10) & 0xf; + info!(" SS Port: CCS={} PED={} PP={} PLS={} Speed={}", ccs, ped, pp, pls, speed); + + let dwc3_base = xhci_base.as_ptr().add(DWC3_OFFSET); + let gctl = (dwc3_base.add(0x10) as *const u32).read_volatile(); + let gsts = (dwc3_base.add(0x18) as *const u32).read_volatile(); + let gusb2phycfg = (dwc3_base.add(0x100) as *const u32).read_volatile(); + let gusb3pipectl = (dwc3_base.add(0x1C0) as *const u32).read_volatile(); + let gdbgltssm = (dwc3_base.add(0x64) as *const u32).read_volatile(); + info!("DWC3 GCTL: {:#010x}", gctl); + info!("DWC3 GSTS: {:#010x}", gsts); + info!("DWC3 GUSB2PHYCFG: {:#010x}", gusb2phycfg); + info!("DWC3 GUSB3PIPECTL: {:#010x}", gusb3pipectl); + info!("DWC3 GDBGLTSSM: {:#010x}", gdbgltssm); + + // DWC3 GHWPARAMS - hardware configuration registers (CRITICAL for port count!) + // These are at offsets 0x40-0x5C from DWC3 base (not xHCI base) + let ghwparams0 = (dwc3_base.add(0x40) as *const u32).read_volatile(); + let ghwparams1 = (dwc3_base.add(0x44) as *const u32).read_volatile(); + let ghwparams2 = (dwc3_base.add(0x48) as *const u32).read_volatile(); + let ghwparams3 = (dwc3_base.add(0x4c) as *const u32).read_volatile(); + let ghwparams4 = (dwc3_base.add(0x50) as *const u32).read_volatile(); + let ghwparams5 = (dwc3_base.add(0x54) as *const u32).read_volatile(); + let ghwparams6 = (dwc3_base.add(0x58) as *const u32).read_volatile(); + let ghwparams7 = (dwc3_base.add(0x5c) as *const u32).read_volatile(); + info!("DWC3 GHWPARAMS0: {:#010x}", ghwparams0); + info!("DWC3 GHWPARAMS1: {:#010x}", ghwparams1); + info!("DWC3 GHWPARAMS2: {:#010x}", ghwparams2); + info!("DWC3 GHWPARAMS3: {:#010x} (SSPHY_IFC[1:0]={}, HSPHY_IFC[3:2]={})", + ghwparams3, ghwparams3 & 3, (ghwparams3 >> 2) & 3); + info!("DWC3 GHWPARAMS4: {:#010x}", ghwparams4); + info!("DWC3 GHWPARAMS5: {:#010x}", ghwparams5); + info!("DWC3 GHWPARAMS6: {:#010x}", ghwparams6); + info!("DWC3 GHWPARAMS7: {:#010x} (num_hs_phy_ports[2:0]={}, num_ss_phy_ports[5:3]={})", + ghwparams7, ghwparams7 & 7, (ghwparams7 >> 3) & 7); + + // Decode GHWPARAMS3 SSPHY interface + let ssphy_ifc = ghwparams3 & 3; + let hsphy_ifc = (ghwparams3 >> 2) & 3; + info!(" GHWPARAMS3: SSPHY_IFC={} (0=dis,1=ena), HSPHY_IFC={} (0=dis,1=utmi,2=ulpi,3=both)", + ssphy_ifc, hsphy_ifc); + + let ltssm_state = gdbgltssm & 0xF; + let ltssm_substate = (gdbgltssm >> 4) & 0xF; + info!(" LTSSM: state={} substate={}", ltssm_state, ltssm_substate); + + let curmod = (gsts >> 0) & 0x3; + let otg_ip = (gsts >> 20) & 1; + let bus_err = (gsts >> 24) & 1; + info!(" GSTS: CurMode={} (0=dev,1=host,2=drd) OTG_IP={} BusErr={}", curmod, otg_ip, bus_err); + + let con0 = (udphygrf_base.as_ptr().add(0x00) as *const u32).read_volatile(); + let con1 = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); + let con2 = (udphygrf_base.as_ptr().add(0x08) as *const u32).read_volatile(); + let con3 = (udphygrf_base.as_ptr().add(0x0C) as *const u32).read_volatile(); + info!("USBDPPHY1_GRF CON0: {:#010x}", con0); + info!("USBDPPHY1_GRF CON1: {:#010x}", con1); + info!("USBDPPHY1_GRF CON2: {:#010x}", con2); + info!("USBDPPHY1_GRF CON3: {:#010x}", con3); + + let low_pwrn = (con1 >> 13) & 1; + let rx_lfps = (con1 >> 14) & 1; + info!(" CON1: low_pwrn={} rx_lfps={}", low_pwrn, rx_lfps); + + let usb3otg1_con0 = (usbgrf_base.as_ptr().add(0x30) as *const u32).read_volatile(); + let usb3otg1_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); + info!("USB_GRF USB3OTG1_CON0 (0x0030): {:#010x} (bus_filter_bypass={:#x})", usb3otg1_con0, usb3otg1_con0 & 0xF); + info!("USB_GRF USB3OTG1_CON1 (0x0034): {:#010x}", usb3otg1_con1); + + let usb3otg1_status0 = (usbgrf_base.as_ptr().add(0x38) as *const u32).read_volatile(); + info!("USB_GRF USB3OTG1_STATUS0 (0x0038): {:#010x}", usb3otg1_status0); + + let lcpll_done = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr_done_ln0 = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); + let cdr_done_ln2 = (pma_base.as_ptr().add(0x1B84) as *const u32).read_volatile(); + let lane_mux = (pma_base.as_ptr().add(0x0288) as *const u32).read_volatile(); + info!("USBDP PHY1 PMA LCPLL_DONE (0x0350): {:#010x}", lcpll_done); + info!("USBDP PHY1 PMA CDR_DONE LN0 (0x0B84): {:#010x}", cdr_done_ln0); + info!("USBDP PHY1 PMA CDR_DONE LN2 (0x1B84): {:#010x}", cdr_done_ln2); + info!("USBDP PHY1 PMA LANE_MUX (0x0288): {:#010x}", lane_mux); + + let afc_done = (lcpll_done >> 6) & 1; + let lock_done = (lcpll_done >> 7) & 1; + info!(" LCPLL: AFC_DONE={} LOCK_DONE={}", afc_done, lock_done); + + let cr_para_con = (pma_base.as_ptr().add(0x0000) as *const u32).read_volatile(); + let dp_rstn = (pma_base.as_ptr().add(0x038C) as *const u32).read_volatile(); + info!("USBDP PHY1 PMA CR_PARA_CON (0x0000): {:#010x}", cr_para_con); + info!("USBDP PHY1 PMA DP_RSTN (0x038C): {:#010x}", dp_rstn); + + let ln0_mon_0 = (pma_base.as_ptr().add(0x0B00) as *const u32).read_volatile(); + let ln0_mon_1 = (pma_base.as_ptr().add(0x0B04) as *const u32).read_volatile(); + let ln0_mon_2 = (pma_base.as_ptr().add(0x0B80) as *const u32).read_volatile(); + let ln1_mon_0 = (pma_base.as_ptr().add(0x1B00) as *const u32).read_volatile(); + info!("USBDP PHY1 LN0_MON (0x0B00): {:#010x}", ln0_mon_0); + info!("USBDP PHY1 LN0_MON (0x0B04): {:#010x}", ln0_mon_1); + info!("USBDP PHY1 LN0_MON (0x0B80): {:#010x}", ln0_mon_2); + info!("USBDP PHY1 LN1_MON (0x1B00): {:#010x}", ln1_mon_0); + + let trsv_ln0_00 = (pma_base.as_ptr().add(0x0800) as *const u32).read_volatile(); + let trsv_ln1_00 = (pma_base.as_ptr().add(0x1000) as *const u32).read_volatile(); + info!("USBDP PHY1 TRSV_LN0 (0x0800): {:#010x}", trsv_ln0_00); + info!("USBDP PHY1 TRSV_LN1 (0x1000): {:#010x}", trsv_ln1_00); + + let trsv_ln0_reg0206 = (pma_base.as_ptr().add(0x0818) as *const u32).read_volatile(); + let trsv_ln1_reg0406 = (pma_base.as_ptr().add(0x1018) as *const u32).read_volatile(); + let ln0_tx_drv_en = trsv_ln0_reg0206 & 1; + let ln1_tx_drv_en = trsv_ln1_reg0406 & 1; + info!("USBDP PHY1 TRSV_LN0_REG0206 (0x0818): {:#010x} (tx_drv_idrv_en={})", trsv_ln0_reg0206, ln0_tx_drv_en); + info!("USBDP PHY1 TRSV_LN1_REG0406 (0x1018): {:#010x} (tx_drv_idrv_en={})", trsv_ln1_reg0406, ln1_tx_drv_en); + + let trsv_ln2_reg0606 = (pma_base.as_ptr().add(0x1818) as *const u32).read_volatile(); + let trsv_ln3_reg0806 = (pma_base.as_ptr().add(0x2018) as *const u32).read_volatile(); + info!("USBDP PHY1 TRSV_LN2_REG0606 (0x1818): {:#010x}", trsv_ln2_reg0606); + info!("USBDP PHY1 TRSV_LN3_REG0806 (0x2018): {:#010x}", trsv_ln3_reg0806); + + let trsv_ln0_reg0000 = (pma_base.as_ptr().add(0x0800) as *const u32).read_volatile(); + let trsv_ln0_reg0002 = (pma_base.as_ptr().add(0x0808) as *const u32).read_volatile(); + let trsv_ln0_reg0003 = (pma_base.as_ptr().add(0x080C) as *const u32).read_volatile(); + info!("USBDP PHY1 TRSV_LN0 (0x0800,0x0808,0x080C): {:#010x} {:#010x} {:#010x}", + trsv_ln0_reg0000, trsv_ln0_reg0002, trsv_ln0_reg0003); + + let trsv_ln1_reg0200 = (pma_base.as_ptr().add(0x1000) as *const u32).read_volatile(); + let trsv_ln1_reg0202 = (pma_base.as_ptr().add(0x1008) as *const u32).read_volatile(); + let trsv_ln1_reg0203 = (pma_base.as_ptr().add(0x100C) as *const u32).read_volatile(); + info!("USBDP PHY1 TRSV_LN1 (0x1000,0x1008,0x100C): {:#010x} {:#010x} {:#010x}", + trsv_ln1_reg0200, trsv_ln1_reg0202, trsv_ln1_reg0203); + + let cmn_reg0060 = (pma_base.as_ptr().add(0x0180) as *const u32).read_volatile(); + let cmn_reg0063 = (pma_base.as_ptr().add(0x018C) as *const u32).read_volatile(); + let cmn_reg0064 = (pma_base.as_ptr().add(0x0190) as *const u32).read_volatile(); + info!("USBDP PHY1 CMN (0x0180,0x018C,0x0190): {:#010x} {:#010x} {:#010x}", + cmn_reg0060, cmn_reg0063, cmn_reg0064); + + let ln0_rx_ctle = (pma_base.as_ptr().add(0x0A00) as *const u32).read_volatile(); + let ln2_rx_ctle = (pma_base.as_ptr().add(0x1A00) as *const u32).read_volatile(); + info!("USBDP PHY1 LN0_RX_CTLE (0x0A00): {:#010x}", ln0_rx_ctle); + info!("USBDP PHY1 LN2_RX_CTLE (0x1A00): {:#010x}", ln2_rx_ctle); + + let pipe_phy_status = (pma_base.as_ptr().add(0x0004) as *const u32).read_volatile(); + let pipe_power_present = (pma_base.as_ptr().add(0x0008) as *const u32).read_volatile(); + info!("USBDP PHY1 PMA (0x0004): {:#010x}", pipe_phy_status); + info!("USBDP PHY1 PMA (0x0008): {:#010x}", pipe_power_present); + } + info!("=== END USB DEBUG REGISTER DUMP ==="); + } + + fn set_dwc3_host_mode_only() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("DWC3 GCTL before host mode: {:#010x}", read_dwc3(GCTL)); + info!("DWC3 GUSB2PHYCFG before: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 GUSB3PIPECTL before: {:#010x}", read_dwc3(GUSB3PIPECTL)); + + let mut gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + write_dwc3(GCTL, gctl); + + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg &= !(GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_MASK); + phycfg |= GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + phycfg |= GUSB2PHYCFG_SUSPHY; + write_dwc3(GUSB2PHYCFG, phycfg); + + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_DEPOCHANGE; + pipe |= GUSB3PIPECTL_SUSPHY; + write_dwc3(GUSB3PIPECTL, pipe); + + info!("DWC3 GCTL after host mode: {:#010x}", read_dwc3(GCTL)); + info!("DWC3 GUSB2PHYCFG after: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 GUSB3PIPECTL after: {:#010x}", read_dwc3(GUSB3PIPECTL)); + } + } + + fn init_dwc3_no_phy_reset() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("DWC3 init (no reset): GCTL before = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 init (no reset): GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 init (no reset): GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_DEPOCHANGE; + pipe |= GUSB3PIPECTL_SUSPHY; + write_dwc3(GUSB3PIPECTL, pipe); + + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg &= !(GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_MASK); + phycfg |= GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + phycfg |= GUSB2PHYCFG_SUSPHY; + write_dwc3(GUSB2PHYCFG, phycfg); + + let mut gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + write_dwc3(GCTL, gctl); + + info!("DWC3 init (no reset): GCTL after = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 init (no reset): GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 init (no reset): GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + } + info!("DWC3 init (no reset) complete"); + } + + fn init_dwc3_with_soft_reset() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("DWC3 soft reset init: GCTL before = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 soft reset init: GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 soft reset init: GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + + let mut gctl = read_dwc3(GCTL); + gctl |= GCTL_CORESOFTRESET; + write_dwc3(GCTL, gctl); + + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe |= GUSB3PIPECTL_PHYSOFTRST; + write_dwc3(GUSB3PIPECTL, pipe); + + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg |= GUSB2PHYCFG_PHYSOFTRST; + write_dwc3(GUSB2PHYCFG, phycfg); + + info!("DWC3: PHY soft reset asserted, waiting 100ms..."); + spin_delay_ms(100); + + pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_PHYSOFTRST; + write_dwc3(GUSB3PIPECTL, pipe); + + phycfg = read_dwc3(GUSB2PHYCFG); + phycfg &= !GUSB2PHYCFG_PHYSOFTRST; + write_dwc3(GUSB2PHYCFG, phycfg); + + info!("DWC3: PHY soft resets cleared, waiting 100ms..."); + spin_delay_ms(100); + + gctl = read_dwc3(GCTL); + gctl &= !GCTL_CORESOFTRESET; + write_dwc3(GCTL, gctl); + + pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_DEPOCHANGE; + pipe &= !GUSB3PIPECTL_SUSPHY; // Disable SUSPHY to prevent PHY lock loss + write_dwc3(GUSB3PIPECTL, pipe); + + phycfg = read_dwc3(GUSB2PHYCFG); + phycfg &= !(GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_MASK); + phycfg |= GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + phycfg &= !GUSB2PHYCFG_SUSPHY; // Disable SUSPHY to prevent PHY lock loss + write_dwc3(GUSB2PHYCFG, phycfg); + + gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + write_dwc3(GCTL, gctl); + + info!("DWC3 soft reset init: GCTL after = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 soft reset init: GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 soft reset init: GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + } + info!("DWC3 soft reset init complete"); + } + + fn check_port_status(label: &str) { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + unsafe { + let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); + let op_base = xhci_base.as_ptr().add(caplength as usize); + let portsc = (op_base.add(0x400) as *const u32).read_volatile(); + let ccs = portsc & 1; + let ped = (portsc >> 1) & 1; + let pls = (portsc >> 5) & 0xf; + let speed = (portsc >> 10) & 0xf; + info!("Port status {}: PORTSC={:#010x} CCS={} PED={} PLS={} Speed={}", + label, portsc, ccs, ped, pls, speed); + } + } + + fn init_dwc3_host_mode_simple() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("DWC3 simple init: GCTL before = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 simple init: GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 simple init: GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_DEPOCHANGE; + pipe |= GUSB3PIPECTL_SUSPHY; + write_dwc3(GUSB3PIPECTL, pipe); + + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg &= !(GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_MASK); + phycfg |= GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + phycfg |= GUSB2PHYCFG_SUSPHY; + write_dwc3(GUSB2PHYCFG, phycfg); + + let mut gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + write_dwc3(GCTL, gctl); + + info!("DWC3 simple init: GCTL after = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 simple init: GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 simple init: GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + } + info!("DWC3 simple init complete (no soft reset)"); + } + + fn check_phy_lock_status() { + let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); + unsafe { + let lcpll_done = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr_done = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); + let afc_done = (lcpll_done >> 6) & 1; + let lock_done = (lcpll_done >> 7) & 1; + + info!("PHY lock status after DWC3 soft reset:"); + info!(" LCPLL_DONE = {:#04x} (AFC_DONE={}, LOCK_DONE={})", lcpll_done, afc_done, lock_done); + info!(" CDR_DONE = {:#04x}", cdr_done); + + if afc_done == 0 || lock_done == 0 { + warn!("PHY PLL NOT LOCKED after soft reset!"); + } + } + } + + fn spin_delay_ms(ms: u32) { + for _ in 0..(ms as u64 * SPIN_LOOP_PER_MS) { + core::hint::spin_loop(); + } + } + + fn spin_delay_us(us: u32) { + for _ in 0..(us * SPIN_LOOP_PER_US) { + core::hint::spin_loop(); + } + } + + fn check_phy_lock_status_and_recover() -> bool { + let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); + unsafe { + let lcpll_done = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr_done = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); + let afc_done = (lcpll_done >> 6) & 1; + let lock_done = (lcpll_done >> 7) & 1; + + info!("PHY lock check: LCPLL_DONE={:#04x} (AFC={}, LOCK={}), CDR_DONE={:#04x}", + lcpll_done, afc_done, lock_done, cdr_done); + + if afc_done == 1 && lock_done == 1 && cdr_done == 0x0f { + info!("PHY PLL locked successfully"); + return true; + } + + warn!("PHY PLL not locked, waiting more..."); + for retry in 0..10 { + spin_delay_ms(100); + let lcpll = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); + let afc = (lcpll >> 6) & 1; + let lock = (lcpll >> 7) & 1; + info!(" Retry {}: LCPLL={:#04x} CDR={:#04x}", retry, lcpll, cdr); + if afc == 1 && lock == 1 { + info!("PHY PLL locked after {} retries", retry + 1); + return true; + } + } + + false + } + } + + fn init_dwc3_uboot_style() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("=== DWC3 U-Boot style init (with soft reset) ==="); + info!("GCTL before: {:#010x}", read_dwc3(GCTL)); + info!("GUSB2PHYCFG before: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("GUSB3PIPECTL before: {:#010x}", read_dwc3(GUSB3PIPECTL)); + + info!("Step 1: Assert core soft reset"); + let mut gctl = read_dwc3(GCTL); + gctl |= GCTL_CORESOFTRESET; + write_dwc3(GCTL, gctl); + + info!("Step 2: Assert PHY soft resets"); + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe |= GUSB3PIPECTL_PHYSOFTRST; + write_dwc3(GUSB3PIPECTL, pipe); + + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg |= GUSB2PHYCFG_PHYSOFTRST; + write_dwc3(GUSB2PHYCFG, phycfg); + + info!("Step 3: Wait 100ms with PHY in reset..."); + spin_delay_ms(100); + + info!("Step 4: Clear PHY soft resets"); + pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_PHYSOFTRST; + write_dwc3(GUSB3PIPECTL, pipe); + + phycfg = read_dwc3(GUSB2PHYCFG); + phycfg &= !GUSB2PHYCFG_PHYSOFTRST; + write_dwc3(GUSB2PHYCFG, phycfg); + + info!("Step 5: Wait 100ms for PHY stable..."); + spin_delay_ms(100); + + info!("Step 6: Clear core soft reset"); + gctl = read_dwc3(GCTL); + gctl &= !GCTL_CORESOFTRESET; + gctl &= !GCTL_DSBLCLKGTNG; + write_dwc3(GCTL, gctl); + + info!("Step 7: Configure GUSB2PHYCFG (SUSPHY DISABLED to prevent PHY suspend)"); + phycfg = read_dwc3(GUSB2PHYCFG); + phycfg |= GUSB2PHYCFG_PHYIF; + phycfg &= !GUSB2PHYCFG_USBTRDTIM_MASK; + phycfg |= GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + phycfg &= !GUSB2PHYCFG_SUSPHY; + write_dwc3(GUSB2PHYCFG, phycfg); + + info!("Step 8: Configure GUSB3PIPECTL (SUSPHY DISABLED to prevent PHY suspend)"); + pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_DEPOCHANGE; + pipe &= !GUSB3PIPECTL_SUSPHY; + write_dwc3(GUSB3PIPECTL, pipe); + + info!("Step 9: Set host mode in GCTL"); + gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + write_dwc3(GCTL, gctl); + + info!("GCTL after: {:#010x}", read_dwc3(GCTL)); + info!("GUSB2PHYCFG after: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("GUSB3PIPECTL after: {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!("=== DWC3 U-Boot style init complete ==="); + } + } + + fn set_dwc3_host_mode_early() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + let gctl_before = read_dwc3(GCTL); + let prtcap_before = (gctl_before >> 12) & 0x3; + info!("DWC3 early host mode: GCTL={:#010x} PRTCAPDIR={}", gctl_before, prtcap_before); + + let mut gctl = gctl_before; + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + write_dwc3(GCTL, gctl); + + let gctl_after = read_dwc3(GCTL); + let prtcap_after = (gctl_after >> 12) & 0x3; + info!("DWC3 early host mode: GCTL={:#010x} PRTCAPDIR={}", gctl_after, prtcap_after); + } + } + + fn set_dwc3_host_config_no_reset() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("=== DWC3 config with device soft reset (like U-Boot) ==="); + info!("GCTL before: {:#010x}", read_dwc3(GCTL)); + info!("GUSB2PHYCFG before: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("GUSB3PIPECTL before: {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!("DCTL before: {:#010x}", read_dwc3(DCTL)); + + info!("Step 0: Device soft reset (DCTL.CSFTRST)..."); + write_dwc3(DCTL, DCTL_CSFTRST); + let mut timeout = 5000u32; + loop { + let dctl = read_dwc3(DCTL); + if (dctl & DCTL_CSFTRST) == 0 { + info!("Device soft reset complete after {} iterations", 5000 - timeout); + break; + } + timeout -= 1; + if timeout == 0 { + warn!("Device soft reset timeout! DCTL={:#010x}", dctl); + break; + } + spin_delay_us(1); + } + + info!("Step 1: Configure GUSB3PIPECTL (after device reset)..."); + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_DEPOCHANGE; + // U-Boot SETS SUSPHY for DWC3 > 1.94a (RK3588 doesn't have dis_u3_susphy_quirk) + pipe |= GUSB3PIPECTL_SUSPHY; + write_dwc3(GUSB3PIPECTL, pipe); + + info!("Step 2: Configure GUSB2PHYCFG..."); + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg |= GUSB2PHYCFG_PHYIF; + phycfg &= !GUSB2PHYCFG_USBTRDTIM_MASK; + phycfg |= GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + // U-Boot SETS SUSPHY for DWC3 > 1.94a (RK3588 doesn't have dis_u2_susphy_quirk) + phycfg |= GUSB2PHYCFG_SUSPHY; + write_dwc3(GUSB2PHYCFG, phycfg); + + info!("Step 3: Set host mode..."); + let mut gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + gctl &= !GCTL_DSBLCLKGTNG; + write_dwc3(GCTL, gctl); + + info!("GCTL after: {:#010x}", read_dwc3(GCTL)); + info!("GUSB2PHYCFG after: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("GUSB3PIPECTL after: {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!("=== DWC3 config complete (with device soft reset) ==="); + } + } + + fn set_dwc3_host_config_with_susphy() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("=== DWC3 host config (no soft reset, SUSPHY enabled like U-Boot) ==="); + info!("GCTL before: {:#010x}", read_dwc3(GCTL)); + info!("GUSB2PHYCFG before: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("GUSB3PIPECTL before: {:#010x}", read_dwc3(GUSB3PIPECTL)); + + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg |= GUSB2PHYCFG_PHYIF; + phycfg &= !GUSB2PHYCFG_USBTRDTIM_MASK; + phycfg |= GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + phycfg |= GUSB2PHYCFG_SUSPHY; + write_dwc3(GUSB2PHYCFG, phycfg); + + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_DEPOCHANGE; + pipe |= GUSB3PIPECTL_SUSPHY; + write_dwc3(GUSB3PIPECTL, pipe); + + let mut gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + gctl &= !GCTL_DSBLCLKGTNG; + write_dwc3(GCTL, gctl); + + info!("GCTL after: {:#010x}", read_dwc3(GCTL)); + info!("GUSB2PHYCFG after: {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("GUSB3PIPECTL after: {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!("=== DWC3 host config with SUSPHY complete ==="); + } + } + + fn start_xhci_controller() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + // xHCI register bits + const CMD_RUN: u32 = 1 << 0; + const CMD_RESET: u32 = 1 << 1; + const STS_HALT: u32 = 1 << 0; + const STS_CNR: u32 = 1 << 11; // Controller Not Ready + unsafe { + let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); + let op_base = xhci_base.as_ptr().add(caplength as usize); + + let usbcmd_ptr = op_base.add(0x00) as *mut u32; + let usbsts_ptr = op_base.add(0x04) as *const u32; + + let usbcmd = usbcmd_ptr.read_volatile(); + let usbsts = usbsts_ptr.read_volatile(); + info!("Before reset: USBCMD={:#010x}, USBSTS={:#010x}", usbcmd, usbsts); + + // Step 1: Halt the controller if not already halted + if (usbsts & STS_HALT) == 0 { + info!("Halting xHCI controller..."); + let cmd = usbcmd & !CMD_RUN; + usbcmd_ptr.write_volatile(cmd); + + // Wait for halt + for _ in 0..1000 { + let sts = usbsts_ptr.read_volatile(); + if (sts & STS_HALT) != 0 { + info!("xHCI halted"); + break; + } + for _ in 0..1000 { + core::hint::spin_loop(); + } + } + } + + // Step 2: Reset the controller (like U-Boot xhci_reset) + info!("Resetting xHCI controller (CMD_RESET)..."); + let cmd = usbcmd_ptr.read_volatile(); + usbcmd_ptr.write_volatile(cmd | CMD_RESET); + + // Wait for reset to complete (CMD_RESET bit clears) + for i in 0..1000 { + let cmd = usbcmd_ptr.read_volatile(); + if (cmd & CMD_RESET) == 0 { + info!("xHCI reset complete after {} iterations", i); + break; + } + for _ in 0..1000 { + core::hint::spin_loop(); + } + } + + // Step 3: Wait for Controller Not Ready to clear + for i in 0..1000 { + let sts = usbsts_ptr.read_volatile(); + if (sts & STS_CNR) == 0 { + info!("xHCI controller ready after {} iterations", i); + break; + } + for _ in 0..1000 { + core::hint::spin_loop(); + } + } + + let usbcmd_after_reset = usbcmd_ptr.read_volatile(); + let usbsts_after_reset = usbsts_ptr.read_volatile(); + info!("After reset: USBCMD={:#010x}, USBSTS={:#010x}", usbcmd_after_reset, usbsts_after_reset); + + // Check PHY PLL lock immediately after xHCI reset + let pma_base_check = iomap(0xfed98000.into(), 0x10000); + let lcpll_after_reset = (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr_after_reset = (pma_base_check.as_ptr().add(0x0B84) as *const u32).read_volatile(); + info!("PHY PLL after xHCI reset: LCPLL={:#04x} CDR={:#04x} (AFC={}, LOCK={})", + lcpll_after_reset, cdr_after_reset, + (lcpll_after_reset >> 6) & 1, (lcpll_after_reset >> 7) & 1); + + let hcsparams1 = (xhci_base.as_ptr().add(0x04) as *const u32).read_volatile(); + let max_ports = ((hcsparams1 >> 24) & 0xff) as usize; + info!("xHCI has {} ports, powering on all ports...", max_ports); + + const PORT_POWER: u32 = 1 << 9; + for i in 0..max_ports { + let portsc_offset = 0x400 + i * 0x10; + let portsc_ptr = op_base.add(portsc_offset) as *mut u32; + let portsc = portsc_ptr.read_volatile(); + info!("Port {} PORTSC before power: {:#010x} (PP={})", i, portsc, (portsc >> 9) & 1); + + if (portsc & PORT_POWER) == 0 { + let new_portsc = portsc | PORT_POWER; + portsc_ptr.write_volatile(new_portsc); + info!("Port {} power enabled", i); + } + } + + spin_delay_ms(20); + + for i in 0..max_ports { + let portsc_offset = 0x400 + i * 0x10; + let portsc_ptr = op_base.add(portsc_offset) as *const u32; + let portsc = portsc_ptr.read_volatile(); + info!("Port {} PORTSC after power: {:#010x} (PP={})", i, portsc, (portsc >> 9) & 1); + } + + info!("Starting xHCI controller (CMD_RUN)..."); + let cmd = usbcmd_ptr.read_volatile(); + usbcmd_ptr.write_volatile(cmd | CMD_RUN); + + // Wait for STS_HALT to clear + for i in 0..100 { + let sts = usbsts_ptr.read_volatile(); + if (sts & STS_HALT) == 0 { + info!("xHCI running after {} iterations! USBSTS={:#010x}", i, sts); + break; + } + for _ in 0..10000 { + core::hint::spin_loop(); + } + } + + let usbcmd_after = usbcmd_ptr.read_volatile(); + let usbsts_after = usbsts_ptr.read_volatile(); + info!("After start: USBCMD={:#010x}, USBSTS={:#010x}", usbcmd_after, usbsts_after); + } + } + + fn init_dwc3_full() { + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; + + unsafe { + let read_dwc3 = |off: usize| -> u32 { + (dwc3_base.add(off) as *const u32).read_volatile() + }; + let write_dwc3 = |off: usize, val: u32| { + (dwc3_base.add(off) as *mut u32).write_volatile(val); + }; + + info!("DWC3 init: GCTL before = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 init: GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 init: GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + + let mut gctl = read_dwc3(GCTL); + gctl |= GCTL_CORESOFTRESET; + write_dwc3(GCTL, gctl); + + let mut pipe = read_dwc3(GUSB3PIPECTL); + pipe |= GUSB3PIPECTL_PHYSOFTRST; + write_dwc3(GUSB3PIPECTL, pipe); + + let mut phycfg = read_dwc3(GUSB2PHYCFG); + phycfg |= GUSB2PHYCFG_PHYSOFTRST; + write_dwc3(GUSB2PHYCFG, phycfg); + + spin_delay_ms(100); + + pipe = read_dwc3(GUSB3PIPECTL); + pipe &= !GUSB3PIPECTL_PHYSOFTRST; + pipe &= !GUSB3PIPECTL_DEPOCHANGE; // RK3588 quirk: dis-del-phy-power-chg-quirk + pipe |= GUSB3PIPECTL_SUSPHY; + write_dwc3(GUSB3PIPECTL, pipe); + + spin_delay_ms(100); + + phycfg = read_dwc3(GUSB2PHYCFG); + phycfg &= !GUSB2PHYCFG_PHYSOFTRST; + phycfg &= !(GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_MASK); + phycfg |= GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_16BIT; + phycfg &= !GUSB2PHYCFG_ENBLSLPM; + phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; + phycfg |= GUSB2PHYCFG_SUSPHY; + write_dwc3(GUSB2PHYCFG, phycfg); + + spin_delay_ms(100); + + gctl = read_dwc3(GCTL); + gctl &= !GCTL_CORESOFTRESET; + write_dwc3(GCTL, gctl); + + gctl = read_dwc3(GCTL); + gctl &= !GCTL_PRTCAPDIR_MASK; + gctl |= GCTL_PRTCAPDIR_HOST; + write_dwc3(GCTL, gctl); + + info!("DWC3 init: GCTL after = {:#010x}", read_dwc3(GCTL)); + info!("DWC3 init: GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); + info!("DWC3 init: GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + } + info!("DWC3 full init complete"); } #[derive(Debug)] @@ -518,6 +1790,54 @@ mod tests { Ok(()) } + fn disable_vcc5v0_host(fdt: &Fdt<'static>) -> Result<(), GpioError> { + let Some(reg_node) = fdt + .all_nodes() + .find(|n| n.find_property("regulator-name").map(|p| p.str()) == Some("vcc5v0_host")) + else { + return Err(GpioError::NotFound); + }; + + let gpio_prop = reg_node + .find_property("gpio") + .or_else(|| reg_node.find_property("gpios")) + .ok_or(GpioError::NotFound)?; + + let mut vals = gpio_prop.u32_list(); + let ctrl = vals.next().ok_or(GpioError::NotFound)?; + let pin = vals.next().ok_or(GpioError::NotFound)?; + + let ctrl_node = fdt + .get_node_by_phandle(ctrl.into()) + .ok_or(GpioError::NotFound)?; + + let mut regs = ctrl_node.reg().ok_or(GpioError::RegMissing)?; + let reg = regs.next().ok_or(GpioError::RegMissing)?; + let base = iomap( + (reg.address as usize).into(), + reg.size.unwrap_or(0x1000).max(0x1000), + ); + + unsafe { + let dr = base.as_ptr() as *mut u32; + let ddr = dr.add(1); + + let mut dir = ddr.read_volatile(); + dir |= 1 << pin; + ddr.write_volatile(dir); + + let mut val = dr.read_volatile(); + val &= !(1 << pin); + dr.write_volatile(val); + } + + info!( + "vcc5v0_host DISABLED via gpio ctrl phandle 0x{:x}, pin {}", + ctrl, pin + ); + Ok(()) + } + #[derive(Debug)] enum PhyError { NotFound, @@ -530,73 +1850,576 @@ mod tests { RegMissing, } - /// 在 RK3588 上通过 USB2PHY_GRF_CON3 强制退出 suspend - /// 针对 OPi5+,usb2phy-grf 基地址 fd5d4000。 - fn force_usb2phy_active(fdt: &Fdt<'static>) -> Result<(), PhyError> { - // 找 usb2phy-grf syscon - let Some(grf_node) = fdt - .all_nodes() - .find(|n| n.compatibles().any(|c| c.contains("usb2phy-grf"))) - else { - return Err(PhyError::NotFound); - }; + fn init_usb2phy1_full(_fdt: &Fdt<'static>) -> Result<(), PhyError> { + // USB2PHY1_GRF syscon base (for bvalid control in CON2/CON4) + const USB2PHY1_GRF_SYSCON_BASE: usize = 0xfd5d4000; + // USB2PHY1 registers are at offset 0x4000 within syscon (from device tree: usb2-phy@4000) + const USB2PHY1_OFFSET: usize = 0x4000; + const USB2PHY1_GRF_SIZE: usize = 0x8000; - let mut regs = grf_node.reg().ok_or(PhyError::RegMissing)?; - let reg = regs.next().ok_or(PhyError::RegMissing)?; - let base = iomap( - (reg.address as usize).into(), - reg.size.unwrap_or(0x1000).max(0x1000), - ); + let grf_base = iomap(USB2PHY1_GRF_SYSCON_BASE.into(), USB2PHY1_GRF_SIZE); + let phy_base = unsafe { core::ptr::NonNull::new_unchecked(grf_base.as_ptr().add(USB2PHY1_OFFSET)) }; + let cru_base = iomap(CRU_BASE.into(), 0x10000); + + unsafe { + // PHY register access (at base + 0x4000) + let write_phy_reg = |offset: usize, mask: u32, val: u32| { + let ptr = phy_base.as_ptr().add(offset) as *mut u32; + ptr.write_volatile((mask << 16) | (val & mask)); + }; + + let read_phy_reg = |offset: usize| -> u32 { + let ptr = phy_base.as_ptr().add(offset) as *const u32; + ptr.read_volatile() + }; + + // GRF syscon register access (at base directly, for bvalid control) + let write_grf_reg = |offset: usize, mask: u32, val: u32| { + let ptr = grf_base.as_ptr().add(offset) as *mut u32; + ptr.write_volatile((mask << 16) | (val & mask)); + }; + + let read_grf_reg = |offset: usize| -> u32 { + let ptr = grf_base.as_ptr().add(offset) as *const u32; + ptr.read_volatile() + }; + + let assert_reset = |cru: core::ptr::NonNull, id: u32| { + let (offset, bit) = if id >= 0xC0_000 { + let rel = id - 0xC0_000; + (0x30a00 + (rel / 16) * 4, rel % 16) + } else { + (0xa00 + (id / 16) * 4, id % 16) + }; + let reg = cru.as_ptr().add(offset as usize) as *mut u32; + reg.write_volatile((1u32 << (bit + 16)) | (1u32 << bit)); + }; + + let deassert_reset = |cru: core::ptr::NonNull, id: u32| { + let (offset, bit) = if id >= 0xC0_000 { + let rel = id - 0xC0_000; + (0x30a00 + (rel / 16) * 4, rel % 16) + } else { + (0xa00 + (id / 16) * 4, id % 16) + }; + let reg = cru.as_ptr().add(offset as usize) as *mut u32; + reg.write_volatile(1u32 << (bit + 16)); + }; + + // Read PHY registers (at offset 0x4000) + info!("USB2PHY1 (phy): reg 0x0008 = {:#010x}", read_phy_reg(0x0008)); + info!("USB2PHY1 (phy): reg 0x000c = {:#010x}", read_phy_reg(0x000c)); + + // Read GRF syscon registers (at base, for bvalid) + info!("USB2PHY1 (grf): reg 0x0008 = {:#010x}", read_grf_reg(0x0008)); + info!("USB2PHY1 (grf): reg 0x0010 = {:#010x}", read_grf_reg(0x0010)); + + let siddq = read_phy_reg(0x0008); + if (siddq & (1 << 13)) != 0 { + info!("USB2PHY1: SIDDQ set, clearing to power on analog block"); + write_phy_reg(0x0008, 1 << 13, 0); + for _ in 0..50000 { + core::hint::spin_loop(); + } + + info!("USB2PHY1: Performing PHY reset after SIDDQ clear"); + assert_reset(cru_base, 0xc0048); + for _ in 0..10000 { + core::hint::spin_loop(); + } + deassert_reset(cru_base, 0xc0048); + for _ in 0..20000 { + core::hint::spin_loop(); + } + } + + let sus_before = read_phy_reg(0x000c); + info!("USB2PHY1: phy_sus (0x000c) before = {:#010x}, bit11 = {}", sus_before, (sus_before >> 11) & 1); + + write_phy_reg(0x000c, 1 << 11, 0); + + for _ in 0..200000 { + core::hint::spin_loop(); + } + + let sus_after = read_phy_reg(0x000c); + info!("USB2PHY1: phy_sus (0x000c) after = {:#010x}, bit11 = {}", sus_after, (sus_after >> 11) & 1); + + // Enable bvalid via software control in GRF syscon (NOT PHY regs!) + // This is CRITICAL for device detection in USBDP combo PHY! + // From U-Boot phy-rockchip-usbdp.c: + // bvalid_phy_con = { 0x0008, 1, 0, 0x2, 0x3 } - GRF CON2, bits 1-0 + // bvalid_grf_con = { 0x0010, 3, 2, 0x2, 0x3 } - GRF CON4, bits 3-2 + // + // CON2 bits 1-0: vbusvldextsel (bit 1) + vbusvldext (bit 0) + // 0x3 = use external VBUS valid indicator and assert it + // CON4 bits 3-2: sft_vbus_sel (bit 3) + sft_vbus (bit 2) + // 0x3 = use software VBUS control and assert VBUS valid + + info!("USB2PHY1: Enabling bvalid via software control in GRF..."); + + // GRF CON2 (0x0008): Set vbusvldextsel=1, vbusvldext=1 (bits 1:0 = 0x3) + let con2_before = read_grf_reg(0x0008); + write_grf_reg(0x0008, 0x3, 0x3); + let con2_after = read_grf_reg(0x0008); + info!("USB2PHY1 GRF CON2 (0x0008) bvalid: {:#010x} -> {:#010x}", con2_before, con2_after); + + // GRF CON4 (0x0010): Set sft_vbus_sel=1, sft_vbus=1 (bits 3:2 = 0xC) + let con4_before = read_grf_reg(0x0010); + write_grf_reg(0x0010, 0xC, 0xC); + let con4_after = read_grf_reg(0x0010); + info!("USB2PHY1 GRF CON4 (0x0010) bvalid: {:#010x} -> {:#010x}", con4_before, con4_after); + + // Wait for UTMI clock to stabilize after bvalid assertion + for _ in 0..200000 { + core::hint::spin_loop(); + } + + // Read STATUS0 to check actual PHY status + let status0 = read_grf_reg(0x00C0); + let utmi_bvalid = (status0 >> 6) & 1; + let utmi_linestate = (status0 >> 9) & 0x3; + let utmi_vbusvalid = (status0 >> 8) & 1; + info!("USB2PHY1 STATUS0 (grf base): {:#010x} (bvalid={}, linestate={:02b}, vbusvalid={})", + status0, utmi_bvalid, utmi_linestate, utmi_vbusvalid); + + // Also read STATUS0 at PHY offset (0x4000 + 0xC0) + let phy_status0 = read_phy_reg(0x00C0); + let phy_bvalid = (phy_status0 >> 6) & 1; + let phy_linestate = (phy_status0 >> 9) & 0x3; + let phy_vbusvalid = (phy_status0 >> 8) & 1; + info!("USB2PHY1 STATUS0 (phy offset): {:#010x} (bvalid={}, linestate={:02b}, vbusvalid={})", + phy_status0, phy_bvalid, phy_linestate, phy_vbusvalid); + } + + info!("usb2phy1 fully initialized with bvalid enabled"); + Ok(()) + } + fn force_usbdp_phy_active(_fdt: &Fdt<'static>) -> Result<(), PhyError> { + const USBDPPHY1_GRF_SIZE: usize = 0x4000; + + let base = iomap(USBDPPHY1_GRF_BASE.into(), USBDPPHY1_GRF_SIZE); unsafe { - // USB2PHY_GRF_CON3 offset 0x0C - // 写使能+数据:bit11(choose override)=1,bit12(suspendm)=1 - let val: u32 = (((1 << 11) | (1 << 12)) << 16) | (1 << 11) | (1 << 12); - let ptr = base.as_ptr().add(0x0C) as *mut u32; + // USBDPPHY_GRF_CON1 offset 0x04: low_pwrn (bit 13), rx_lfps (bit 14) + let val: u32 = (((1 << 13) | (1 << 14)) << 16) | (1 << 13) | (1 << 14); + let ptr = base.as_ptr().add(0x04) as *mut u32; ptr.write_volatile(val); } info!( - "usb2phy-grf {} active (CON3 suspend override set)", - grf_node.name() + "usbdpphy1-grf @ 0x{:08x} active (CON1 low_pwrn/rx_lfps set)", + USBDPPHY1_GRF_BASE ); Ok(()) } - /// 通过 USBDPPHY_GRF_CON3 解除 powerdown / 打开 RX Termination - /// 针对 OPi5+,usbdpphy-grf 基地址 fd5cc000(USB3 PHY1,与 usb@fc400000 配套)。 - fn force_usbdp_phy_active(fdt: &Fdt<'static>) -> Result<(), PhyError> { - let Some(grf_node) = fdt - .all_nodes() - .find(|n| n.compatibles().any(|c| c.contains("usbdpphy-grf"))) - else { - return Err(PhyError::NotFound); + + /// Full USBDP PHY1 initialization for USB3_1 (Port 1) + /// This follows the U-Boot rk3588_udphy_init sequence from phy-rockchip-usbdp.c + fn init_usbdp_phy1_full(_fdt: &Fdt<'static>) -> Result<(), PhyError> { + info!("=== Starting full USBDP PHY1 initialization ==="); + + + let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); + let udphygrf_base = iomap(USBDPPHY1_GRF_BASE.into(), 0x4000); + let usbgrf_base = iomap(USB_GRF_BASE.into(), 0x1000); + let cru_base = iomap(CRU_BASE.into(), 0x10000); + + info!("PHY1 PMA mapped at {:?}", pma_base); + info!("USBDPPHY1_GRF mapped at {:?}", udphygrf_base); + info!("USB_GRF mapped at {:?}", usbgrf_base); + info!("CRU mapped at {:?}", cru_base); + let phy_already_initialized = unsafe { + let lcpll = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); + let locked = (lcpll & 0xC0) == 0xC0 && (cdr & 0x0F) != 0; + info!("PHY lock check: LCPLL={:#04x} CDR={:#04x} already_init={}", lcpll, cdr, locked); + locked }; + if phy_already_initialized { + info!("PHY already initialized by bootloader, skipping full PHY init"); + info!("Only configuring USB_GRF to enable USB3 port..."); + + unsafe { + let con1_ptr = usbgrf_base.as_ptr().add(0x34) as *mut u32; + let old_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); + // Write 0x1100 to enable USB3: host_num_u3_port=1, host_num_u2_port=1 + con1_ptr.write_volatile((0xFFFF << 16) | 0x1100); + let new_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); + info!("USB3OTG1_CON1: {:#010x} -> {:#010x} (USB3 port enabled)", old_con1, new_con1); + + let grf_con1_ptr = udphygrf_base.as_ptr().add(0x04) as *mut u32; + let old_grf = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); + grf_con1_ptr.write_volatile(old_grf | (0x3 << 29) | (0x3 << 13)); + let new_grf = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); + info!("USBDPPHY1_GRF CON1: {:#010x} -> {:#010x} (rx_lfps, low_pwrn enabled)", old_grf, new_grf); + } + + info!("=== USBDP PHY1 quick config complete (bootloader-init path) ==="); + return Ok(()); + } - let mut regs = grf_node.reg().ok_or(PhyError::RegMissing)?; - let reg = regs.next().ok_or(PhyError::RegMissing)?; - let base = iomap( - (reg.address as usize).into(), - reg.size.unwrap_or(0x1000).max(0x1000), - ); + let deassert_phy_reset = |cru: core::ptr::NonNull, id: u32| { + let idx = id / 16; + let bit = id % 16; + let offset = 0xa00 + idx * 4; + unsafe { + let reg = cru.as_ptr().add(offset as usize) as *mut u32; + // Write 1 to bit+16 (write enable) and 0 to bit (deassert) + reg.write_volatile(1u32 << (bit + 16)); + } + }; + + let assert_phy_reset = |cru: core::ptr::NonNull, id: u32| { + let idx = id / 16; + let bit = id % 16; + let offset = 0xa00 + idx * 4; + unsafe { + let reg = cru.as_ptr().add(offset as usize) as *mut u32; + // Write 1 to bit+16 (write enable) and 1 to bit (assert reset) + reg.write_volatile((1u32 << (bit + 16)) | (1u32 << bit)); + } + }; unsafe { - // USBDPPHY_GRF_CON3 offset 0x0C - // bit0: override enable =1 - // bit4:3 powerdown=0 - // bit2 tx_elecidle=0 (keep driving) - // bit1 rx_termination=1 (enable) - // 写掩码在高 16 位 - let data: u32 = (1 << 0) | (1 << 1); // override + rx_term on, others 0 - let wen: u32 = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); - let val = (wen << 16) | data; - let ptr = base.as_ptr().add(0x0C) as *mut u32; + let softrst_con02 = (cru_base.as_ptr().add(0xa08) as *const u32).read_volatile(); + let softrst_con03 = (cru_base.as_ptr().add(0xa0c) as *const u32).read_volatile(); + let softrst_con72 = (cru_base.as_ptr().add(0xb20) as *const u32).read_volatile(); + info!("CRU reset state: CON02={:#010x} CON03={:#010x} CON72={:#010x}", + softrst_con02, softrst_con03, softrst_con72); + + let init_reset = (softrst_con02 >> 15) & 1; + let cmn_reset = (softrst_con03 >> 0) & 1; + let lane_reset = (softrst_con03 >> 1) & 1; + let pcs_reset = (softrst_con03 >> 2) & 1; + let pma_apb_reset = (softrst_con72 >> 4) & 1; + info!("PHY1 reset bits: init={} cmn={} lane={} pcs={} pma_apb={}", + init_reset, cmn_reset, lane_reset, pcs_reset, pma_apb_reset); + + let gate_con02 = (cru_base.as_ptr().add(0x0808) as *const u32).read_volatile(); + let gate_con72 = (cru_base.as_ptr().add(0x0920) as *const u32).read_volatile(); + info!("Clock gates: GATE_CON02={:#010x} (IMMORTAL1 bit15={}) GATE_CON72={:#010x} (PCLK_PHY1 bit4={})", + gate_con02, (gate_con02 >> 15) & 1, gate_con72, (gate_con72 >> 4) & 1); + + let lane_mux = (pma_base.as_ptr().add(0x0288) as *const u32).read_volatile(); + let lcpll = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); + info!("PHY state before init: LANE_MUX={:#04x} LCPLL={:#04x} CDR={:#04x}", + lane_mux, lcpll, cdr); + } + info!("Enabling PHY clocks: USBDP_PHY1_IMMORTAL, PCLK_USBDPPHY1"); + unsafe { + let gate_con02_ptr = cru_base.as_ptr().add(0x0808) as *mut u32; + gate_con02_ptr.write_volatile((1u32 << 31) | (0u32 << 15)); + + let gate_con72_ptr = cru_base.as_ptr().add(0x0920) as *mut u32; + gate_con72_ptr.write_volatile((1u32 << 20) | (0u32 << 4)); + + let gate_con02_after = (cru_base.as_ptr().add(0x0808) as *const u32).read_volatile(); + let gate_con72_after = (cru_base.as_ptr().add(0x0920) as *const u32).read_volatile(); + info!("After clock enable: GATE_CON02={:#010x} GATE_CON72={:#010x}", gate_con02_after, gate_con72_after); + } + // CRITICAL: Disable USB3 port BEFORE PHY init (like U-Boot/Linux) + // This ensures the port doesn't try to use the PHY until it's fully initialized + // Write 0x0188 to set host_num_u3_port=0 (xHCI sees no USB3 ports) + info!("Step 0a: DISABLE USB3 port BEFORE PHY init (CON1 = 0x0188)"); + unsafe { + let con1_ptr = usbgrf_base.as_ptr().add(0x34) as *mut u32; + let old_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); + con1_ptr.write_volatile((0xFFFF << 16) | 0x0188); + let new_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); + info!("USB3OTG1_CON1: {:#010x} -> {:#010x} (USB3 port DISABLED for PHY init)", old_con1, new_con1); + } + info!("Step 0b: Asserting ALL PHY resets"); + assert_phy_reset(cru_base, 47); // init + assert_phy_reset(cru_base, 48); // cmn + assert_phy_reset(cru_base, 49); // lane + assert_phy_reset(cru_base, 50); // pcs_apb + assert_phy_reset(cru_base, 1156); // pma_apb + for _ in 0..10000 { + core::hint::spin_loop(); + } + unsafe { + let softrst_con02 = (cru_base.as_ptr().add(0xa08) as *const u32).read_volatile(); + let softrst_con03 = (cru_base.as_ptr().add(0xa0c) as *const u32).read_volatile(); + let softrst_con72 = (cru_base.as_ptr().add(0xb20) as *const u32).read_volatile(); + info!("After assert: CON02={:#010x} CON03={:#010x} CON72={:#010x}", + softrst_con02, softrst_con03, softrst_con72); + } + + info!("Step 1: Enable rx_lfps for USB mode"); + unsafe { + let ptr = udphygrf_base.as_ptr().add(0x04) as *mut u32; + let val: u32 = ((1 << 14) << 16) | (1 << 14); ptr.write_volatile(val); } - info!( - "usbdpphy-grf {} active (CON3 override/powerdown cleared)", - grf_node.name() - ); + info!("Step 2: Power on PMA (low_pwrn=1)"); + unsafe { + let ptr = udphygrf_base.as_ptr().add(0x04) as *mut u32; + let val: u32 = ((1 << 13) << 16) | (1 << 13); + ptr.write_volatile(val); + } + + info!("Step 3: Deassert pma_apb and pcs_apb resets"); + deassert_phy_reset(cru_base, 1156); + deassert_phy_reset(cru_base, 50); + + // Step 4: Write init sequence to PMA + // From U-Boot rk3588_udphy_init_sequence + const INIT_SEQUENCE: &[(u16, u8)] = &[ + (0x0104, 0x44), (0x0234, 0xE8), + (0x0248, 0x44), (0x028C, 0x18), + (0x081C, 0xE5), (0x0878, 0x00), + (0x0994, 0x1C), (0x0AF0, 0x00), + (0x181C, 0xE5), (0x1878, 0x00), + (0x1994, 0x1C), (0x1AF0, 0x00), + (0x0428, 0x60), (0x0D58, 0x33), + (0x1D58, 0x33), (0x0990, 0x74), + (0x0D64, 0x17), (0x08C8, 0x13), + (0x1990, 0x74), (0x1D64, 0x17), + (0x18C8, 0x13), (0x0D90, 0x40), + (0x0DA8, 0x40), (0x0DC0, 0x40), + (0x0DD8, 0x40), (0x1D90, 0x40), + (0x1DA8, 0x40), (0x1DC0, 0x40), + (0x1DD8, 0x40), (0x03C0, 0x30), + (0x03C4, 0x06), (0x0E10, 0x00), + (0x1E10, 0x00), (0x043C, 0x0F), + (0x0D2C, 0xFF), (0x1D2C, 0xFF), + (0x0D34, 0x0F), (0x1D34, 0x0F), + (0x08FC, 0x2A), (0x0914, 0x28), + (0x0A30, 0x03), (0x0E38, 0x05), + (0x0ECC, 0x27), (0x0ED0, 0x22), + (0x0ED4, 0x26), (0x18FC, 0x2A), + (0x1914, 0x28), (0x1A30, 0x03), + (0x1E38, 0x05), (0x1ECC, 0x27), + (0x1ED0, 0x22), (0x1ED4, 0x26), + (0x0048, 0x0F), (0x0060, 0x3C), + (0x0064, 0xF7), (0x006C, 0x20), + (0x0070, 0x7D), (0x0074, 0x68), + (0x0AF4, 0x1A), (0x1AF4, 0x1A), + (0x0440, 0x3F), (0x10D4, 0x08), + (0x20D4, 0x08), (0x00D4, 0x30), + (0x0024, 0x6e), + ]; + + unsafe { + for &(offset, value) in INIT_SEQUENCE { + let ptr = pma_base.as_ptr().add(offset as usize) as *mut u32; + ptr.write_volatile(value as u32); + } + } + info!("Step 4: Init sequence written ({} registers)", INIT_SEQUENCE.len()); + + // Step 5: Write 24MHz reference clock configuration + const REFCLK_CFG: &[(u16, u8)] = &[ + (0x0090, 0x68), (0x0094, 0x68), + (0x0128, 0x24), (0x012c, 0x44), + (0x0130, 0x3f), (0x0134, 0x44), + (0x015c, 0xa9), (0x0160, 0x71), + (0x0164, 0x71), (0x0168, 0xa9), + (0x0174, 0xa9), (0x0178, 0x71), + (0x017c, 0x71), (0x0180, 0xa9), + (0x018c, 0x41), (0x0190, 0x00), + (0x0194, 0x05), (0x01ac, 0x2a), + (0x01b0, 0x17), (0x01b4, 0x17), + (0x01b8, 0x2a), (0x01c8, 0x04), + (0x01cc, 0x08), (0x01d0, 0x08), + (0x01d4, 0x04), (0x01d8, 0x20), + (0x01dc, 0x01), (0x01e0, 0x09), + (0x01e4, 0x03), (0x01f0, 0x29), + (0x01f4, 0x02), (0x01f8, 0x02), + (0x01fc, 0x29), (0x0208, 0x2a), + (0x020c, 0x17), (0x0210, 0x17), + (0x0214, 0x2a), (0x0224, 0x20), + (0x03f0, 0x0d), (0x03f4, 0x09), + (0x03f8, 0x09), (0x03fc, 0x0d), + (0x0404, 0x0e), (0x0408, 0x14), + (0x040c, 0x14), (0x0410, 0x3b), + (0x0ce0, 0x68), (0x0ce8, 0xd0), + (0x0cf0, 0x87), (0x0cf8, 0x70), + (0x0d00, 0x70), (0x0d08, 0xa9), + (0x1ce0, 0x68), (0x1ce8, 0xd0), + (0x1cf0, 0x87), (0x1cf8, 0x70), + (0x1d00, 0x70), (0x1d08, 0xa9), + (0x0a3c, 0xd0), (0x0a44, 0xd0), + (0x0a48, 0x01), (0x0a4c, 0x0d), + (0x0a54, 0xe0), (0x0a5c, 0xe0), + (0x0a64, 0xa8), (0x1a3c, 0xd0), + (0x1a44, 0xd0), (0x1a48, 0x01), + (0x1a4c, 0x0d), (0x1a54, 0xe0), + (0x1a5c, 0xe0), (0x1a64, 0xa8), + ]; + + unsafe { + for &(offset, value) in REFCLK_CFG { + let ptr = pma_base.as_ptr().add(offset as usize) as *mut u32; + ptr.write_volatile(value as u32); + } + } + info!("Step 5: Refclk config written ({} registers)", REFCLK_CFG.len()); + + // Step 6: Configure lane mux - ALL lanes USB mode (matching U-Boot working config) + // Note: DTS has rockchip,dp-lane-mux = <2 3> but U-Boot's usb start uses 0x00 (all USB) + // The GL3523 hub is connected to USB lanes, not DP lanes + info!("Step 6: Configure lane mux - ALL lanes USB mode (match U-Boot)"); + unsafe { + let ptr = pma_base.as_ptr().add(0x0288) as *mut u32; + let current = ptr.read_volatile(); + let mask: u32 = 0xFF; + let value: u32 = 0x00; + let new_val = (current & !mask) | (value & mask); + ptr.write_volatile(new_val); + info!("Lane mux: current=0x{:02x}, new=0x{:02x} (all USB)", current, new_val); + } + + info!("Step 7: Deassert init reset (USB mode)"); + deassert_phy_reset(cru_base, 47); + + for _ in 0..1000 { + core::hint::spin_loop(); + } + info!("Step 8: Deassert cmn and lane resets (USB mode)"); + deassert_phy_reset(cru_base, 48); + deassert_phy_reset(cru_base, 49); + + info!("Step 9: Waiting for LCPLL lock..."); + let mut timeout = 500; // 500 iterations + loop { + let val = unsafe { + let ptr = pma_base.as_ptr().add(0x0350) as *const u32; + ptr.read_volatile() + }; + let afc_done = (val & (1 << 6)) != 0; + let lock_done = (val & (1 << 7)) != 0; + if afc_done && lock_done { + info!("LCPLL locked! val=0x{:02x}", val); + break; + } + timeout -= 1; + if timeout == 0 { + warn!("LCPLL lock timeout! val=0x{:02x} (AFC={}, LOCK={})", val, afc_done, lock_done); + break; + } + for _ in 0..10000 { + core::hint::spin_loop(); + } + } + + // Step 7: Wait for CDR lock (Lane 0 for non-flipped) + // TRSV_LN0_MON_RX_CDR_DONE offset 0x0B84, LOCK_DONE = bit 0 + info!("Step 10: Waiting for CDR lock..."); + timeout = 500; + loop { + let val = unsafe { + let ptr = pma_base.as_ptr().add(0x0B84) as *const u32; + ptr.read_volatile() + }; + if (val & 1) != 0 { + info!("CDR locked! val=0x{:02x}", val); + break; + } + timeout -= 1; + if timeout == 0 { + warn!("CDR lock timeout! val=0x{:02x} - continuing anyway", val); + break; + } + for _ in 0..10000 { + core::hint::spin_loop(); + } + } + + // Step 10: Read USB_GRF_USB3OTG1_CON0 (offset 0x0030) - don't modify + unsafe { + let ptr = usbgrf_base.as_ptr().add(0x0030) as *const u32; + let con0 = ptr.read_volatile(); + info!("Step 10: USB3OTG1_CON0: {:#010x} (bus_filter_bypass={:#x}) - NOT modifying", con0, con0 & 0xF); + } + + // Step 11: ALWAYS enable USB3 after PHY init completes + // Write 0x1100: host_num_u3_port=1, host_num_u2_port=1 (USB3 mode) + unsafe { + let ptr = usbgrf_base.as_ptr().add(0x0034) as *mut u32; + let current_con1 = (usbgrf_base.as_ptr().add(0x0034) as *const u32).read_volatile(); + let val: u32 = (0xFFFF << 16) | 0x1100; + ptr.write_volatile(val); + let con1_after = (usbgrf_base.as_ptr().add(0x0034) as *const u32).read_volatile(); + info!("Step 11: ENABLE USB3 port (CON1: {:#010x} -> {:#010x})", current_con1, con1_after); + } + + info!("=== USBDP PHY1 initialization complete ==="); + Ok(()) + } + + fn reinit_usbdp_phy1_after_reset(_fdt: &Fdt<'static>) -> Result<(), PhyError> { + info!("=== Re-initializing USBDP PHY1 after DWC3 soft reset ==="); + + + let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); + let udphygrf_base = iomap(USBDPPHY1_GRF_BASE.into(), 0x4000); + let cru_base = iomap(CRU_BASE.into(), 0x10000); + + let deassert_phy_reset = |cru: core::ptr::NonNull, id: u32| { + let idx = id / 16; + let bit = id % 16; + let offset = 0xa00 + idx * 4; + unsafe { + let reg = cru.as_ptr().add(offset as usize) as *mut u32; + reg.write_volatile(1u32 << (bit + 16)); + } + }; + + unsafe { + let ptr = udphygrf_base.as_ptr().add(0x04) as *mut u32; + let val: u32 = ((1 << 14) << 16) | (1 << 14); + ptr.write_volatile(val); + } + info!("Re-enabled rx_lfps"); + + unsafe { + let ptr = udphygrf_base.as_ptr().add(0x04) as *mut u32; + let val: u32 = ((1 << 13) << 16) | (1 << 13); + ptr.write_volatile(val); + } + info!("Re-enabled low_pwrn"); + + deassert_phy_reset(cru_base, 1156); + deassert_phy_reset(cru_base, 50); + deassert_phy_reset(cru_base, 47); + deassert_phy_reset(cru_base, 48); + deassert_phy_reset(cru_base, 49); + info!("Deasserted PHY resets"); + + info!("Waiting for PHY to re-lock..."); + let mut timeout = 1000; + loop { + let val = unsafe { + let ptr = pma_base.as_ptr().add(0x0350) as *const u32; + ptr.read_volatile() + }; + let afc_done = (val & (1 << 6)) != 0; + let lock_done = (val & (1 << 7)) != 0; + if afc_done && lock_done { + info!("PHY re-locked! LCPLL=0x{:02x}", val); + break; + } + timeout -= 1; + if timeout == 0 { + warn!("PHY re-lock timeout! LCPLL=0x{:02x}", val); + break; + } + for _ in 0..10000 { + core::hint::spin_loop(); + } + } + + let cdr_val = unsafe { + let ptr = pma_base.as_ptr().add(0x0B84) as *const u32; + ptr.read_volatile() + }; + info!("CDR status: 0x{:02x}", cdr_val); + + info!("=== USBDP PHY1 re-initialization complete ==="); Ok(()) } @@ -677,6 +2500,37 @@ mod tests { reg.write_volatile(1u32 << (bit + 16)); } } + + /// Toggle VBUS power with configurable timing + /// Returns true if device connected after toggle + fn try_vbus_gpio_toggle_timed(off_ms: u32, on_wait_ms: u32) -> bool { + let gpio3_base = iomap(GPIO3_BASE.into(), 0x1000); + let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); + unsafe { + // Ensure GPIO3_B7 is configured as output + let ddr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DDR_L) as *mut u32; + ddr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); + + // Turn OFF VBUS + let dr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *mut u32; + dr_ptr.write_volatile(WRITE_MASK_BIT15 | 0); + + spin_delay_ms(off_ms); + + // Turn ON VBUS + dr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); + + spin_delay_ms(on_wait_ms); + + // Check if device connected + let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); + let op_base = xhci_base.as_ptr().add(caplength as usize); + let portsc = (op_base.add(0x410) as *const u32).read_volatile(); // Port 1 (USB3) + let ccs = portsc & 1; + ccs == 1 + } + } + } trait Align { From f674fd5c5f42aee80f64280af9d799745de12841 Mon Sep 17 00:00:00 2001 From: seven_bear <26707756+yueneiqi@users.noreply.github.com> Date: Sun, 11 Jan 2026 22:49:19 +0800 Subject: [PATCH 4/4] refactor(rk3588): remove dead code from PHY and DWC3 modules - dwc3.rs: keep only is_dwc3_xhci(), remove unused Dwc3 struct and init code - rk3588_phy.rs: keep only VBUS toggle functions, remove unused USBDP PHY code - root.rs: remove 4 unused port speed helper methods - delay.rs: remove unused delay_us(), keep only delay_ms() --- usb-host/src/backend/xhci/delay.rs | 9 - usb-host/src/backend/xhci/dwc3.rs | 422 +------- usb-host/src/backend/xhci/mod.rs | 23 +- usb-host/src/backend/xhci/rk3588_phy.rs | 380 +------- usb-host/src/backend/xhci/root.rs | 75 -- usb-host/tests/test.rs | 1166 +++++++++++++++-------- 6 files changed, 825 insertions(+), 1250 deletions(-) diff --git a/usb-host/src/backend/xhci/delay.rs b/usb-host/src/backend/xhci/delay.rs index a367b1b..d2f78a7 100644 --- a/usb-host/src/backend/xhci/delay.rs +++ b/usb-host/src/backend/xhci/delay.rs @@ -1,12 +1,3 @@ -//! Spin delay utilities for hardware initialization (CPU-speed dependent). - -#[inline] -pub fn delay_us(us: u32) { - for _ in 0..(us * 100) { - core::hint::spin_loop(); - } -} - #[inline] pub fn delay_ms(ms: u32) { for _ in 0..(ms as u64 * 100_000) { diff --git a/usb-host/src/backend/xhci/dwc3.rs b/usb-host/src/backend/xhci/dwc3.rs index 26475a5..b71f08f 100644 --- a/usb-host/src/backend/xhci/dwc3.rs +++ b/usb-host/src/backend/xhci/dwc3.rs @@ -1,417 +1,19 @@ -//! DWC3 (DesignWare USB3 Controller) initialization module -//! -//! This module provides DWC3 core initialization required for XHCI controllers -//! based on Synopsys DesignWare USB3 DRD IP, such as those found in RK3588. -//! -//! The DWC3 registers are located at offset 0xC100 from the XHCI base address. -//! -//! Reference: U-Boot drivers/usb/host/xhci-dwc3.c - -#![allow(dead_code)] // Module contains complete API for future use +//! DWC3 (DesignWare USB3 Controller) detection for RK3588. use core::ptr::NonNull; -use log::{debug, warn}; - -use super::delay::delay_ms; - -/// DWC3 register offset from XHCI base address -pub const DWC3_REG_OFFSET: usize = 0xC100; - -/// DWC3 Global registers structure -/// Located at XHCI_BASE + 0xC100 -#[repr(C)] -pub struct Dwc3Regs { - pub g_sbuscfg0: u32, // 0x00 - pub g_sbuscfg1: u32, // 0x04 - pub g_txthrcfg: u32, // 0x08 - pub g_rxthrcfg: u32, // 0x0C - pub g_ctl: u32, // 0x10 - Global Control Register - _reserved1: u32, // 0x14 - pub g_sts: u32, // 0x18 - Global Status Register - _reserved2: u32, // 0x1C - pub g_snpsid: u32, // 0x20 - Synopsys ID Register - pub g_gpio: u32, // 0x24 - pub g_uid: u32, // 0x28 - pub g_uctl: u32, // 0x2C - pub g_buserraddr_lo: u32, // 0x30 - pub g_buserraddr_hi: u32, // 0x34 - pub g_prtbimap_lo: u32, // 0x38 - pub g_prtbimap_hi: u32, // 0x3C - pub g_hwparams0: u32, // 0x40 - pub g_hwparams1: u32, // 0x44 - pub g_hwparams2: u32, // 0x48 - pub g_hwparams3: u32, // 0x4C - pub g_hwparams4: u32, // 0x50 - pub g_hwparams5: u32, // 0x54 - pub g_hwparams6: u32, // 0x58 - pub g_hwparams7: u32, // 0x5C - pub g_dbgfifospace: u32, // 0x60 - pub g_dbgltssm: u32, // 0x64 - pub g_dbglnmcc: u32, // 0x68 - pub g_dbgbmu: u32, // 0x6C - pub g_dbglspmux: u32, // 0x70 - pub g_dbglsp: u32, // 0x74 - pub g_dbgepinfo0: u32, // 0x78 - pub g_dbgepinfo1: u32, // 0x7C - pub g_prtbimap_hs_lo: u32, // 0x80 - pub g_prtbimap_hs_hi: u32, // 0x84 - pub g_prtbimap_fs_lo: u32, // 0x88 - pub g_prtbimap_fs_hi: u32, // 0x8C - _reserved3: [u32; 28], // 0x90-0xFF - pub g_usb2phycfg: [u32; 16], // 0x100 - USB2 PHY Configuration - pub g_usb2i2cctl: [u32; 16], // 0x140 - pub g_usb2phyacc: [u32; 16], // 0x180 - pub g_usb3pipectl: [u32; 16], // 0x1C0 - USB3 PIPE Control - pub g_txfifosiz: [u32; 32], // 0x200 - pub g_rxfifosiz: [u32; 32], // 0x280 - // ... more registers follow -} - -// DWC3 Synopsys ID masks -pub const DWC3_GSNPSID_MASK: u32 = 0xFFFF0000; -pub const DWC3_GSNPSID_CORE_3: u32 = 0x55330000; -pub const DWC3_GSNPSID_CORE_31: u32 = 0x33310000; -// DWC3 Global Control Register (GCTL) bits -pub const DWC3_GCTL_PWRDNSCALE_MASK: u32 = 0xFFF80000; -pub const DWC3_GCTL_PWRDNSCALE_SHIFT: u32 = 19; -pub const DWC3_GCTL_U2RSTECN: u32 = 1 << 16; -pub const DWC3_GCTL_RAMCLKSEL_MASK: u32 = 3 << 6; -pub const DWC3_GCTL_PRTCAPDIR_MASK: u32 = 3 << 12; -pub const DWC3_GCTL_PRTCAPDIR_HOST: u32 = 1 << 12; -pub const DWC3_GCTL_PRTCAPDIR_DEVICE: u32 = 2 << 12; -pub const DWC3_GCTL_PRTCAPDIR_OTG: u32 = 3 << 12; -pub const DWC3_GCTL_CORESOFTRESET: u32 = 1 << 11; -pub const DWC3_GCTL_SCALEDOWN_MASK: u32 = 3 << 4; -pub const DWC3_GCTL_DISSCRAMBLE: u32 = 1 << 3; -pub const DWC3_GCTL_DSBLCLKGTNG: u32 = 1 << 0; - -// DWC3 Global Hardware Params 1 (GHWPARAMS1) -pub const DWC3_GHWPARAMS1_EN_PWROPT_MASK: u32 = 3 << 24; -pub const DWC3_GHWPARAMS1_EN_PWROPT_NO: u32 = 0; -pub const DWC3_GHWPARAMS1_EN_PWROPT_CLK: u32 = 1 << 24; - -// DWC3 USB2 PHY Configuration Register (GUSB2PHYCFG) bits -pub const DWC3_GUSB2PHYCFG_PHYSOFTRST: u32 = 1 << 31; -pub const DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS: u32 = 1 << 30; -pub const DWC3_GUSB2PHYCFG_ENBLSLPM: u32 = 1 << 8; -pub const DWC3_GUSB2PHYCFG_SUSPHY: u32 = 1 << 6; -pub const DWC3_GUSB2PHYCFG_PHYIF: u32 = 1 << 3; -pub const DWC3_GUSB2PHYCFG_USBTRDTIM_MASK: u32 = 0xF << 10; -pub const DWC3_GUSB2PHYCFG_USBTRDTIM_16BIT: u32 = 0x5 << 10; -pub const DWC3_GUSB2PHYCFG_USBTRDTIM_8BIT: u32 = 0x9 << 10; - -// DWC3 USB3 PIPE Control Register (GUSB3PIPECTL) bits -pub const DWC3_GUSB3PIPECTL_PHYSOFTRST: u32 = 1 << 31; -pub const DWC3_GUSB3PIPECTL_DISRXDETP3: u32 = 1 << 28; -pub const DWC3_GUSB3PIPECTL_SUSPHY: u32 = 1 << 17; - -// DWC3 revision mask -pub const DWC3_REVISION_MASK: u32 = 0xFFFF; - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Dwc3Mode { - Host, - Device, - Otg, -} +const DWC3_REG_OFFSET: usize = 0xC100; +const DWC3_GSNPSID_MASK: u32 = 0xFFFF0000; +const DWC3_GSNPSID_CORE_3: u32 = 0x55330000; +const DWC3_GSNPSID_CORE_31: u32 = 0x33310000; -#[derive(Debug, Clone, Default)] -pub struct Dwc3Quirks { - pub dis_enblslpm: bool, - pub dis_u2_freeclk_exists: bool, - pub dis_u2_susphy: bool, - pub utmi_wide: bool, -} - -impl Dwc3Quirks { - pub fn rk3588_default() -> Self { - Self { - dis_enblslpm: true, - dis_u2_freeclk_exists: true, - dis_u2_susphy: false, - utmi_wide: true, - } - } -} - -pub struct Dwc3 { - regs: NonNull, -} - -unsafe impl Send for Dwc3 {} - -impl Dwc3 { - /// Create a new DWC3 instance from XHCI base address - /// - /// # Safety - /// The caller must ensure the XHCI base address is valid and properly mapped. - pub unsafe fn from_xhci_base(xhci_base: NonNull) -> Self { - unsafe { - let dwc3_addr = xhci_base.as_ptr().add(DWC3_REG_OFFSET); - Self { - regs: NonNull::new_unchecked(dwc3_addr as *mut Dwc3Regs), - } - } - } - - /// Read a register - fn read_reg(&self, offset: usize) -> u32 { - unsafe { - let ptr = (self.regs.as_ptr() as *const u8).add(offset) as *const u32; - ptr.read_volatile() - } - } - - /// Write a register - fn write_reg(&self, offset: usize, val: u32) { - unsafe { - let ptr = (self.regs.as_ptr() as *mut u8).add(offset) as *mut u32; - ptr.write_volatile(val); - } - } - - /// Read GCTL register - fn read_gctl(&self) -> u32 { - self.read_reg(0x10) - } - - /// Write GCTL register - fn write_gctl(&self, val: u32) { - self.write_reg(0x10, val); - } - - /// Read GUSB2PHYCFG[0] register - fn read_gusb2phycfg(&self) -> u32 { - self.read_reg(0x100) - } - - /// Write GUSB2PHYCFG[0] register - fn write_gusb2phycfg(&self, val: u32) { - self.write_reg(0x100, val); - } - - /// Read GUSB3PIPECTL[0] register - fn read_gusb3pipectl(&self) -> u32 { - self.read_reg(0x1C0) - } - - /// Write GUSB3PIPECTL[0] register - fn write_gusb3pipectl(&self, val: u32) { - self.write_reg(0x1C0, val); - } - - /// Read GHWPARAMS1 register - fn read_ghwparams1(&self) -> u32 { - self.read_reg(0x44) - } - - /// Read Synopsys ID register - fn read_gsnpsid(&self) -> u32 { - self.read_reg(0x20) - } - - /// Verify this is a valid DWC3 core - pub fn verify_id(&self) -> bool { - let id = self.read_gsnpsid(); +/// # Safety +/// The caller must ensure the XHCI base address is valid and properly mapped. +pub unsafe fn is_dwc3_xhci(xhci_base: NonNull) -> bool { + unsafe { + let gsnpsid_addr = xhci_base.as_ptr().add(DWC3_REG_OFFSET + 0x20) as *const u32; + let id = gsnpsid_addr.read_volatile(); let masked = id & DWC3_GSNPSID_MASK; - - if masked == DWC3_GSNPSID_CORE_3 || masked == DWC3_GSNPSID_CORE_31 { - let revision = id & DWC3_REVISION_MASK; - debug!("DWC3 Core ID: {:#010x}, revision: {:#06x}", id, revision); - true - } else { - warn!("Not a DWC3 core: ID={:#010x}", id); - false - } + masked == DWC3_GSNPSID_CORE_3 || masked == DWC3_GSNPSID_CORE_31 } - - /// Perform PHY soft reset sequence - fn phy_reset(&self) { - debug!("DWC3: PHY reset sequence"); - - // NOTE: Skip USB3 PHY soft reset - this causes the SuperSpeed port - // to become invisible to xHCI on RK3588 when external USBDP PHY is used. - // The USBDP PHY is already initialized before DWC3 init. - // Only reset USB2 PHY. - - // Assert USB2 PHY reset - let mut phycfg = self.read_gusb2phycfg(); - phycfg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; - self.write_gusb2phycfg(phycfg); - - delay_ms(100); - - // Clear USB2 PHY reset - phycfg = self.read_gusb2phycfg(); - phycfg &= !DWC3_GUSB2PHYCFG_PHYSOFTRST; - self.write_gusb2phycfg(phycfg); - - debug!("DWC3: PHY reset complete (USB2 only)"); - } - - /// Perform core soft reset - fn core_soft_reset(&self) { - debug!("DWC3: Core soft reset"); - - // Put core in reset before resetting PHY - let mut gctl = self.read_gctl(); - gctl |= DWC3_GCTL_CORESOFTRESET; - self.write_gctl(gctl); - - // Reset USB3 and USB2 PHY - self.phy_reset(); - - // Wait 100ms for core reset - delay_ms(100); - - // Take core out of reset - gctl = self.read_gctl(); - gctl &= !DWC3_GCTL_CORESOFTRESET; - self.write_gctl(gctl); - - debug!("DWC3: Core soft reset complete"); - } - - /// Initialize DWC3 core - /// - /// This follows the U-Boot dwc3_core_init sequence: - /// 1. Verify DWC3 ID - /// 2. Perform core soft reset - /// 3. Configure power optimization - /// 4. Apply revision-specific workarounds - pub fn core_init(&self) -> Result<(), &'static str> { - if !self.verify_id() { - return Err("Not a DWC3 core"); - } - - // NOTE: Skip core soft reset on RK3588 with external USBDP PHY. - // The core soft reset causes the SuperSpeed port to become invisible - // to xHCI when the USBDP PHY has already been initialized. - // self.core_soft_reset(); - - let hwparams1 = self.read_ghwparams1(); - - let mut gctl = self.read_gctl(); - - gctl &= !DWC3_GCTL_SCALEDOWN_MASK; - gctl &= !DWC3_GCTL_DISSCRAMBLE; - - let pwropt = (hwparams1 & DWC3_GHWPARAMS1_EN_PWROPT_MASK) >> 24; - if pwropt == 1 { - gctl &= !DWC3_GCTL_DSBLCLKGTNG; - debug!("DWC3: Clock gating enabled"); - } - - let revision = self.read_gsnpsid() & DWC3_REVISION_MASK; - if revision < 0x190a { - gctl |= DWC3_GCTL_U2RSTECN; - debug!("DWC3: Applied U2RSTECN workaround for revision {:#x}", revision); - } - - self.write_gctl(gctl); - - debug!("DWC3: Core initialized"); - Ok(()) - } - - /// Configure USB2 PHY settings with quirks - pub fn configure_usb2_phy(&self, quirks: &Dwc3Quirks) { - let mut reg = self.read_gusb2phycfg(); - - // Configure UTMI interface width - if quirks.utmi_wide { - reg |= DWC3_GUSB2PHYCFG_PHYIF; - reg &= !DWC3_GUSB2PHYCFG_USBTRDTIM_MASK; - reg |= DWC3_GUSB2PHYCFG_USBTRDTIM_16BIT; - debug!("DWC3: USB2 PHY configured for UTMI wide (16-bit)"); - } - - // Apply quirks - if quirks.dis_enblslpm { - reg &= !DWC3_GUSB2PHYCFG_ENBLSLPM; - debug!("DWC3: Disabled ENBLSLPM"); - } - - if quirks.dis_u2_freeclk_exists { - reg &= !DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS; - debug!("DWC3: Disabled U2_FREECLK_EXISTS"); - } - - if quirks.dis_u2_susphy { - reg &= !DWC3_GUSB2PHYCFG_SUSPHY; - debug!("DWC3: Disabled U2 SUSPHY"); - } - - self.write_gusb2phycfg(reg); - } - - /// Set DWC3 operating mode (Host/Device/OTG) - pub fn set_mode(&self, mode: Dwc3Mode) { - let mut gctl = self.read_gctl(); - gctl &= !DWC3_GCTL_PRTCAPDIR_MASK; - - match mode { - Dwc3Mode::Host => { - gctl |= DWC3_GCTL_PRTCAPDIR_HOST; - debug!("DWC3: Set to Host mode"); - } - Dwc3Mode::Device => { - gctl |= DWC3_GCTL_PRTCAPDIR_DEVICE; - debug!("DWC3: Set to Device mode"); - } - Dwc3Mode::Otg => { - gctl |= DWC3_GCTL_PRTCAPDIR_OTG; - debug!("DWC3: Set to OTG mode"); - } - } - - self.write_gctl(gctl); - } - - /// Full initialization sequence for XHCI host mode - /// - /// This performs: - /// 1. Core initialization (soft reset, config) - /// 2. USB2 PHY configuration with quirks - /// 3. Set host mode - pub fn init_for_xhci_host(&self, quirks: &Dwc3Quirks) -> Result<(), &'static str> { - // Initialize core - self.core_init()?; - - // Configure USB2 PHY - self.configure_usb2_phy(quirks); - - // Set host mode - self.set_mode(Dwc3Mode::Host); - - debug!("DWC3: Initialized for XHCI host mode"); - Ok(()) - } -} - -pub unsafe fn init_dwc3_for_xhci(xhci_base: NonNull) -> Result<(), &'static str> { - let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; - let quirks = Dwc3Quirks::rk3588_default(); - dwc3.init_for_xhci_host(&quirks) -} - -pub unsafe fn is_dwc3_xhci(xhci_base: NonNull) -> bool { - let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; - dwc3.verify_id() -} - -pub unsafe fn read_gctl(xhci_base: NonNull) -> u32 { - let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; - dwc3.read_reg(0x10) -} - -pub unsafe fn read_gusb2phycfg(xhci_base: NonNull) -> u32 { - let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; - dwc3.read_reg(0x100) -} - -pub unsafe fn read_gusb3pipectl(xhci_base: NonNull) -> u32 { - let dwc3 = unsafe { Dwc3::from_xhci_base(xhci_base) }; - dwc3.read_reg(0x1C0) } diff --git a/usb-host/src/backend/xhci/mod.rs b/usb-host/src/backend/xhci/mod.rs index fc6c591..0804ec1 100644 --- a/usb-host/src/backend/xhci/mod.rs +++ b/usb-host/src/backend/xhci/mod.rs @@ -54,12 +54,12 @@ impl usb_if::host::Controller for Xhci { root_hub.init()?; self.root = Some(root_hub); self.root()?.wait_for_running().await; - + // GL3523 hub workaround: Toggle VBUS after xHCI is running // This resets the hub so it sees immediate host activity when it powers up. // Without this, the hub enters standby mode and doesn't respond to Rx.Detect. self.toggle_vbus_if_rk3588().await; - + self.root()?.lock().enable_irq(); self.root()?.lock().reset_ports(); sleep(Duration::from_millis(100)).await; @@ -134,9 +134,9 @@ impl Xhci { debug!("Detected DWC3-based XHCI controller"); } } - + /// Toggle VBUS power if this is an RK3588 USB3_1 controller - /// + /// /// This is a workaround for the GL3523 USB hub cold-start issue on Orange Pi 5 Plus. /// The hub enters a non-responsive standby state if VBUS is present but no USB host /// activity occurs within ~1-2 seconds. By toggling VBUS after the xHCI controller @@ -144,21 +144,18 @@ impl Xhci { #[cfg(feature = "aggressive_usb_reset")] async fn toggle_vbus_if_rk3588(&self) { let base_addr = self.mmio_base.as_ptr() as usize; - + if rk3588_phy::is_rk3588_usb3_port1(base_addr) { debug!("RK3588 USB3_1: Applying GL3523 hub VBUS toggle workaround"); - + unsafe { - rk3588_phy::toggle_vbus_port1( - rk3588_phy::VBUS_OFF_MS, - rk3588_phy::VBUS_ON_WAIT_MS, - ); + rk3588_phy::toggle_vbus_port1(rk3588_phy::VBUS_OFF_MS, rk3588_phy::VBUS_ON_WAIT_MS); } - + sleep(Duration::from_millis(200)).await; } } - + /// No-op variant when aggressive_usb_reset feature is disabled #[cfg(not(feature = "aggressive_usb_reset"))] async fn toggle_vbus_if_rk3588(&self) { @@ -167,7 +164,7 @@ impl Xhci { async fn chip_hardware_reset(&mut self) -> Result { debug!("Reset begin ..."); - + self.reg.operational.usbcmd.update_volatile(|c| { c.clear_run_stop(); }); diff --git a/usb-host/src/backend/xhci/rk3588_phy.rs b/usb-host/src/backend/xhci/rk3588_phy.rs index 6ec9c8c..2ef27fd 100644 --- a/usb-host/src/backend/xhci/rk3588_phy.rs +++ b/usb-host/src/backend/xhci/rk3588_phy.rs @@ -1,316 +1,8 @@ -//! RK3588 USBDP Combo PHY and GPIO VBUS control -//! -//! This module provides PHY initialization for USB3 ports on RK3588 SoC, -//! plus GPIO-based VBUS power control for the GL3523 hub workaround. +//! RK3588 GPIO VBUS control for GL3523 hub workaround. -#![allow(dead_code)] // Complete API for RK3588 USB subsystem +use core::ptr::write_volatile; -use core::ptr::{read_volatile, write_volatile}; -use log::{debug, warn}; - -use super::delay::{delay_ms, delay_us}; - -pub const USBDPPHY1_BASE: usize = 0xFED90000; -pub const USBDPPHY1_PMA_BASE: usize = USBDPPHY1_BASE + 0x8000; -pub const USBDPPHY1_GRF_BASE: usize = 0xFD5CC000; -pub const USB2PHY1_GRF_BASE: usize = 0xFD5D4000; -pub const USB_GRF_BASE: usize = 0xFD5AC000; - -const USBDPPHY_GRF_CON0: usize = 0x0000; -const USBDPPHY_GRF_CON1: usize = 0x0004; -const USB_GRF_USB3OTG1_CON0: usize = 0x0030; -const USB_GRF_USB3OTG1_CON1: usize = 0x0034; - -const USBDPPHY_GRF_LOW_PWRN_BIT: u32 = 13; -const USBDPPHY_GRF_RX_LFPS_BIT: u32 = 14; - -const USB3OTG1_CFG_ENABLE: u32 = 0x1100; -const USB3OTG1_CFG_DISABLE: u32 = 0x0188; -const USB3OTG1_CFG_MASK: u32 = 0xFFFF; - -const CMN_LANE_MUX_AND_EN_OFFSET: usize = 0x0288; -const CMN_DP_LANE_MUX_N: fn(u32) -> u32 = |n| 1 << (n + 4); -const CMN_DP_LANE_EN_N: fn(u32) -> u32 = |n| 1 << n; -const CMN_DP_LANE_MUX_ALL: u32 = 0xF0; -const CMN_DP_LANE_EN_ALL: u32 = 0x0F; - -const PHY_LANE_MUX_USB: u32 = 0; -const PHY_LANE_MUX_DP: u32 = 1; - -const CMN_DP_RSTN_OFFSET: usize = 0x038C; -const CMN_DP_INIT_RSTN: u32 = 1 << 3; - -const CMN_ANA_LCPLL_DONE_OFFSET: usize = 0x0350; -const CMN_ANA_LCPLL_AFC_DONE: u32 = 1 << 6; -const CMN_ANA_LCPLL_LOCK_DONE: u32 = 1 << 7; - -const TRSV_LN0_MON_RX_CDR_DONE_OFFSET: usize = 0x0B84; -const TRSV_LN0_MON_RX_CDR_LOCK_DONE: u32 = 1 << 0; - -const TRSV_LN2_MON_RX_CDR_DONE_OFFSET: usize = 0x1B84; -const TRSV_LN2_MON_RX_CDR_LOCK_DONE: u32 = 1 << 0; - -static RK3588_UDPHY_24M_REFCLK_CFG: &[(u16, u8)] = &[ - (0x0090, 0x68), (0x0094, 0x68), - (0x0128, 0x24), (0x012c, 0x44), - (0x0130, 0x3f), (0x0134, 0x44), - (0x015c, 0xa9), (0x0160, 0x71), - (0x0164, 0x71), (0x0168, 0xa9), - (0x0174, 0xa9), (0x0178, 0x71), - (0x017c, 0x71), (0x0180, 0xa9), - (0x018c, 0x41), (0x0190, 0x00), - (0x0194, 0x05), (0x01ac, 0x2a), - (0x01b0, 0x17), (0x01b4, 0x17), - (0x01b8, 0x2a), (0x01c8, 0x04), - (0x01cc, 0x08), (0x01d0, 0x08), - (0x01d4, 0x04), (0x01d8, 0x20), - (0x01dc, 0x01), (0x01e0, 0x09), - (0x01e4, 0x03), (0x01f0, 0x29), - (0x01f4, 0x02), (0x01f8, 0x02), - (0x01fc, 0x29), (0x0208, 0x2a), - (0x020c, 0x17), (0x0210, 0x17), - (0x0214, 0x2a), (0x0224, 0x20), - (0x03f0, 0x0d), (0x03f4, 0x09), - (0x03f8, 0x09), (0x03fc, 0x0d), - (0x0404, 0x0e), (0x0408, 0x14), - (0x040c, 0x14), (0x0410, 0x3b), - (0x0ce0, 0x68), (0x0ce8, 0xd0), - (0x0cf0, 0x87), (0x0cf8, 0x70), - (0x0d00, 0x70), (0x0d08, 0xa9), - (0x1ce0, 0x68), (0x1ce8, 0xd0), - (0x1cf0, 0x87), (0x1cf8, 0x70), - (0x1d00, 0x70), (0x1d08, 0xa9), - (0x0a3c, 0xd0), (0x0a44, 0xd0), - (0x0a48, 0x01), (0x0a4c, 0x0d), - (0x0a54, 0xe0), (0x0a5c, 0xe0), - (0x0a64, 0xa8), (0x1a3c, 0xd0), - (0x1a44, 0xd0), (0x1a48, 0x01), - (0x1a4c, 0x0d), (0x1a54, 0xe0), - (0x1a5c, 0xe0), (0x1a64, 0xa8), -]; - -static RK3588_UDPHY_INIT_SEQUENCE: &[(u16, u8)] = &[ - (0x0104, 0x44), (0x0234, 0xE8), - (0x0248, 0x44), (0x028C, 0x18), - (0x081C, 0xE5), (0x0878, 0x00), - (0x0994, 0x1C), (0x0AF0, 0x00), - (0x181C, 0xE5), (0x1878, 0x00), - (0x1994, 0x1C), (0x1AF0, 0x00), - (0x0428, 0x60), (0x0D58, 0x33), - (0x1D58, 0x33), (0x0990, 0x74), - (0x0D64, 0x17), (0x08C8, 0x13), - (0x1990, 0x74), (0x1D64, 0x17), - (0x18C8, 0x13), (0x0D90, 0x40), - (0x0DA8, 0x40), (0x0DC0, 0x40), - (0x0DD8, 0x40), (0x1D90, 0x40), - (0x1DA8, 0x40), (0x1DC0, 0x40), - (0x1DD8, 0x40), (0x03C0, 0x30), - (0x03C4, 0x06), (0x0E10, 0x00), - (0x1E10, 0x00), (0x043C, 0x0F), - (0x0D2C, 0xFF), (0x1D2C, 0xFF), - (0x0D34, 0x0F), (0x1D34, 0x0F), - (0x08FC, 0x2A), (0x0914, 0x28), - (0x0A30, 0x03), (0x0E38, 0x05), - (0x0ECC, 0x27), (0x0ED0, 0x22), - (0x0ED4, 0x26), (0x18FC, 0x2A), - (0x1914, 0x28), (0x1A30, 0x03), - (0x1E38, 0x05), (0x1ECC, 0x27), - (0x1ED0, 0x22), (0x1ED4, 0x26), - (0x0048, 0x0F), (0x0060, 0x3C), - (0x0064, 0xF7), (0x006C, 0x20), - (0x0070, 0x7D), (0x0074, 0x68), - (0x0AF4, 0x1A), (0x1AF4, 0x1A), - (0x0440, 0x3F), (0x10D4, 0x08), - (0x20D4, 0x08), (0x00D4, 0x30), - (0x0024, 0x6e), -]; - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum UdphyMode { - Usb = 1, - Dp = 2, - UsbDp = 3, -} - -#[derive(Debug, Clone)] -pub struct LaneMuxConfig { - pub lane_mux_sel: [u32; 4], -} - -impl Default for LaneMuxConfig { - fn default() -> Self { - Self { - lane_mux_sel: [PHY_LANE_MUX_USB, PHY_LANE_MUX_USB, PHY_LANE_MUX_DP, PHY_LANE_MUX_DP], - } - } -} - -pub struct Rk3588UsbdpPhy { - pma_base: usize, - udphygrf_base: usize, - usbgrf_base: usize, - mode: UdphyMode, - lane_mux: LaneMuxConfig, - flip: bool, -} - -impl Rk3588UsbdpPhy { - pub unsafe fn new_port1() -> Self { - Self { - pma_base: USBDPPHY1_PMA_BASE, - udphygrf_base: USBDPPHY1_GRF_BASE, - usbgrf_base: USB_GRF_BASE, - mode: UdphyMode::UsbDp, - lane_mux: LaneMuxConfig::default(), - flip: false, - } - } - - pub fn init(&self) -> Result<(), &'static str> { - debug!("RK3588 USBDP PHY: Starting initialization for Port 1"); - - // Step 1: Enable rx_lfps for USB - if self.mode == UdphyMode::Usb || self.mode == UdphyMode::UsbDp { - self.grf_write(self.udphygrf_base, USBDPPHY_GRF_CON1, USBDPPHY_GRF_RX_LFPS_BIT, true); - } - - // Step 2: Power on PMA (set low_pwrn high) - self.grf_write(self.udphygrf_base, USBDPPHY_GRF_CON1, USBDPPHY_GRF_LOW_PWRN_BIT, true); - - delay_us(100); - - // Step 3: Write init sequence to PMA - for &(offset, value) in RK3588_UDPHY_INIT_SEQUENCE { - self.pma_write(offset as usize, value as u32); - } - - // Step 4: Write 24MHz reference clock configuration - for &(offset, value) in RK3588_UDPHY_24M_REFCLK_CFG { - self.pma_write(offset as usize, value as u32); - } - - // Step 5: Configure lane mux - let mut lane_mux_val = 0u32; - for (i, &mux) in self.lane_mux.lane_mux_sel.iter().enumerate() { - if mux == PHY_LANE_MUX_DP { - lane_mux_val |= CMN_DP_LANE_MUX_N(i as u32); - } - } - self.pma_update(CMN_LANE_MUX_AND_EN_OFFSET, - CMN_DP_LANE_MUX_ALL | CMN_DP_LANE_EN_ALL, - lane_mux_val); - - // Step 6: Deassert init reset for USB mode - if self.mode == UdphyMode::Usb || self.mode == UdphyMode::UsbDp { - let current = self.pma_read(CMN_DP_RSTN_OFFSET); - self.pma_write(CMN_DP_RSTN_OFFSET, current | CMN_DP_INIT_RSTN); - } - - delay_us(1); - - // Step 7: Wait for PLL lock - if self.mode == UdphyMode::Usb || self.mode == UdphyMode::UsbDp { - self.wait_for_pll_lock()?; - } - - // Step 8: Enable USB3 port in USB GRF - self.usb3_port_enable(true); - - debug!("RK3588 USBDP PHY: Initialization complete"); - Ok(()) - } - - fn wait_for_pll_lock(&self) -> Result<(), &'static str> { - let mut timeout = 200; - loop { - let val = self.pma_read(CMN_ANA_LCPLL_DONE_OFFSET); - if (val & CMN_ANA_LCPLL_AFC_DONE) != 0 && (val & CMN_ANA_LCPLL_LOCK_DONE) != 0 { - break; - } - timeout -= 1; - if timeout == 0 { - warn!("RK3588 USBDP PHY: LCPLL lock timeout (val=0x{:02x})", val); - return Err("LCPLL lock timeout"); - } - delay_us(1000); - } - - let cdr_offset = if self.flip { - TRSV_LN2_MON_RX_CDR_DONE_OFFSET - } else { - TRSV_LN0_MON_RX_CDR_DONE_OFFSET - }; - let cdr_done_bit = if self.flip { - TRSV_LN2_MON_RX_CDR_LOCK_DONE - } else { - TRSV_LN0_MON_RX_CDR_LOCK_DONE - }; - - timeout = 200; - loop { - let val = self.pma_read(cdr_offset); - if (val & cdr_done_bit) != 0 { - break; - } - timeout -= 1; - if timeout == 0 { - warn!("RK3588 USBDP PHY: CDR lock timeout (val=0x{:02x})", val); - break; - } - delay_us(1000); - } - - Ok(()) - } - - fn usb3_port_enable(&self, enable: bool) { - let val = if enable { USB3OTG1_CFG_ENABLE } else { USB3OTG1_CFG_DISABLE }; - let write_val = (USB3OTG1_CFG_MASK << 16) | val; - unsafe { - let ptr = (self.usbgrf_base + USB_GRF_USB3OTG1_CON1) as *mut u32; - write_volatile(ptr, write_val); - } - } - - fn grf_write(&self, base: usize, offset: usize, bit: u32, set: bool) { - let mask = 1u32 << bit; - let val = if set { mask } else { 0 }; - let write_val = (mask << 16) | val; - unsafe { - let ptr = (base + offset) as *mut u32; - write_volatile(ptr, write_val); - } - } - - fn pma_read(&self, offset: usize) -> u32 { - unsafe { - let ptr = (self.pma_base + offset) as *const u32; - read_volatile(ptr) - } - } - - fn pma_write(&self, offset: usize, value: u32) { - unsafe { - let ptr = (self.pma_base + offset) as *mut u32; - write_volatile(ptr, value); - } - } - - fn pma_update(&self, offset: usize, mask: u32, value: u32) { - let current = self.pma_read(offset); - let new_val = (current & !mask) | (value & mask); - self.pma_write(offset, new_val); - } -} - -pub unsafe fn init_rk3588_usbdp_phy_port1() -> Result<(), &'static str> { - let phy = unsafe { Rk3588UsbdpPhy::new_port1() }; - phy.init() -} - -pub fn is_rk3588_usb3_port1(xhci_base: usize) -> bool { - xhci_base == 0xFC400000 -} +use super::delay::delay_ms; const GPIO3_BASE: usize = 0xFEC40000; const GPIO_SWPORT_DR_L: usize = 0x0000; @@ -318,58 +10,30 @@ const GPIO_SWPORT_DDR_L: usize = 0x0008; const GPIO3_B7_BIT: u32 = 1 << 15; const WRITE_MASK_BIT15: u32 = 1 << 31; -pub struct Rk3588VbusGpio { - gpio_base: usize, - pin_bit: u32, - write_mask: u32, -} +pub const VBUS_OFF_MS: u32 = 1000; +pub const VBUS_ON_WAIT_MS: u32 = 500; -impl Rk3588VbusGpio { - pub unsafe fn new_port1() -> Self { - Self { - gpio_base: GPIO3_BASE, - pin_bit: GPIO3_B7_BIT, - write_mask: WRITE_MASK_BIT15, - } - } +const RK3588_USB3_PORT1_BASE: usize = 0xFC400000; - fn configure_as_output(&self) { - unsafe { - let ddr_ptr = (self.gpio_base + GPIO_SWPORT_DDR_L) as *mut u32; - write_volatile(ddr_ptr, self.write_mask | self.pin_bit); - } - } +pub fn is_rk3588_usb3_port1(xhci_base: usize) -> bool { + xhci_base == RK3588_USB3_PORT1_BASE +} - pub fn set_vbus(&self, enabled: bool) { - self.configure_as_output(); - - unsafe { - let dr_ptr = (self.gpio_base + GPIO_SWPORT_DR_L) as *mut u32; - let value = if enabled { self.pin_bit } else { 0 }; - write_volatile(dr_ptr, self.write_mask | value); - } - } +/// # Safety +/// Direct hardware register access. Caller must ensure this is RK3588 hardware. +pub unsafe fn toggle_vbus_port1(off_ms: u32, on_wait_ms: u32) { + unsafe { + let ddr_ptr = (GPIO3_BASE + GPIO_SWPORT_DDR_L) as *mut u32; + write_volatile(ddr_ptr, WRITE_MASK_BIT15 | GPIO3_B7_BIT); - pub fn get_vbus(&self) -> bool { - unsafe { - let dr_ptr = (self.gpio_base + GPIO_SWPORT_DR_L) as *const u32; - (read_volatile(dr_ptr) & self.pin_bit) != 0 - } + let dr_ptr = (GPIO3_BASE + GPIO_SWPORT_DR_L) as *mut u32; + write_volatile(dr_ptr, WRITE_MASK_BIT15); } + delay_ms(off_ms); - pub fn toggle_vbus(&self, off_ms: u32, on_wait_ms: u32) { - self.set_vbus(false); - delay_ms(off_ms); - - self.set_vbus(true); - delay_ms(on_wait_ms); + unsafe { + let dr_ptr = (GPIO3_BASE + GPIO_SWPORT_DR_L) as *mut u32; + write_volatile(dr_ptr, WRITE_MASK_BIT15 | GPIO3_B7_BIT); } + delay_ms(on_wait_ms); } - -pub unsafe fn toggle_vbus_port1(off_ms: u32, on_wait_ms: u32) { - let gpio = unsafe { Rk3588VbusGpio::new_port1() }; - gpio.toggle_vbus(off_ms, on_wait_ms); -} - -pub const VBUS_OFF_MS: u32 = 1000; -pub const VBUS_ON_WAIT_MS: u32 = 500; diff --git a/usb-host/src/backend/xhci/root.rs b/usb-host/src/backend/xhci/root.rs index 4631592..e8b2fe4 100644 --- a/usb-host/src/backend/xhci/root.rs +++ b/usb-host/src/backend/xhci/root.rs @@ -362,81 +362,6 @@ impl Root { .portsc .port_speed() } - - /// 解析端口速度 ID 为实际速度 (Mbps) - #[allow(dead_code)] - pub fn parse_port_speed_to_mbps(&self, port: PortId) -> Option { - let speed_id = self.port_speed(port); - if speed_id == 0 { - return None; // PSIV 值 0 是保留的 - } - - // TODO: 从 xHCI Extended Capabilities 中查找对应的 PSI - // 现在先返回常见速度的硬编码值 - match speed_id { - 1 => Some(12), // USB 1.x Full Speed (12 Mbps) - 2 => Some(480), // USB 2.0 High Speed (480 Mbps) - 3 => Some(5000), // USB 3.0 SuperSpeed (5 Gbps = 5000 Mbps) - 4 => Some(10000), // USB 3.1 SuperSpeedPlus (10 Gbps) - 5 => Some(20000), // USB 3.2 SuperSpeedPlus (20 Gbps) - _ => None, - } - } - - /// 获取端口速度的描述字符串 - #[allow(dead_code)] - pub fn port_speed_description(&self, port: PortId) -> &'static str { - let speed_id = self.port_speed(port); - match speed_id { - 0 => "Unknown", - 1 => "Full Speed (12 Mbps)", - 2 => "High Speed (480 Mbps)", - 3 => "SuperSpeed (5 Gbps)", - 4 => "SuperSpeedPlus (10 Gbps)", - 5 => "SuperSpeedPlus (20 Gbps)", - _ => "Reserved/Unknown", - } - } - - /// 根据端口速度计算默认控制端点的最大包大小 - /// 基于 USB 规范和 xHCI 规范 4.3.3 Address Assignment - #[allow(dead_code)] - pub fn calculate_default_max_packet_size(&self, port: PortId) -> u16 { - let speed_id = self.port_speed(port); - match speed_id { - 0 => 8, // Unknown/Reserved - 使用最小值 - 1 => 64, // Full Speed (USB 1.1): 8, 16, 32, 或 64 字节 - // 规范建议初始化时使用 64,后续通过设备描述符确认实际值 - 2 => 64, // High Speed (USB 2.0): 固定 64 字节 - 3 => 512, // SuperSpeed (USB 3.0): 固定 512 字节 - 4 => 512, // SuperSpeedPlus (USB 3.1): 固定 512 字节 - 5 => 512, // SuperSpeedPlus (USB 3.2): 固定 512 字节 - _ => 8, // 其他/未知速度 - 使用最保守的值 - } - } - - /// 获取特定速度的详细包大小信息 - #[allow(dead_code)] - pub fn get_packet_size_info(&self, port: PortId) -> (u16, &'static str) { - let speed_id = self.port_speed(port); - match speed_id { - 0 => (8, "Unknown speed, using minimum"), - 1 => (64, "Full Speed: 8/16/32/64 bytes possible, using 64"), - 2 => (64, "High Speed: fixed 64 bytes"), - 3 => (512, "SuperSpeed: fixed 512 bytes"), - 4 => (512, "SuperSpeedPlus 10G: fixed 512 bytes"), - 5 => (512, "SuperSpeedPlus 20G: fixed 512 bytes"), - _ => (8, "Reserved/Unknown speed, using minimum"), - } - } - - // pub fn is_64_byte(&self) -> bool { - // self.reg - // .capability - // .hccparams1 - // .read_volatile() - // .addressing_capability() - // } } #[derive(Clone)] diff --git a/usb-host/tests/test.rs b/usb-host/tests/test.rs index f51fc31..ca031ec 100644 --- a/usb-host/tests/test.rs +++ b/usb-host/tests/test.rs @@ -507,7 +507,10 @@ mod tests { let val: u32 = (0xFFFF << 16) | 0x1100; ptr.write_volatile(val); let con1 = (usbgrf_base.as_ptr().add(0x0034) as *const u32).read_volatile(); - info!("USB_GRF USB3OTG1_CON1 set to {:#010x} (USB3 mode, host_num_u3_port=1)", con1); + info!( + "USB_GRF USB3OTG1_CON1 set to {:#010x} (USB3 mode, host_num_u3_port=1)", + con1 + ); } if let Err(e) = init_usb2phy1_full(fdt) { @@ -554,9 +557,15 @@ mod tests { let pma_base_check = iomap(0xfed98000.into(), 0x10000); for i in 0..5 { spin_delay_ms(100); - let lcpll = unsafe { (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile() }; + let lcpll = + unsafe { (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile() }; let locked = (lcpll & 0xC0) == 0xC0; - info!(" {}ms: LCPLL={:#04x} locked={}", (i + 1) * 100, lcpll, locked); + info!( + " {}ms: LCPLL={:#04x} locked={}", + (i + 1) * 100, + lcpll, + locked + ); if !locked { info!(" PHY PLL lost lock during wait!"); } @@ -577,12 +586,14 @@ mod tests { check_port_status("after warm reset"); let grf_base2 = iomap(0xfd5d4000.into(), 0x8000); unsafe { - let read_grf = |off: usize| -> u32 { - (grf_base2.as_ptr().add(off) as *const u32).read_volatile() - }; + let read_grf = + |off: usize| -> u32 { (grf_base2.as_ptr().add(off) as *const u32).read_volatile() }; let phy_status0 = read_grf(0x4000 + 0xC0); let linestate = (phy_status0 >> 9) & 0x3; - info!("USB2PHY1 STATUS0 after wait: {:#010x} (linestate={:02b})", phy_status0, linestate); + info!( + "USB2PHY1 STATUS0 after wait: {:#010x} (linestate={:02b})", + phy_status0, linestate + ); } info!("=== TRY #2: Port Power Cycle (PP bit toggle) ==="); @@ -594,18 +605,23 @@ mod tests { let val: u32 = (0xFFFF << 16) | 0x0188; ptr.write_volatile(val); let con1 = (usbgrf_base2.as_ptr().add(0x0034) as *const u32).read_volatile(); - info!("USB_GRF USB3OTG1_CON1 set to {:#010x} (USB2-only mode, host_num_u3_port=0)", con1); + info!( + "USB_GRF USB3OTG1_CON1 set to {:#010x} (USB2-only mode, host_num_u3_port=0)", + con1 + ); } info!("Waiting 500ms for USB2 detection..."); spin_delay_ms(500); check_port_status("after USB2-only mode switch"); unsafe { - let read_grf = |off: usize| -> u32 { - (grf_base2.as_ptr().add(off) as *const u32).read_volatile() - }; + let read_grf = + |off: usize| -> u32 { (grf_base2.as_ptr().add(off) as *const u32).read_volatile() }; let phy_status0 = read_grf(0x4000 + 0xC0); let linestate = (phy_status0 >> 9) & 0x3; - info!("USB2PHY1 STATUS0 (USB2 mode): {:#010x} (linestate={:02b})", phy_status0, linestate); + info!( + "USB2PHY1 STATUS0 (USB2 mode): {:#010x} (linestate={:02b})", + phy_status0, linestate + ); } info!("=== Restore USB3 mode ==="); @@ -614,7 +630,10 @@ mod tests { let val: u32 = (0xFFFF << 16) | 0x1100; ptr.write_volatile(val); let con1 = (usbgrf_base2.as_ptr().add(0x0034) as *const u32).read_volatile(); - info!("USB_GRF USB3OTG1_CON1 restored to {:#010x} (USB3 mode)", con1); + info!( + "USB_GRF USB3OTG1_CON1 restored to {:#010x} (USB3 mode)", + con1 + ); } info!("=== TRY #4: VBUS GPIO Toggle (hardware power cycle) ==="); @@ -623,86 +642,95 @@ mod tests { dump_usb_debug_registers(); } - + fn try_force_rx_detect() { let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_pipe = || -> u32 { - (dwc3_base.add(GUSB3PIPECTL) as *const u32).read_volatile() - }; + let read_pipe = + || -> u32 { (dwc3_base.add(GUSB3PIPECTL) as *const u32).read_volatile() }; let write_pipe = |val: u32| { (dwc3_base.add(GUSB3PIPECTL) as *mut u32).write_volatile(val); }; - + let pipe_before = read_pipe(); info!("GUSB3PIPECTL before RxDet: {:#010x}", pipe_before); - + let mut pipe = pipe_before; pipe |= GUSB3PIPECTL_STARTRXDETU3RXDET; write_pipe(pipe); info!("Set StartRxDetU3RxDet=1 (pulse)"); - + spin_delay_ms(10); - + pipe = read_pipe(); pipe &= !GUSB3PIPECTL_STARTRXDETU3RXDET; write_pipe(pipe); info!("Cleared StartRxDetU3RxDet"); - + let pipe_after = read_pipe(); info!("GUSB3PIPECTL after RxDet: {:#010x}", pipe_after); } } - + fn try_port_power_cycle() { let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); unsafe { let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); let op_base = xhci_base.as_ptr().add(caplength as usize); - + let portsc_usb2_ptr = op_base.add(0x400) as *mut u32; let portsc_usb3_ptr = op_base.add(0x410) as *mut u32; - + let portsc_usb2 = portsc_usb2_ptr.read_volatile(); let portsc_usb3 = portsc_usb3_ptr.read_volatile(); - info!("Before PP cycle: USB2 PORTSC={:#010x}, USB3 PORTSC={:#010x}", portsc_usb2, portsc_usb3); - + info!( + "Before PP cycle: USB2 PORTSC={:#010x}, USB3 PORTSC={:#010x}", + portsc_usb2, portsc_usb3 + ); + let portsc_usb2_pp_off = (portsc_usb2 & PORTSC_RW_MASK) & !PORTSC_PP_BIT; let portsc_usb3_pp_off = (portsc_usb3 & PORTSC_RW_MASK) & !PORTSC_PP_BIT; - + info!("Powering OFF ports (clearing PP bit)..."); portsc_usb2_ptr.write_volatile(portsc_usb2_pp_off); portsc_usb3_ptr.write_volatile(portsc_usb3_pp_off); - + info!("Waiting 500ms with port power OFF..."); spin_delay_ms(500); - + let portsc_usb2_off = portsc_usb2_ptr.read_volatile(); let portsc_usb3_off = portsc_usb3_ptr.read_volatile(); - info!("After PP off: USB2 PORTSC={:#010x} (PP={}), USB3 PORTSC={:#010x} (PP={})", - portsc_usb2_off, (portsc_usb2_off >> 9) & 1, portsc_usb3_off, (portsc_usb3_off >> 9) & 1); - + info!( + "After PP off: USB2 PORTSC={:#010x} (PP={}), USB3 PORTSC={:#010x} (PP={})", + portsc_usb2_off, + (portsc_usb2_off >> 9) & 1, + portsc_usb3_off, + (portsc_usb3_off >> 9) & 1 + ); + let portsc_usb2_pp_on = (portsc_usb2_off & PORTSC_RW_MASK) | PORTSC_PP_BIT; let portsc_usb3_pp_on = (portsc_usb3_off & PORTSC_RW_MASK) | PORTSC_PP_BIT; - + info!("Powering ON ports (setting PP bit)..."); portsc_usb2_ptr.write_volatile(portsc_usb2_pp_on); portsc_usb3_ptr.write_volatile(portsc_usb3_pp_on); - + info!("Waiting 500ms for device detection after PP on..."); spin_delay_ms(500); - + let portsc_usb2_on = portsc_usb2_ptr.read_volatile(); let portsc_usb3_on = portsc_usb3_ptr.read_volatile(); let ccs_usb2 = portsc_usb2_on & 1; let ccs_usb3 = portsc_usb3_on & 1; let pls_usb2 = (portsc_usb2_on >> 5) & 0xf; let pls_usb3 = (portsc_usb3_on >> 5) & 0xf; - - info!("After PP on: USB2 PORTSC={:#010x} (CCS={}, PLS={}), USB3 PORTSC={:#010x} (CCS={}, PLS={})", - portsc_usb2_on, ccs_usb2, pls_usb2, portsc_usb3_on, ccs_usb3, pls_usb3); - + + info!( + "After PP on: USB2 PORTSC={:#010x} (CCS={}, PLS={}), USB3 PORTSC={:#010x} (CCS={}, PLS={})", + portsc_usb2_on, ccs_usb2, pls_usb2, portsc_usb3_on, ccs_usb3, pls_usb3 + ); + if ccs_usb2 == 1 || ccs_usb3 == 1 { info!("SUCCESS: Device detected after port power cycle!"); } else { @@ -717,90 +745,124 @@ mod tests { let gpio3_base = iomap(GPIO3_BASE.into(), 0x1000); unsafe { // Read current state - let dr_before = (gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *const u32).read_volatile(); - let ddr_before = (gpio3_base.as_ptr().add(GPIO_SWPORT_DDR_L) as *const u32).read_volatile(); - info!("GPIO3 before: DR_L={:#010x}, DDR_L={:#010x}", dr_before, ddr_before); - info!(" GPIO3_B7 (bit15): value={}, direction={} (1=output)", - (dr_before >> 15) & 1, (ddr_before >> 15) & 1); - + let dr_before = + (gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *const u32).read_volatile(); + let ddr_before = + (gpio3_base.as_ptr().add(GPIO_SWPORT_DDR_L) as *const u32).read_volatile(); + info!( + "GPIO3 before: DR_L={:#010x}, DDR_L={:#010x}", + dr_before, ddr_before + ); + info!( + " GPIO3_B7 (bit15): value={}, direction={} (1=output)", + (dr_before >> 15) & 1, + (ddr_before >> 15) & 1 + ); + // Ensure GPIO3_B7 is configured as output let ddr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DDR_L) as *mut u32; ddr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); - + // Turn OFF VBUS (set GPIO3_B7 low - active high regulator) info!("Turning OFF VBUS (GPIO3_B7 = 0)..."); let dr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *mut u32; - dr_ptr.write_volatile(WRITE_MASK_BIT15 | 0); // Clear bit 15 - + dr_ptr.write_volatile(WRITE_MASK_BIT15 | 0); // Clear bit 15 + let dr_off = (gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *const u32).read_volatile(); - info!("GPIO3 DR_L after OFF: {:#010x} (bit15={})", dr_off, (dr_off >> 15) & 1); - + info!( + "GPIO3 DR_L after OFF: {:#010x} (bit15={})", + dr_off, + (dr_off >> 15) & 1 + ); + // Wait 1 second with VBUS off info!("Waiting 1000ms with VBUS OFF..."); spin_delay_ms(1000); - + // Turn ON VBUS (set GPIO3_B7 high) info!("Turning ON VBUS (GPIO3_B7 = 1)..."); dr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); - + let dr_on = (gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *const u32).read_volatile(); - info!("GPIO3 DR_L after ON: {:#010x} (bit15={})", dr_on, (dr_on >> 15) & 1); - + info!( + "GPIO3 DR_L after ON: {:#010x} (bit15={})", + dr_on, + (dr_on >> 15) & 1 + ); + // Wait for hub to power up and initialize info!("Waiting 500ms for hub to power up..."); spin_delay_ms(500); } } - fn try_warm_port_reset() { let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); unsafe { let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); let op_base = xhci_base.as_ptr().add(caplength as usize); let portsc_ptr = op_base.add(0x400) as *mut u32; - + let portsc = portsc_ptr.read_volatile(); let pls = (portsc >> 5) & 0xf; let ccs = portsc & 1; - - info!("Before warm reset: PORTSC={:#010x}, PLS={}, CCS={}", portsc, pls, ccs); - + + info!( + "Before warm reset: PORTSC={:#010x}, PLS={}, CCS={}", + portsc, pls, ccs + ); + let pma_base_before = iomap(0xfed98000.into(), 0x10000); - let lcpll_before_wpr = (pma_base_before.as_ptr().add(0x0350) as *const u32).read_volatile(); - info!("PHY PLL before warm reset decision: LCPLL={:#04x} (AFC={}, LOCK={})", - lcpll_before_wpr, (lcpll_before_wpr >> 6) & 1, (lcpll_before_wpr >> 7) & 1); - + let lcpll_before_wpr = + (pma_base_before.as_ptr().add(0x0350) as *const u32).read_volatile(); + info!( + "PHY PLL before warm reset decision: LCPLL={:#04x} (AFC={}, LOCK={})", + lcpll_before_wpr, + (lcpll_before_wpr >> 6) & 1, + (lcpll_before_wpr >> 7) & 1 + ); + if pls == 4 { info!("Port in Inactive state (PLS=4), attempting warm reset via WPR bit"); let new_portsc = (portsc & 0x0e00c3e0) | (1 << 31); portsc_ptr.write_volatile(new_portsc); - + for _ in 0..1000000 { core::hint::spin_loop(); } - + let portsc_after = portsc_ptr.read_volatile(); let pls_after = (portsc_after >> 5) & 0xf; - info!("After WPR: PORTSC={:#010x}, PLS={}", portsc_after, pls_after); + info!( + "After WPR: PORTSC={:#010x}, PLS={}", + portsc_after, pls_after + ); } else if pls == 5 { info!("Port in RxDetect state (PLS=5), NOT doing warm reset (would kill PHY PLL)"); } else { info!("Port in PLS={} state", pls); } - + let portsc_final = portsc_ptr.read_volatile(); let pls_final = (portsc_final >> 5) & 0xf; let ccs_final = portsc_final & 1; - info!("Final: PORTSC={:#010x}, PLS={}, CCS={}", portsc_final, pls_final, ccs_final); - + info!( + "Final: PORTSC={:#010x}, PLS={}, CCS={}", + portsc_final, pls_final, ccs_final + ); + let pma_base_check = iomap(0xfed98000.into(), 0x10000); - let lcpll_after_wpr = (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile(); - info!("PHY PLL after warm reset: LCPLL={:#04x} (AFC={}, LOCK={})", - lcpll_after_wpr, (lcpll_after_wpr >> 6) & 1, (lcpll_after_wpr >> 7) & 1); + let lcpll_after_wpr = + (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile(); + info!( + "PHY PLL after warm reset: LCPLL={:#04x} (AFC={}, LOCK={})", + lcpll_after_wpr, + (lcpll_after_wpr >> 6) & 1, + (lcpll_after_wpr >> 7) & 1 + ); } } - + fn dump_initial_phy_state() { let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); @@ -812,35 +874,59 @@ mod tests { let cdr_ln0 = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); let cdr_ln2 = (pma_base.as_ptr().add(0x1B84) as *const u32).read_volatile(); let lane_mux = (pma_base.as_ptr().add(0x0288) as *const u32).read_volatile(); - info!("PHY PMA: LCPLL={:#04x} CDR_LN0={:#04x} CDR_LN2={:#04x} LANE_MUX={:#04x}", - lcpll, cdr_ln0, cdr_ln2, lane_mux); - info!(" LCPLL: AFC={} LOCK={}", (lcpll >> 6) & 1, (lcpll >> 7) & 1); - + info!( + "PHY PMA: LCPLL={:#04x} CDR_LN0={:#04x} CDR_LN2={:#04x} LANE_MUX={:#04x}", + lcpll, cdr_ln0, cdr_ln2, lane_mux + ); + info!( + " LCPLL: AFC={} LOCK={}", + (lcpll >> 6) & 1, + (lcpll >> 7) & 1 + ); + let con0 = (udphygrf_base.as_ptr().add(0x00) as *const u32).read_volatile(); let con1 = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); info!("USBDPPHY1_GRF: CON0={:#010x} CON1={:#010x}", con0, con1); - info!(" CON1: low_pwrn={} rx_lfps={}", (con1 >> 13) & 1, (con1 >> 14) & 1); - + info!( + " CON1: low_pwrn={} rx_lfps={}", + (con1 >> 13) & 1, + (con1 >> 14) & 1 + ); + let otg1_con0 = (usbgrf_base.as_ptr().add(0x30) as *const u32).read_volatile(); let otg1_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); - info!("USB_GRF: OTG1_CON0={:#010x} OTG1_CON1={:#010x}", otg1_con0, otg1_con1); - + info!( + "USB_GRF: OTG1_CON0={:#010x} OTG1_CON1={:#010x}", + otg1_con0, otg1_con1 + ); + let grf_con2 = (u2phygrf_base.as_ptr().add(0x08) as *const u32).read_volatile(); let grf_con4 = (u2phygrf_base.as_ptr().add(0x10) as *const u32).read_volatile(); - info!("USB2PHY1_GRF: CON2={:#010x} CON4={:#010x} (bvalid regs)", grf_con2, grf_con4); - + info!( + "USB2PHY1_GRF: CON2={:#010x} CON4={:#010x} (bvalid regs)", + grf_con2, grf_con4 + ); + let dwc3_base = xhci_base.as_ptr().add(DWC3_OFFSET); let gctl = (dwc3_base.add(0x10) as *const u32).read_volatile(); let gusb2phycfg = (dwc3_base.add(0x100) as *const u32).read_volatile(); let gusb3pipectl = (dwc3_base.add(0x1c0) as *const u32).read_volatile(); - info!("DWC3: GCTL={:#010x} GUSB2PHYCFG={:#010x} GUSB3PIPECTL={:#010x}", gctl, gusb2phycfg, gusb3pipectl); - + info!( + "DWC3: GCTL={:#010x} GUSB2PHYCFG={:#010x} GUSB3PIPECTL={:#010x}", + gctl, gusb2phycfg, gusb3pipectl + ); + let portsc = (xhci_base.as_ptr().add(0x20 + 0x400) as *const u32).read_volatile(); - info!("xHCI: PORTSC={:#010x} (CCS={} PED={} PLS={})", - portsc, portsc & 1, (portsc >> 1) & 1, (portsc >> 5) & 0xF); + info!( + "xHCI: PORTSC={:#010x} (CCS={} PED={} PLS={})", + portsc, + portsc & 1, + (portsc >> 1) & 1, + (portsc >> 5) & 0xF + ); } } - + fn dump_usb_debug_registers() { let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); @@ -852,60 +938,88 @@ mod tests { // GATE_CON02 contains USBDP_PHY1_IMMORTAL at bit 15 let gate_con02 = (cru_base.as_ptr().add(0x0808) as *const u32).read_volatile(); info!("CRU GATE_CON02 (0x0808): {:#010x}", gate_con02); - info!(" USBDP_PHY0_IMMORTAL (bit8)={} USBDP_PHY1_IMMORTAL (bit15)={} (0=enabled,1=disabled)", - (gate_con02 >> 8) & 1, (gate_con02 >> 15) & 1); - + info!( + " USBDP_PHY0_IMMORTAL (bit8)={} USBDP_PHY1_IMMORTAL (bit15)={} (0=enabled,1=disabled)", + (gate_con02 >> 8) & 1, + (gate_con02 >> 15) & 1 + ); + let gate_con42 = (cru_base.as_ptr().add(0x08a8) as *const u32).read_volatile(); let gate_con35 = (cru_base.as_ptr().add(0x088c) as *const u32).read_volatile(); info!("CRU GATE_CON42 (0x08a8): {:#010x}", gate_con42); - info!(" aclk_usb_root={} hclk_usb_root={} aclk_usb3otg0={} aclk_usb3otg1={}", - (gate_con42 >> 0) & 1, (gate_con42 >> 1) & 1, - (gate_con42 >> 4) & 1, (gate_con42 >> 7) & 1); - info!(" ref_clk_usb3otg0={} ref_clk_usb3otg1={} suspend_clk_usb3otg1={}", - (gate_con42 >> 6) & 1, (gate_con42 >> 9) & 1, (gate_con42 >> 8) & 1); + info!( + " aclk_usb_root={} hclk_usb_root={} aclk_usb3otg0={} aclk_usb3otg1={}", + (gate_con42 >> 0) & 1, + (gate_con42 >> 1) & 1, + (gate_con42 >> 4) & 1, + (gate_con42 >> 7) & 1 + ); + info!( + " ref_clk_usb3otg0={} ref_clk_usb3otg1={} suspend_clk_usb3otg1={}", + (gate_con42 >> 6) & 1, + (gate_con42 >> 9) & 1, + (gate_con42 >> 8) & 1 + ); info!("CRU GATE_CON35 (0x088c): {:#010x}", gate_con35); - + // Also check GATE_CON72 for PCLK_USBDPPHY clocks let gate_con72 = (cru_base.as_ptr().add(0x0920) as *const u32).read_volatile(); info!("CRU GATE_CON72 (0x0920): {:#010x}", gate_con72); - info!(" PCLK_USBDPPHY0 (bit2)={} PCLK_USBDPPHY1 (bit4)={} (0=enabled,1=disabled)", - (gate_con72 >> 2) & 1, (gate_con72 >> 4) & 1); + info!( + " PCLK_USBDPPHY0 (bit2)={} PCLK_USBDPPHY1 (bit4)={} (0=enabled,1=disabled)", + (gate_con72 >> 2) & 1, + (gate_con72 >> 4) & 1 + ); } unsafe { let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); let hcsparams1 = (xhci_base.as_ptr().add(0x04) as *const u32).read_volatile(); let num_ports = (hcsparams1 >> 24) & 0xFF; - info!("xHCI CAPLENGTH: {:#x}, HCSPARAMS1: {:#010x} (ports={})", caplength, hcsparams1, num_ports); - + info!( + "xHCI CAPLENGTH: {:#x}, HCSPARAMS1: {:#010x} (ports={})", + caplength, hcsparams1, num_ports + ); + let op_base = xhci_base.as_ptr().add(caplength as usize); let portsc_base = op_base.add(0x400); - + for port_idx in 0..4 { let portsc_off = port_idx * 0x10; let portsc = (portsc_base.add(portsc_off) as *const u32).read_volatile(); if portsc != 0 || port_idx < 2 { - info!("xHCI PORTSC[{}] (op+0x{:03x}): {:#010x}", port_idx, 0x400 + portsc_off, portsc); + info!( + "xHCI PORTSC[{}] (op+0x{:03x}): {:#010x}", + port_idx, + 0x400 + portsc_off, + portsc + ); let ccs = (portsc >> 0) & 1; let ped = (portsc >> 1) & 1; let pp = (portsc >> 9) & 1; let pls = (portsc >> 5) & 0xf; let speed = (portsc >> 10) & 0xf; - info!(" Port {}: CCS={} PED={} PP={} PLS={} Speed={}", port_idx, ccs, ped, pp, pls, speed); + info!( + " Port {}: CCS={} PED={} PP={} PLS={} Speed={}", + port_idx, ccs, ped, pp, pls, speed + ); } } - + let portsc_ss = (xhci_base.as_ptr().add(0x430) as *const u32).read_volatile(); let portsc_usb2 = (xhci_base.as_ptr().add(0x420) as *const u32).read_volatile(); info!("xHCI PORTSC (raw 0x430): {:#010x}", portsc_ss); info!("xHCI PORTSC (raw 0x420): {:#010x}", portsc_usb2); - + let ccs = (portsc_ss >> 0) & 1; let ped = (portsc_ss >> 1) & 1; let pp = (portsc_ss >> 9) & 1; let pls = (portsc_ss >> 5) & 0xf; let speed = (portsc_ss >> 10) & 0xf; - info!(" SS Port: CCS={} PED={} PP={} PLS={} Speed={}", ccs, ped, pp, pls, speed); - + info!( + " SS Port: CCS={} PED={} PP={} PLS={} Speed={}", + ccs, ped, pp, pls, speed + ); + let dwc3_base = xhci_base.as_ptr().add(DWC3_OFFSET); let gctl = (dwc3_base.add(0x10) as *const u32).read_volatile(); let gsts = (dwc3_base.add(0x18) as *const u32).read_volatile(); @@ -917,7 +1031,7 @@ mod tests { info!("DWC3 GUSB2PHYCFG: {:#010x}", gusb2phycfg); info!("DWC3 GUSB3PIPECTL: {:#010x}", gusb3pipectl); info!("DWC3 GDBGLTSSM: {:#010x}", gdbgltssm); - + // DWC3 GHWPARAMS - hardware configuration registers (CRITICAL for port count!) // These are at offsets 0x40-0x5C from DWC3 base (not xHCI base) let ghwparams0 = (dwc3_base.add(0x40) as *const u32).read_volatile(); @@ -931,29 +1045,42 @@ mod tests { info!("DWC3 GHWPARAMS0: {:#010x}", ghwparams0); info!("DWC3 GHWPARAMS1: {:#010x}", ghwparams1); info!("DWC3 GHWPARAMS2: {:#010x}", ghwparams2); - info!("DWC3 GHWPARAMS3: {:#010x} (SSPHY_IFC[1:0]={}, HSPHY_IFC[3:2]={})", - ghwparams3, ghwparams3 & 3, (ghwparams3 >> 2) & 3); + info!( + "DWC3 GHWPARAMS3: {:#010x} (SSPHY_IFC[1:0]={}, HSPHY_IFC[3:2]={})", + ghwparams3, + ghwparams3 & 3, + (ghwparams3 >> 2) & 3 + ); info!("DWC3 GHWPARAMS4: {:#010x}", ghwparams4); info!("DWC3 GHWPARAMS5: {:#010x}", ghwparams5); info!("DWC3 GHWPARAMS6: {:#010x}", ghwparams6); - info!("DWC3 GHWPARAMS7: {:#010x} (num_hs_phy_ports[2:0]={}, num_ss_phy_ports[5:3]={})", - ghwparams7, ghwparams7 & 7, (ghwparams7 >> 3) & 7); - + info!( + "DWC3 GHWPARAMS7: {:#010x} (num_hs_phy_ports[2:0]={}, num_ss_phy_ports[5:3]={})", + ghwparams7, + ghwparams7 & 7, + (ghwparams7 >> 3) & 7 + ); + // Decode GHWPARAMS3 SSPHY interface let ssphy_ifc = ghwparams3 & 3; let hsphy_ifc = (ghwparams3 >> 2) & 3; - info!(" GHWPARAMS3: SSPHY_IFC={} (0=dis,1=ena), HSPHY_IFC={} (0=dis,1=utmi,2=ulpi,3=both)", - ssphy_ifc, hsphy_ifc); - + info!( + " GHWPARAMS3: SSPHY_IFC={} (0=dis,1=ena), HSPHY_IFC={} (0=dis,1=utmi,2=ulpi,3=both)", + ssphy_ifc, hsphy_ifc + ); + let ltssm_state = gdbgltssm & 0xF; let ltssm_substate = (gdbgltssm >> 4) & 0xF; info!(" LTSSM: state={} substate={}", ltssm_state, ltssm_substate); - + let curmod = (gsts >> 0) & 0x3; let otg_ip = (gsts >> 20) & 1; let bus_err = (gsts >> 24) & 1; - info!(" GSTS: CurMode={} (0=dev,1=host,2=drd) OTG_IP={} BusErr={}", curmod, otg_ip, bus_err); - + info!( + " GSTS: CurMode={} (0=dev,1=host,2=drd) OTG_IP={} BusErr={}", + curmod, otg_ip, bus_err + ); + let con0 = (udphygrf_base.as_ptr().add(0x00) as *const u32).read_volatile(); let con1 = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); let con2 = (udphygrf_base.as_ptr().add(0x08) as *const u32).read_volatile(); @@ -962,37 +1089,50 @@ mod tests { info!("USBDPPHY1_GRF CON1: {:#010x}", con1); info!("USBDPPHY1_GRF CON2: {:#010x}", con2); info!("USBDPPHY1_GRF CON3: {:#010x}", con3); - + let low_pwrn = (con1 >> 13) & 1; let rx_lfps = (con1 >> 14) & 1; info!(" CON1: low_pwrn={} rx_lfps={}", low_pwrn, rx_lfps); - + let usb3otg1_con0 = (usbgrf_base.as_ptr().add(0x30) as *const u32).read_volatile(); let usb3otg1_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); - info!("USB_GRF USB3OTG1_CON0 (0x0030): {:#010x} (bus_filter_bypass={:#x})", usb3otg1_con0, usb3otg1_con0 & 0xF); + info!( + "USB_GRF USB3OTG1_CON0 (0x0030): {:#010x} (bus_filter_bypass={:#x})", + usb3otg1_con0, + usb3otg1_con0 & 0xF + ); info!("USB_GRF USB3OTG1_CON1 (0x0034): {:#010x}", usb3otg1_con1); - + let usb3otg1_status0 = (usbgrf_base.as_ptr().add(0x38) as *const u32).read_volatile(); - info!("USB_GRF USB3OTG1_STATUS0 (0x0038): {:#010x}", usb3otg1_status0); - + info!( + "USB_GRF USB3OTG1_STATUS0 (0x0038): {:#010x}", + usb3otg1_status0 + ); + let lcpll_done = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); let cdr_done_ln0 = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); let cdr_done_ln2 = (pma_base.as_ptr().add(0x1B84) as *const u32).read_volatile(); let lane_mux = (pma_base.as_ptr().add(0x0288) as *const u32).read_volatile(); info!("USBDP PHY1 PMA LCPLL_DONE (0x0350): {:#010x}", lcpll_done); - info!("USBDP PHY1 PMA CDR_DONE LN0 (0x0B84): {:#010x}", cdr_done_ln0); - info!("USBDP PHY1 PMA CDR_DONE LN2 (0x1B84): {:#010x}", cdr_done_ln2); + info!( + "USBDP PHY1 PMA CDR_DONE LN0 (0x0B84): {:#010x}", + cdr_done_ln0 + ); + info!( + "USBDP PHY1 PMA CDR_DONE LN2 (0x1B84): {:#010x}", + cdr_done_ln2 + ); info!("USBDP PHY1 PMA LANE_MUX (0x0288): {:#010x}", lane_mux); - + let afc_done = (lcpll_done >> 6) & 1; let lock_done = (lcpll_done >> 7) & 1; info!(" LCPLL: AFC_DONE={} LOCK_DONE={}", afc_done, lock_done); - + let cr_para_con = (pma_base.as_ptr().add(0x0000) as *const u32).read_volatile(); let dp_rstn = (pma_base.as_ptr().add(0x038C) as *const u32).read_volatile(); info!("USBDP PHY1 PMA CR_PARA_CON (0x0000): {:#010x}", cr_para_con); info!("USBDP PHY1 PMA DP_RSTN (0x038C): {:#010x}", dp_rstn); - + let ln0_mon_0 = (pma_base.as_ptr().add(0x0B00) as *const u32).read_volatile(); let ln0_mon_1 = (pma_base.as_ptr().add(0x0B04) as *const u32).read_volatile(); let ln0_mon_2 = (pma_base.as_ptr().add(0x0B80) as *const u32).read_volatile(); @@ -1001,47 +1141,65 @@ mod tests { info!("USBDP PHY1 LN0_MON (0x0B04): {:#010x}", ln0_mon_1); info!("USBDP PHY1 LN0_MON (0x0B80): {:#010x}", ln0_mon_2); info!("USBDP PHY1 LN1_MON (0x1B00): {:#010x}", ln1_mon_0); - + let trsv_ln0_00 = (pma_base.as_ptr().add(0x0800) as *const u32).read_volatile(); let trsv_ln1_00 = (pma_base.as_ptr().add(0x1000) as *const u32).read_volatile(); info!("USBDP PHY1 TRSV_LN0 (0x0800): {:#010x}", trsv_ln0_00); info!("USBDP PHY1 TRSV_LN1 (0x1000): {:#010x}", trsv_ln1_00); - + let trsv_ln0_reg0206 = (pma_base.as_ptr().add(0x0818) as *const u32).read_volatile(); let trsv_ln1_reg0406 = (pma_base.as_ptr().add(0x1018) as *const u32).read_volatile(); let ln0_tx_drv_en = trsv_ln0_reg0206 & 1; let ln1_tx_drv_en = trsv_ln1_reg0406 & 1; - info!("USBDP PHY1 TRSV_LN0_REG0206 (0x0818): {:#010x} (tx_drv_idrv_en={})", trsv_ln0_reg0206, ln0_tx_drv_en); - info!("USBDP PHY1 TRSV_LN1_REG0406 (0x1018): {:#010x} (tx_drv_idrv_en={})", trsv_ln1_reg0406, ln1_tx_drv_en); - + info!( + "USBDP PHY1 TRSV_LN0_REG0206 (0x0818): {:#010x} (tx_drv_idrv_en={})", + trsv_ln0_reg0206, ln0_tx_drv_en + ); + info!( + "USBDP PHY1 TRSV_LN1_REG0406 (0x1018): {:#010x} (tx_drv_idrv_en={})", + trsv_ln1_reg0406, ln1_tx_drv_en + ); + let trsv_ln2_reg0606 = (pma_base.as_ptr().add(0x1818) as *const u32).read_volatile(); let trsv_ln3_reg0806 = (pma_base.as_ptr().add(0x2018) as *const u32).read_volatile(); - info!("USBDP PHY1 TRSV_LN2_REG0606 (0x1818): {:#010x}", trsv_ln2_reg0606); - info!("USBDP PHY1 TRSV_LN3_REG0806 (0x2018): {:#010x}", trsv_ln3_reg0806); - + info!( + "USBDP PHY1 TRSV_LN2_REG0606 (0x1818): {:#010x}", + trsv_ln2_reg0606 + ); + info!( + "USBDP PHY1 TRSV_LN3_REG0806 (0x2018): {:#010x}", + trsv_ln3_reg0806 + ); + let trsv_ln0_reg0000 = (pma_base.as_ptr().add(0x0800) as *const u32).read_volatile(); let trsv_ln0_reg0002 = (pma_base.as_ptr().add(0x0808) as *const u32).read_volatile(); let trsv_ln0_reg0003 = (pma_base.as_ptr().add(0x080C) as *const u32).read_volatile(); - info!("USBDP PHY1 TRSV_LN0 (0x0800,0x0808,0x080C): {:#010x} {:#010x} {:#010x}", - trsv_ln0_reg0000, trsv_ln0_reg0002, trsv_ln0_reg0003); - + info!( + "USBDP PHY1 TRSV_LN0 (0x0800,0x0808,0x080C): {:#010x} {:#010x} {:#010x}", + trsv_ln0_reg0000, trsv_ln0_reg0002, trsv_ln0_reg0003 + ); + let trsv_ln1_reg0200 = (pma_base.as_ptr().add(0x1000) as *const u32).read_volatile(); let trsv_ln1_reg0202 = (pma_base.as_ptr().add(0x1008) as *const u32).read_volatile(); let trsv_ln1_reg0203 = (pma_base.as_ptr().add(0x100C) as *const u32).read_volatile(); - info!("USBDP PHY1 TRSV_LN1 (0x1000,0x1008,0x100C): {:#010x} {:#010x} {:#010x}", - trsv_ln1_reg0200, trsv_ln1_reg0202, trsv_ln1_reg0203); - + info!( + "USBDP PHY1 TRSV_LN1 (0x1000,0x1008,0x100C): {:#010x} {:#010x} {:#010x}", + trsv_ln1_reg0200, trsv_ln1_reg0202, trsv_ln1_reg0203 + ); + let cmn_reg0060 = (pma_base.as_ptr().add(0x0180) as *const u32).read_volatile(); let cmn_reg0063 = (pma_base.as_ptr().add(0x018C) as *const u32).read_volatile(); let cmn_reg0064 = (pma_base.as_ptr().add(0x0190) as *const u32).read_volatile(); - info!("USBDP PHY1 CMN (0x0180,0x018C,0x0190): {:#010x} {:#010x} {:#010x}", - cmn_reg0060, cmn_reg0063, cmn_reg0064); - + info!( + "USBDP PHY1 CMN (0x0180,0x018C,0x0190): {:#010x} {:#010x} {:#010x}", + cmn_reg0060, cmn_reg0063, cmn_reg0064 + ); + let ln0_rx_ctle = (pma_base.as_ptr().add(0x0A00) as *const u32).read_volatile(); let ln2_rx_ctle = (pma_base.as_ptr().add(0x1A00) as *const u32).read_volatile(); info!("USBDP PHY1 LN0_RX_CTLE (0x0A00): {:#010x}", ln0_rx_ctle); info!("USBDP PHY1 LN2_RX_CTLE (0x1A00): {:#010x}", ln2_rx_ctle); - + let pipe_phy_status = (pma_base.as_ptr().add(0x0004) as *const u32).read_volatile(); let pipe_power_present = (pma_base.as_ptr().add(0x0008) as *const u32).read_volatile(); info!("USBDP PHY1 PMA (0x0004): {:#010x}", pipe_phy_status); @@ -1055,22 +1213,24 @@ mod tests { let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; - + info!("DWC3 GCTL before host mode: {:#010x}", read_dwc3(GCTL)); info!("DWC3 GUSB2PHYCFG before: {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 GUSB3PIPECTL before: {:#010x}", read_dwc3(GUSB3PIPECTL)); - + info!( + "DWC3 GUSB3PIPECTL before: {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); + let mut gctl = read_dwc3(GCTL); gctl &= !GCTL_PRTCAPDIR_MASK; gctl |= GCTL_PRTCAPDIR_HOST; write_dwc3(GCTL, gctl); - + let mut phycfg = read_dwc3(GUSB2PHYCFG); phycfg &= !(GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_MASK); phycfg |= GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_16BIT; @@ -1078,33 +1238,41 @@ mod tests { phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; phycfg |= GUSB2PHYCFG_SUSPHY; write_dwc3(GUSB2PHYCFG, phycfg); - + let mut pipe = read_dwc3(GUSB3PIPECTL); pipe &= !GUSB3PIPECTL_DEPOCHANGE; pipe |= GUSB3PIPECTL_SUSPHY; write_dwc3(GUSB3PIPECTL, pipe); - + info!("DWC3 GCTL after host mode: {:#010x}", read_dwc3(GCTL)); info!("DWC3 GUSB2PHYCFG after: {:#010x}", read_dwc3(GUSB2PHYCFG)); info!("DWC3 GUSB3PIPECTL after: {:#010x}", read_dwc3(GUSB3PIPECTL)); } } - + fn init_dwc3_no_phy_reset() { let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; - info!("DWC3 init (no reset): GCTL before = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 init (no reset): GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 init (no reset): GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 init (no reset): GCTL before = {:#010x}", + read_dwc3(GCTL) + ); + info!( + "DWC3 init (no reset): GUSB2PHYCFG before = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 init (no reset): GUSB3PIPECTL before = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); let mut pipe = read_dwc3(GUSB3PIPECTL); pipe &= !GUSB3PIPECTL_DEPOCHANGE; @@ -1124,9 +1292,18 @@ mod tests { gctl |= GCTL_PRTCAPDIR_HOST; write_dwc3(GCTL, gctl); - info!("DWC3 init (no reset): GCTL after = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 init (no reset): GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 init (no reset): GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 init (no reset): GCTL after = {:#010x}", + read_dwc3(GCTL) + ); + info!( + "DWC3 init (no reset): GUSB2PHYCFG after = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 init (no reset): GUSB3PIPECTL after = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); } info!("DWC3 init (no reset) complete"); } @@ -1136,16 +1313,24 @@ mod tests { let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; - info!("DWC3 soft reset init: GCTL before = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 soft reset init: GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 soft reset init: GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 soft reset init: GCTL before = {:#010x}", + read_dwc3(GCTL) + ); + info!( + "DWC3 soft reset init: GUSB2PHYCFG before = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 soft reset init: GUSB3PIPECTL before = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); let mut gctl = read_dwc3(GCTL); gctl |= GCTL_CORESOFTRESET; @@ -1179,7 +1364,7 @@ mod tests { pipe = read_dwc3(GUSB3PIPECTL); pipe &= !GUSB3PIPECTL_DEPOCHANGE; - pipe &= !GUSB3PIPECTL_SUSPHY; // Disable SUSPHY to prevent PHY lock loss + pipe &= !GUSB3PIPECTL_SUSPHY; // Disable SUSPHY to prevent PHY lock loss write_dwc3(GUSB3PIPECTL, pipe); phycfg = read_dwc3(GUSB2PHYCFG); @@ -1187,7 +1372,7 @@ mod tests { phycfg |= GUSB2PHYCFG_PHYIF | GUSB2PHYCFG_USBTRDTIM_16BIT; phycfg &= !GUSB2PHYCFG_ENBLSLPM; phycfg &= !GUSB2PHYCFG_U2_FREECLK_EXISTS; - phycfg &= !GUSB2PHYCFG_SUSPHY; // Disable SUSPHY to prevent PHY lock loss + phycfg &= !GUSB2PHYCFG_SUSPHY; // Disable SUSPHY to prevent PHY lock loss write_dwc3(GUSB2PHYCFG, phycfg); gctl = read_dwc3(GCTL); @@ -1195,9 +1380,18 @@ mod tests { gctl |= GCTL_PRTCAPDIR_HOST; write_dwc3(GCTL, gctl); - info!("DWC3 soft reset init: GCTL after = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 soft reset init: GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 soft reset init: GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 soft reset init: GCTL after = {:#010x}", + read_dwc3(GCTL) + ); + info!( + "DWC3 soft reset init: GUSB2PHYCFG after = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 soft reset init: GUSB3PIPECTL after = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); } info!("DWC3 soft reset init complete"); } @@ -1212,26 +1406,33 @@ mod tests { let ped = (portsc >> 1) & 1; let pls = (portsc >> 5) & 0xf; let speed = (portsc >> 10) & 0xf; - info!("Port status {}: PORTSC={:#010x} CCS={} PED={} PLS={} Speed={}", - label, portsc, ccs, ped, pls, speed); + info!( + "Port status {}: PORTSC={:#010x} CCS={} PED={} PLS={} Speed={}", + label, portsc, ccs, ped, pls, speed + ); } } - + fn init_dwc3_host_mode_simple() { let xhci_base = iomap(USB3_1_BASE.into(), 0x10000); let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; info!("DWC3 simple init: GCTL before = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 simple init: GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 simple init: GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 simple init: GUSB2PHYCFG before = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 simple init: GUSB3PIPECTL before = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); let mut pipe = read_dwc3(GUSB3PIPECTL); pipe &= !GUSB3PIPECTL_DEPOCHANGE; @@ -1252,8 +1453,14 @@ mod tests { write_dwc3(GCTL, gctl); info!("DWC3 simple init: GCTL after = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 simple init: GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 simple init: GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 simple init: GUSB2PHYCFG after = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 simple init: GUSB3PIPECTL after = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); } info!("DWC3 simple init complete (no soft reset)"); } @@ -1265,11 +1472,14 @@ mod tests { let cdr_done = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); let afc_done = (lcpll_done >> 6) & 1; let lock_done = (lcpll_done >> 7) & 1; - + info!("PHY lock status after DWC3 soft reset:"); - info!(" LCPLL_DONE = {:#04x} (AFC_DONE={}, LOCK_DONE={})", lcpll_done, afc_done, lock_done); + info!( + " LCPLL_DONE = {:#04x} (AFC_DONE={}, LOCK_DONE={})", + lcpll_done, afc_done, lock_done + ); info!(" CDR_DONE = {:#04x}", cdr_done); - + if afc_done == 0 || lock_done == 0 { warn!("PHY PLL NOT LOCKED after soft reset!"); } @@ -1295,15 +1505,17 @@ mod tests { let cdr_done = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); let afc_done = (lcpll_done >> 6) & 1; let lock_done = (lcpll_done >> 7) & 1; - - info!("PHY lock check: LCPLL_DONE={:#04x} (AFC={}, LOCK={}), CDR_DONE={:#04x}", - lcpll_done, afc_done, lock_done, cdr_done); - + + info!( + "PHY lock check: LCPLL_DONE={:#04x} (AFC={}, LOCK={}), CDR_DONE={:#04x}", + lcpll_done, afc_done, lock_done, cdr_done + ); + if afc_done == 1 && lock_done == 1 && cdr_done == 0x0f { info!("PHY PLL locked successfully"); return true; } - + warn!("PHY PLL not locked, waiting more..."); for retry in 0..10 { spin_delay_ms(100); @@ -1317,7 +1529,7 @@ mod tests { return true; } } - + false } } @@ -1327,9 +1539,8 @@ mod tests { let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; @@ -1408,16 +1619,18 @@ mod tests { let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; let gctl_before = read_dwc3(GCTL); let prtcap_before = (gctl_before >> 12) & 0x3; - info!("DWC3 early host mode: GCTL={:#010x} PRTCAPDIR={}", gctl_before, prtcap_before); + info!( + "DWC3 early host mode: GCTL={:#010x} PRTCAPDIR={}", + gctl_before, prtcap_before + ); let mut gctl = gctl_before; gctl &= !GCTL_PRTCAPDIR_MASK; @@ -1426,7 +1639,10 @@ mod tests { let gctl_after = read_dwc3(GCTL); let prtcap_after = (gctl_after >> 12) & 0x3; - info!("DWC3 early host mode: GCTL={:#010x} PRTCAPDIR={}", gctl_after, prtcap_after); + info!( + "DWC3 early host mode: GCTL={:#010x} PRTCAPDIR={}", + gctl_after, prtcap_after + ); } } @@ -1435,9 +1651,8 @@ mod tests { let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; @@ -1447,14 +1662,17 @@ mod tests { info!("GUSB2PHYCFG before: {:#010x}", read_dwc3(GUSB2PHYCFG)); info!("GUSB3PIPECTL before: {:#010x}", read_dwc3(GUSB3PIPECTL)); info!("DCTL before: {:#010x}", read_dwc3(DCTL)); - + info!("Step 0: Device soft reset (DCTL.CSFTRST)..."); write_dwc3(DCTL, DCTL_CSFTRST); let mut timeout = 5000u32; loop { let dctl = read_dwc3(DCTL); if (dctl & DCTL_CSFTRST) == 0 { - info!("Device soft reset complete after {} iterations", 5000 - timeout); + info!( + "Device soft reset complete after {} iterations", + 5000 - timeout + ); break; } timeout -= 1; @@ -1471,7 +1689,7 @@ mod tests { // U-Boot SETS SUSPHY for DWC3 > 1.94a (RK3588 doesn't have dis_u3_susphy_quirk) pipe |= GUSB3PIPECTL_SUSPHY; write_dwc3(GUSB3PIPECTL, pipe); - + info!("Step 2: Configure GUSB2PHYCFG..."); let mut phycfg = read_dwc3(GUSB2PHYCFG); phycfg |= GUSB2PHYCFG_PHYIF; @@ -1502,9 +1720,8 @@ mod tests { let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; @@ -1547,24 +1764,27 @@ mod tests { const CMD_RUN: u32 = 1 << 0; const CMD_RESET: u32 = 1 << 1; const STS_HALT: u32 = 1 << 0; - const STS_CNR: u32 = 1 << 11; // Controller Not Ready + const STS_CNR: u32 = 1 << 11; // Controller Not Ready unsafe { let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); let op_base = xhci_base.as_ptr().add(caplength as usize); - + let usbcmd_ptr = op_base.add(0x00) as *mut u32; let usbsts_ptr = op_base.add(0x04) as *const u32; - + let usbcmd = usbcmd_ptr.read_volatile(); let usbsts = usbsts_ptr.read_volatile(); - info!("Before reset: USBCMD={:#010x}, USBSTS={:#010x}", usbcmd, usbsts); - + info!( + "Before reset: USBCMD={:#010x}, USBSTS={:#010x}", + usbcmd, usbsts + ); + // Step 1: Halt the controller if not already halted if (usbsts & STS_HALT) == 0 { info!("Halting xHCI controller..."); let cmd = usbcmd & !CMD_RUN; usbcmd_ptr.write_volatile(cmd); - + // Wait for halt for _ in 0..1000 { let sts = usbsts_ptr.read_volatile(); @@ -1577,12 +1797,12 @@ mod tests { } } } - + // Step 2: Reset the controller (like U-Boot xhci_reset) info!("Resetting xHCI controller (CMD_RESET)..."); let cmd = usbcmd_ptr.read_volatile(); usbcmd_ptr.write_volatile(cmd | CMD_RESET); - + // Wait for reset to complete (CMD_RESET bit clears) for i in 0..1000 { let cmd = usbcmd_ptr.read_volatile(); @@ -1594,7 +1814,7 @@ mod tests { core::hint::spin_loop(); } } - + // Step 3: Wait for Controller Not Ready to clear for i in 0..1000 { let sts = usbsts_ptr.read_volatile(); @@ -1606,50 +1826,69 @@ mod tests { core::hint::spin_loop(); } } - + let usbcmd_after_reset = usbcmd_ptr.read_volatile(); let usbsts_after_reset = usbsts_ptr.read_volatile(); - info!("After reset: USBCMD={:#010x}, USBSTS={:#010x}", usbcmd_after_reset, usbsts_after_reset); - + info!( + "After reset: USBCMD={:#010x}, USBSTS={:#010x}", + usbcmd_after_reset, usbsts_after_reset + ); + // Check PHY PLL lock immediately after xHCI reset let pma_base_check = iomap(0xfed98000.into(), 0x10000); - let lcpll_after_reset = (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile(); - let cdr_after_reset = (pma_base_check.as_ptr().add(0x0B84) as *const u32).read_volatile(); - info!("PHY PLL after xHCI reset: LCPLL={:#04x} CDR={:#04x} (AFC={}, LOCK={})", - lcpll_after_reset, cdr_after_reset, - (lcpll_after_reset >> 6) & 1, (lcpll_after_reset >> 7) & 1); - + let lcpll_after_reset = + (pma_base_check.as_ptr().add(0x0350) as *const u32).read_volatile(); + let cdr_after_reset = + (pma_base_check.as_ptr().add(0x0B84) as *const u32).read_volatile(); + info!( + "PHY PLL after xHCI reset: LCPLL={:#04x} CDR={:#04x} (AFC={}, LOCK={})", + lcpll_after_reset, + cdr_after_reset, + (lcpll_after_reset >> 6) & 1, + (lcpll_after_reset >> 7) & 1 + ); + let hcsparams1 = (xhci_base.as_ptr().add(0x04) as *const u32).read_volatile(); let max_ports = ((hcsparams1 >> 24) & 0xff) as usize; info!("xHCI has {} ports, powering on all ports...", max_ports); - + const PORT_POWER: u32 = 1 << 9; for i in 0..max_ports { let portsc_offset = 0x400 + i * 0x10; let portsc_ptr = op_base.add(portsc_offset) as *mut u32; let portsc = portsc_ptr.read_volatile(); - info!("Port {} PORTSC before power: {:#010x} (PP={})", i, portsc, (portsc >> 9) & 1); - + info!( + "Port {} PORTSC before power: {:#010x} (PP={})", + i, + portsc, + (portsc >> 9) & 1 + ); + if (portsc & PORT_POWER) == 0 { let new_portsc = portsc | PORT_POWER; portsc_ptr.write_volatile(new_portsc); info!("Port {} power enabled", i); } } - + spin_delay_ms(20); - + for i in 0..max_ports { let portsc_offset = 0x400 + i * 0x10; let portsc_ptr = op_base.add(portsc_offset) as *const u32; let portsc = portsc_ptr.read_volatile(); - info!("Port {} PORTSC after power: {:#010x} (PP={})", i, portsc, (portsc >> 9) & 1); + info!( + "Port {} PORTSC after power: {:#010x} (PP={})", + i, + portsc, + (portsc >> 9) & 1 + ); } - + info!("Starting xHCI controller (CMD_RUN)..."); let cmd = usbcmd_ptr.read_volatile(); usbcmd_ptr.write_volatile(cmd | CMD_RUN); - + // Wait for STS_HALT to clear for i in 0..100 { let sts = usbsts_ptr.read_volatile(); @@ -1661,10 +1900,13 @@ mod tests { core::hint::spin_loop(); } } - + let usbcmd_after = usbcmd_ptr.read_volatile(); let usbsts_after = usbsts_ptr.read_volatile(); - info!("After start: USBCMD={:#010x}, USBSTS={:#010x}", usbcmd_after, usbsts_after); + info!( + "After start: USBCMD={:#010x}, USBSTS={:#010x}", + usbcmd_after, usbsts_after + ); } } @@ -1673,16 +1915,21 @@ mod tests { let dwc3_base = unsafe { xhci_base.as_ptr().add(DWC3_OFFSET) }; unsafe { - let read_dwc3 = |off: usize| -> u32 { - (dwc3_base.add(off) as *const u32).read_volatile() - }; + let read_dwc3 = + |off: usize| -> u32 { (dwc3_base.add(off) as *const u32).read_volatile() }; let write_dwc3 = |off: usize, val: u32| { (dwc3_base.add(off) as *mut u32).write_volatile(val); }; info!("DWC3 init: GCTL before = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 init: GUSB2PHYCFG before = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 init: GUSB3PIPECTL before = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 init: GUSB2PHYCFG before = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 init: GUSB3PIPECTL before = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); let mut gctl = read_dwc3(GCTL); gctl |= GCTL_CORESOFTRESET; @@ -1727,8 +1974,14 @@ mod tests { write_dwc3(GCTL, gctl); info!("DWC3 init: GCTL after = {:#010x}", read_dwc3(GCTL)); - info!("DWC3 init: GUSB2PHYCFG after = {:#010x}", read_dwc3(GUSB2PHYCFG)); - info!("DWC3 init: GUSB3PIPECTL after = {:#010x}", read_dwc3(GUSB3PIPECTL)); + info!( + "DWC3 init: GUSB2PHYCFG after = {:#010x}", + read_dwc3(GUSB2PHYCFG) + ); + info!( + "DWC3 init: GUSB3PIPECTL after = {:#010x}", + read_dwc3(GUSB3PIPECTL) + ); } info!("DWC3 full init complete"); } @@ -1858,7 +2111,8 @@ mod tests { const USB2PHY1_GRF_SIZE: usize = 0x8000; let grf_base = iomap(USB2PHY1_GRF_SYSCON_BASE.into(), USB2PHY1_GRF_SIZE); - let phy_base = unsafe { core::ptr::NonNull::new_unchecked(grf_base.as_ptr().add(USB2PHY1_OFFSET)) }; + let phy_base = + unsafe { core::ptr::NonNull::new_unchecked(grf_base.as_ptr().add(USB2PHY1_OFFSET)) }; let cru_base = iomap(CRU_BASE.into(), 0x10000); unsafe { @@ -1907,13 +2161,25 @@ mod tests { }; // Read PHY registers (at offset 0x4000) - info!("USB2PHY1 (phy): reg 0x0008 = {:#010x}", read_phy_reg(0x0008)); - info!("USB2PHY1 (phy): reg 0x000c = {:#010x}", read_phy_reg(0x000c)); - + info!( + "USB2PHY1 (phy): reg 0x0008 = {:#010x}", + read_phy_reg(0x0008) + ); + info!( + "USB2PHY1 (phy): reg 0x000c = {:#010x}", + read_phy_reg(0x000c) + ); + // Read GRF syscon registers (at base, for bvalid) - info!("USB2PHY1 (grf): reg 0x0008 = {:#010x}", read_grf_reg(0x0008)); - info!("USB2PHY1 (grf): reg 0x0010 = {:#010x}", read_grf_reg(0x0010)); - + info!( + "USB2PHY1 (grf): reg 0x0008 = {:#010x}", + read_grf_reg(0x0008) + ); + info!( + "USB2PHY1 (grf): reg 0x0010 = {:#010x}", + read_grf_reg(0x0010) + ); + let siddq = read_phy_reg(0x0008); if (siddq & (1 << 13)) != 0 { info!("USB2PHY1: SIDDQ set, clearing to power on analog block"); @@ -1934,16 +2200,24 @@ mod tests { } let sus_before = read_phy_reg(0x000c); - info!("USB2PHY1: phy_sus (0x000c) before = {:#010x}, bit11 = {}", sus_before, (sus_before >> 11) & 1); - + info!( + "USB2PHY1: phy_sus (0x000c) before = {:#010x}, bit11 = {}", + sus_before, + (sus_before >> 11) & 1 + ); + write_phy_reg(0x000c, 1 << 11, 0); - + for _ in 0..200000 { core::hint::spin_loop(); } - + let sus_after = read_phy_reg(0x000c); - info!("USB2PHY1: phy_sus (0x000c) after = {:#010x}, bit11 = {}", sus_after, (sus_after >> 11) & 1); + info!( + "USB2PHY1: phy_sus (0x000c) after = {:#010x}, bit11 = {}", + sus_after, + (sus_after >> 11) & 1 + ); // Enable bvalid via software control in GRF syscon (NOT PHY regs!) // This is CRITICAL for device detection in USBDP combo PHY! @@ -1955,41 +2229,51 @@ mod tests { // 0x3 = use external VBUS valid indicator and assert it // CON4 bits 3-2: sft_vbus_sel (bit 3) + sft_vbus (bit 2) // 0x3 = use software VBUS control and assert VBUS valid - + info!("USB2PHY1: Enabling bvalid via software control in GRF..."); - + // GRF CON2 (0x0008): Set vbusvldextsel=1, vbusvldext=1 (bits 1:0 = 0x3) let con2_before = read_grf_reg(0x0008); write_grf_reg(0x0008, 0x3, 0x3); let con2_after = read_grf_reg(0x0008); - info!("USB2PHY1 GRF CON2 (0x0008) bvalid: {:#010x} -> {:#010x}", con2_before, con2_after); - + info!( + "USB2PHY1 GRF CON2 (0x0008) bvalid: {:#010x} -> {:#010x}", + con2_before, con2_after + ); + // GRF CON4 (0x0010): Set sft_vbus_sel=1, sft_vbus=1 (bits 3:2 = 0xC) let con4_before = read_grf_reg(0x0010); write_grf_reg(0x0010, 0xC, 0xC); let con4_after = read_grf_reg(0x0010); - info!("USB2PHY1 GRF CON4 (0x0010) bvalid: {:#010x} -> {:#010x}", con4_before, con4_after); - + info!( + "USB2PHY1 GRF CON4 (0x0010) bvalid: {:#010x} -> {:#010x}", + con4_before, con4_after + ); + // Wait for UTMI clock to stabilize after bvalid assertion for _ in 0..200000 { core::hint::spin_loop(); } - + // Read STATUS0 to check actual PHY status let status0 = read_grf_reg(0x00C0); let utmi_bvalid = (status0 >> 6) & 1; let utmi_linestate = (status0 >> 9) & 0x3; let utmi_vbusvalid = (status0 >> 8) & 1; - info!("USB2PHY1 STATUS0 (grf base): {:#010x} (bvalid={}, linestate={:02b}, vbusvalid={})", - status0, utmi_bvalid, utmi_linestate, utmi_vbusvalid); - + info!( + "USB2PHY1 STATUS0 (grf base): {:#010x} (bvalid={}, linestate={:02b}, vbusvalid={})", + status0, utmi_bvalid, utmi_linestate, utmi_vbusvalid + ); + // Also read STATUS0 at PHY offset (0x4000 + 0xC0) let phy_status0 = read_phy_reg(0x00C0); let phy_bvalid = (phy_status0 >> 6) & 1; let phy_linestate = (phy_status0 >> 9) & 0x3; let phy_vbusvalid = (phy_status0 >> 8) & 1; - info!("USB2PHY1 STATUS0 (phy offset): {:#010x} (bvalid={}, linestate={:02b}, vbusvalid={})", - phy_status0, phy_bvalid, phy_linestate, phy_vbusvalid); + info!( + "USB2PHY1 STATUS0 (phy offset): {:#010x} (bvalid={}, linestate={:02b}, vbusvalid={})", + phy_status0, phy_bvalid, phy_linestate, phy_vbusvalid + ); } info!("usb2phy1 fully initialized with bvalid enabled"); @@ -2019,7 +2303,6 @@ mod tests { fn init_usbdp_phy1_full(_fdt: &Fdt<'static>) -> Result<(), PhyError> { info!("=== Starting full USBDP PHY1 initialization ==="); - let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); let udphygrf_base = iomap(USBDPPHY1_GRF_BASE.into(), 0x4000); let usbgrf_base = iomap(USB_GRF_BASE.into(), 0x1000); @@ -2033,28 +2316,37 @@ mod tests { let lcpll = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); let cdr = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); let locked = (lcpll & 0xC0) == 0xC0 && (cdr & 0x0F) != 0; - info!("PHY lock check: LCPLL={:#04x} CDR={:#04x} already_init={}", lcpll, cdr, locked); + info!( + "PHY lock check: LCPLL={:#04x} CDR={:#04x} already_init={}", + lcpll, cdr, locked + ); locked }; if phy_already_initialized { info!("PHY already initialized by bootloader, skipping full PHY init"); info!("Only configuring USB_GRF to enable USB3 port..."); - + unsafe { let con1_ptr = usbgrf_base.as_ptr().add(0x34) as *mut u32; let old_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); // Write 0x1100 to enable USB3: host_num_u3_port=1, host_num_u2_port=1 con1_ptr.write_volatile((0xFFFF << 16) | 0x1100); let new_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); - info!("USB3OTG1_CON1: {:#010x} -> {:#010x} (USB3 port enabled)", old_con1, new_con1); - + info!( + "USB3OTG1_CON1: {:#010x} -> {:#010x} (USB3 port enabled)", + old_con1, new_con1 + ); + let grf_con1_ptr = udphygrf_base.as_ptr().add(0x04) as *mut u32; let old_grf = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); grf_con1_ptr.write_volatile(old_grf | (0x3 << 29) | (0x3 << 13)); let new_grf = (udphygrf_base.as_ptr().add(0x04) as *const u32).read_volatile(); - info!("USBDPPHY1_GRF CON1: {:#010x} -> {:#010x} (rx_lfps, low_pwrn enabled)", old_grf, new_grf); + info!( + "USBDPPHY1_GRF CON1: {:#010x} -> {:#010x} (rx_lfps, low_pwrn enabled)", + old_grf, new_grf + ); } - + info!("=== USBDP PHY1 quick config complete (bootloader-init path) ==="); return Ok(()); } @@ -2085,39 +2377,53 @@ mod tests { let softrst_con02 = (cru_base.as_ptr().add(0xa08) as *const u32).read_volatile(); let softrst_con03 = (cru_base.as_ptr().add(0xa0c) as *const u32).read_volatile(); let softrst_con72 = (cru_base.as_ptr().add(0xb20) as *const u32).read_volatile(); - info!("CRU reset state: CON02={:#010x} CON03={:#010x} CON72={:#010x}", - softrst_con02, softrst_con03, softrst_con72); - + info!( + "CRU reset state: CON02={:#010x} CON03={:#010x} CON72={:#010x}", + softrst_con02, softrst_con03, softrst_con72 + ); + let init_reset = (softrst_con02 >> 15) & 1; let cmn_reset = (softrst_con03 >> 0) & 1; let lane_reset = (softrst_con03 >> 1) & 1; let pcs_reset = (softrst_con03 >> 2) & 1; let pma_apb_reset = (softrst_con72 >> 4) & 1; - info!("PHY1 reset bits: init={} cmn={} lane={} pcs={} pma_apb={}", - init_reset, cmn_reset, lane_reset, pcs_reset, pma_apb_reset); - + info!( + "PHY1 reset bits: init={} cmn={} lane={} pcs={} pma_apb={}", + init_reset, cmn_reset, lane_reset, pcs_reset, pma_apb_reset + ); + let gate_con02 = (cru_base.as_ptr().add(0x0808) as *const u32).read_volatile(); let gate_con72 = (cru_base.as_ptr().add(0x0920) as *const u32).read_volatile(); - info!("Clock gates: GATE_CON02={:#010x} (IMMORTAL1 bit15={}) GATE_CON72={:#010x} (PCLK_PHY1 bit4={})", - gate_con02, (gate_con02 >> 15) & 1, gate_con72, (gate_con72 >> 4) & 1); - + info!( + "Clock gates: GATE_CON02={:#010x} (IMMORTAL1 bit15={}) GATE_CON72={:#010x} (PCLK_PHY1 bit4={})", + gate_con02, + (gate_con02 >> 15) & 1, + gate_con72, + (gate_con72 >> 4) & 1 + ); + let lane_mux = (pma_base.as_ptr().add(0x0288) as *const u32).read_volatile(); let lcpll = (pma_base.as_ptr().add(0x0350) as *const u32).read_volatile(); let cdr = (pma_base.as_ptr().add(0x0B84) as *const u32).read_volatile(); - info!("PHY state before init: LANE_MUX={:#04x} LCPLL={:#04x} CDR={:#04x}", - lane_mux, lcpll, cdr); + info!( + "PHY state before init: LANE_MUX={:#04x} LCPLL={:#04x} CDR={:#04x}", + lane_mux, lcpll, cdr + ); } info!("Enabling PHY clocks: USBDP_PHY1_IMMORTAL, PCLK_USBDPPHY1"); unsafe { let gate_con02_ptr = cru_base.as_ptr().add(0x0808) as *mut u32; gate_con02_ptr.write_volatile((1u32 << 31) | (0u32 << 15)); - + let gate_con72_ptr = cru_base.as_ptr().add(0x0920) as *mut u32; gate_con72_ptr.write_volatile((1u32 << 20) | (0u32 << 4)); - + let gate_con02_after = (cru_base.as_ptr().add(0x0808) as *const u32).read_volatile(); let gate_con72_after = (cru_base.as_ptr().add(0x0920) as *const u32).read_volatile(); - info!("After clock enable: GATE_CON02={:#010x} GATE_CON72={:#010x}", gate_con02_after, gate_con72_after); + info!( + "After clock enable: GATE_CON02={:#010x} GATE_CON72={:#010x}", + gate_con02_after, gate_con72_after + ); } // CRITICAL: Disable USB3 port BEFORE PHY init (like U-Boot/Linux) // This ensures the port doesn't try to use the PHY until it's fully initialized @@ -2128,13 +2434,16 @@ mod tests { let old_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); con1_ptr.write_volatile((0xFFFF << 16) | 0x0188); let new_con1 = (usbgrf_base.as_ptr().add(0x34) as *const u32).read_volatile(); - info!("USB3OTG1_CON1: {:#010x} -> {:#010x} (USB3 port DISABLED for PHY init)", old_con1, new_con1); + info!( + "USB3OTG1_CON1: {:#010x} -> {:#010x} (USB3 port DISABLED for PHY init)", + old_con1, new_con1 + ); } info!("Step 0b: Asserting ALL PHY resets"); - assert_phy_reset(cru_base, 47); // init - assert_phy_reset(cru_base, 48); // cmn - assert_phy_reset(cru_base, 49); // lane - assert_phy_reset(cru_base, 50); // pcs_apb + assert_phy_reset(cru_base, 47); // init + assert_phy_reset(cru_base, 48); // cmn + assert_phy_reset(cru_base, 49); // lane + assert_phy_reset(cru_base, 50); // pcs_apb assert_phy_reset(cru_base, 1156); // pma_apb for _ in 0..10000 { core::hint::spin_loop(); @@ -2143,8 +2452,10 @@ mod tests { let softrst_con02 = (cru_base.as_ptr().add(0xa08) as *const u32).read_volatile(); let softrst_con03 = (cru_base.as_ptr().add(0xa0c) as *const u32).read_volatile(); let softrst_con72 = (cru_base.as_ptr().add(0xb20) as *const u32).read_volatile(); - info!("After assert: CON02={:#010x} CON03={:#010x} CON72={:#010x}", - softrst_con02, softrst_con03, softrst_con72); + info!( + "After assert: CON02={:#010x} CON03={:#010x} CON72={:#010x}", + softrst_con02, softrst_con03, softrst_con72 + ); } info!("Step 1: Enable rx_lfps for USB mode"); @@ -2168,38 +2479,70 @@ mod tests { // Step 4: Write init sequence to PMA // From U-Boot rk3588_udphy_init_sequence const INIT_SEQUENCE: &[(u16, u8)] = &[ - (0x0104, 0x44), (0x0234, 0xE8), - (0x0248, 0x44), (0x028C, 0x18), - (0x081C, 0xE5), (0x0878, 0x00), - (0x0994, 0x1C), (0x0AF0, 0x00), - (0x181C, 0xE5), (0x1878, 0x00), - (0x1994, 0x1C), (0x1AF0, 0x00), - (0x0428, 0x60), (0x0D58, 0x33), - (0x1D58, 0x33), (0x0990, 0x74), - (0x0D64, 0x17), (0x08C8, 0x13), - (0x1990, 0x74), (0x1D64, 0x17), - (0x18C8, 0x13), (0x0D90, 0x40), - (0x0DA8, 0x40), (0x0DC0, 0x40), - (0x0DD8, 0x40), (0x1D90, 0x40), - (0x1DA8, 0x40), (0x1DC0, 0x40), - (0x1DD8, 0x40), (0x03C0, 0x30), - (0x03C4, 0x06), (0x0E10, 0x00), - (0x1E10, 0x00), (0x043C, 0x0F), - (0x0D2C, 0xFF), (0x1D2C, 0xFF), - (0x0D34, 0x0F), (0x1D34, 0x0F), - (0x08FC, 0x2A), (0x0914, 0x28), - (0x0A30, 0x03), (0x0E38, 0x05), - (0x0ECC, 0x27), (0x0ED0, 0x22), - (0x0ED4, 0x26), (0x18FC, 0x2A), - (0x1914, 0x28), (0x1A30, 0x03), - (0x1E38, 0x05), (0x1ECC, 0x27), - (0x1ED0, 0x22), (0x1ED4, 0x26), - (0x0048, 0x0F), (0x0060, 0x3C), - (0x0064, 0xF7), (0x006C, 0x20), - (0x0070, 0x7D), (0x0074, 0x68), - (0x0AF4, 0x1A), (0x1AF4, 0x1A), - (0x0440, 0x3F), (0x10D4, 0x08), - (0x20D4, 0x08), (0x00D4, 0x30), + (0x0104, 0x44), + (0x0234, 0xE8), + (0x0248, 0x44), + (0x028C, 0x18), + (0x081C, 0xE5), + (0x0878, 0x00), + (0x0994, 0x1C), + (0x0AF0, 0x00), + (0x181C, 0xE5), + (0x1878, 0x00), + (0x1994, 0x1C), + (0x1AF0, 0x00), + (0x0428, 0x60), + (0x0D58, 0x33), + (0x1D58, 0x33), + (0x0990, 0x74), + (0x0D64, 0x17), + (0x08C8, 0x13), + (0x1990, 0x74), + (0x1D64, 0x17), + (0x18C8, 0x13), + (0x0D90, 0x40), + (0x0DA8, 0x40), + (0x0DC0, 0x40), + (0x0DD8, 0x40), + (0x1D90, 0x40), + (0x1DA8, 0x40), + (0x1DC0, 0x40), + (0x1DD8, 0x40), + (0x03C0, 0x30), + (0x03C4, 0x06), + (0x0E10, 0x00), + (0x1E10, 0x00), + (0x043C, 0x0F), + (0x0D2C, 0xFF), + (0x1D2C, 0xFF), + (0x0D34, 0x0F), + (0x1D34, 0x0F), + (0x08FC, 0x2A), + (0x0914, 0x28), + (0x0A30, 0x03), + (0x0E38, 0x05), + (0x0ECC, 0x27), + (0x0ED0, 0x22), + (0x0ED4, 0x26), + (0x18FC, 0x2A), + (0x1914, 0x28), + (0x1A30, 0x03), + (0x1E38, 0x05), + (0x1ECC, 0x27), + (0x1ED0, 0x22), + (0x1ED4, 0x26), + (0x0048, 0x0F), + (0x0060, 0x3C), + (0x0064, 0xF7), + (0x006C, 0x20), + (0x0070, 0x7D), + (0x0074, 0x68), + (0x0AF4, 0x1A), + (0x1AF4, 0x1A), + (0x0440, 0x3F), + (0x10D4, 0x08), + (0x20D4, 0x08), + (0x00D4, 0x30), (0x0024, 0x6e), ]; @@ -2209,46 +2552,85 @@ mod tests { ptr.write_volatile(value as u32); } } - info!("Step 4: Init sequence written ({} registers)", INIT_SEQUENCE.len()); + info!( + "Step 4: Init sequence written ({} registers)", + INIT_SEQUENCE.len() + ); // Step 5: Write 24MHz reference clock configuration const REFCLK_CFG: &[(u16, u8)] = &[ - (0x0090, 0x68), (0x0094, 0x68), - (0x0128, 0x24), (0x012c, 0x44), - (0x0130, 0x3f), (0x0134, 0x44), - (0x015c, 0xa9), (0x0160, 0x71), - (0x0164, 0x71), (0x0168, 0xa9), - (0x0174, 0xa9), (0x0178, 0x71), - (0x017c, 0x71), (0x0180, 0xa9), - (0x018c, 0x41), (0x0190, 0x00), - (0x0194, 0x05), (0x01ac, 0x2a), - (0x01b0, 0x17), (0x01b4, 0x17), - (0x01b8, 0x2a), (0x01c8, 0x04), - (0x01cc, 0x08), (0x01d0, 0x08), - (0x01d4, 0x04), (0x01d8, 0x20), - (0x01dc, 0x01), (0x01e0, 0x09), - (0x01e4, 0x03), (0x01f0, 0x29), - (0x01f4, 0x02), (0x01f8, 0x02), - (0x01fc, 0x29), (0x0208, 0x2a), - (0x020c, 0x17), (0x0210, 0x17), - (0x0214, 0x2a), (0x0224, 0x20), - (0x03f0, 0x0d), (0x03f4, 0x09), - (0x03f8, 0x09), (0x03fc, 0x0d), - (0x0404, 0x0e), (0x0408, 0x14), - (0x040c, 0x14), (0x0410, 0x3b), - (0x0ce0, 0x68), (0x0ce8, 0xd0), - (0x0cf0, 0x87), (0x0cf8, 0x70), - (0x0d00, 0x70), (0x0d08, 0xa9), - (0x1ce0, 0x68), (0x1ce8, 0xd0), - (0x1cf0, 0x87), (0x1cf8, 0x70), - (0x1d00, 0x70), (0x1d08, 0xa9), - (0x0a3c, 0xd0), (0x0a44, 0xd0), - (0x0a48, 0x01), (0x0a4c, 0x0d), - (0x0a54, 0xe0), (0x0a5c, 0xe0), - (0x0a64, 0xa8), (0x1a3c, 0xd0), - (0x1a44, 0xd0), (0x1a48, 0x01), - (0x1a4c, 0x0d), (0x1a54, 0xe0), - (0x1a5c, 0xe0), (0x1a64, 0xa8), + (0x0090, 0x68), + (0x0094, 0x68), + (0x0128, 0x24), + (0x012c, 0x44), + (0x0130, 0x3f), + (0x0134, 0x44), + (0x015c, 0xa9), + (0x0160, 0x71), + (0x0164, 0x71), + (0x0168, 0xa9), + (0x0174, 0xa9), + (0x0178, 0x71), + (0x017c, 0x71), + (0x0180, 0xa9), + (0x018c, 0x41), + (0x0190, 0x00), + (0x0194, 0x05), + (0x01ac, 0x2a), + (0x01b0, 0x17), + (0x01b4, 0x17), + (0x01b8, 0x2a), + (0x01c8, 0x04), + (0x01cc, 0x08), + (0x01d0, 0x08), + (0x01d4, 0x04), + (0x01d8, 0x20), + (0x01dc, 0x01), + (0x01e0, 0x09), + (0x01e4, 0x03), + (0x01f0, 0x29), + (0x01f4, 0x02), + (0x01f8, 0x02), + (0x01fc, 0x29), + (0x0208, 0x2a), + (0x020c, 0x17), + (0x0210, 0x17), + (0x0214, 0x2a), + (0x0224, 0x20), + (0x03f0, 0x0d), + (0x03f4, 0x09), + (0x03f8, 0x09), + (0x03fc, 0x0d), + (0x0404, 0x0e), + (0x0408, 0x14), + (0x040c, 0x14), + (0x0410, 0x3b), + (0x0ce0, 0x68), + (0x0ce8, 0xd0), + (0x0cf0, 0x87), + (0x0cf8, 0x70), + (0x0d00, 0x70), + (0x0d08, 0xa9), + (0x1ce0, 0x68), + (0x1ce8, 0xd0), + (0x1cf0, 0x87), + (0x1cf8, 0x70), + (0x1d00, 0x70), + (0x1d08, 0xa9), + (0x0a3c, 0xd0), + (0x0a44, 0xd0), + (0x0a48, 0x01), + (0x0a4c, 0x0d), + (0x0a54, 0xe0), + (0x0a5c, 0xe0), + (0x0a64, 0xa8), + (0x1a3c, 0xd0), + (0x1a44, 0xd0), + (0x1a48, 0x01), + (0x1a4c, 0x0d), + (0x1a54, 0xe0), + (0x1a5c, 0xe0), + (0x1a64, 0xa8), ]; unsafe { @@ -2257,7 +2639,10 @@ mod tests { ptr.write_volatile(value as u32); } } - info!("Step 5: Refclk config written ({} registers)", REFCLK_CFG.len()); + info!( + "Step 5: Refclk config written ({} registers)", + REFCLK_CFG.len() + ); // Step 6: Configure lane mux - ALL lanes USB mode (matching U-Boot working config) // Note: DTS has rockchip,dp-lane-mux = <2 3> but U-Boot's usb start uses 0x00 (all USB) @@ -2270,7 +2655,10 @@ mod tests { let value: u32 = 0x00; let new_val = (current & !mask) | (value & mask); ptr.write_volatile(new_val); - info!("Lane mux: current=0x{:02x}, new=0x{:02x} (all USB)", current, new_val); + info!( + "Lane mux: current=0x{:02x}, new=0x{:02x} (all USB)", + current, new_val + ); } info!("Step 7: Deassert init reset (USB mode)"); @@ -2298,7 +2686,10 @@ mod tests { } timeout -= 1; if timeout == 0 { - warn!("LCPLL lock timeout! val=0x{:02x} (AFC={}, LOCK={})", val, afc_done, lock_done); + warn!( + "LCPLL lock timeout! val=0x{:02x} (AFC={}, LOCK={})", + val, afc_done, lock_done + ); break; } for _ in 0..10000 { @@ -2333,7 +2724,11 @@ mod tests { unsafe { let ptr = usbgrf_base.as_ptr().add(0x0030) as *const u32; let con0 = ptr.read_volatile(); - info!("Step 10: USB3OTG1_CON0: {:#010x} (bus_filter_bypass={:#x}) - NOT modifying", con0, con0 & 0xF); + info!( + "Step 10: USB3OTG1_CON0: {:#010x} (bus_filter_bypass={:#x}) - NOT modifying", + con0, + con0 & 0xF + ); } // Step 11: ALWAYS enable USB3 after PHY init completes @@ -2344,7 +2739,10 @@ mod tests { let val: u32 = (0xFFFF << 16) | 0x1100; ptr.write_volatile(val); let con1_after = (usbgrf_base.as_ptr().add(0x0034) as *const u32).read_volatile(); - info!("Step 11: ENABLE USB3 port (CON1: {:#010x} -> {:#010x})", current_con1, con1_after); + info!( + "Step 11: ENABLE USB3 port (CON1: {:#010x} -> {:#010x})", + current_con1, con1_after + ); } info!("=== USBDP PHY1 initialization complete ==="); @@ -2354,7 +2752,6 @@ mod tests { fn reinit_usbdp_phy1_after_reset(_fdt: &Fdt<'static>) -> Result<(), PhyError> { info!("=== Re-initializing USBDP PHY1 after DWC3 soft reset ==="); - let pma_base = iomap(USBDPPHY1_PMA_BASE.into(), 0x10000); let udphygrf_base = iomap(USBDPPHY1_GRF_BASE.into(), 0x4000); let cru_base = iomap(CRU_BASE.into(), 0x10000); @@ -2510,27 +2907,26 @@ mod tests { // Ensure GPIO3_B7 is configured as output let ddr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DDR_L) as *mut u32; ddr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); - + // Turn OFF VBUS let dr_ptr = gpio3_base.as_ptr().add(GPIO_SWPORT_DR_L) as *mut u32; dr_ptr.write_volatile(WRITE_MASK_BIT15 | 0); - + spin_delay_ms(off_ms); - + // Turn ON VBUS dr_ptr.write_volatile(WRITE_MASK_BIT15 | GPIO3_B7_BIT); - + spin_delay_ms(on_wait_ms); - + // Check if device connected let caplength = (xhci_base.as_ptr().add(0x00) as *const u8).read_volatile(); let op_base = xhci_base.as_ptr().add(caplength as usize); - let portsc = (op_base.add(0x410) as *const u32).read_volatile(); // Port 1 (USB3) + let portsc = (op_base.add(0x410) as *const u32).read_volatile(); // Port 1 (USB3) let ccs = portsc & 1; ccs == 1 } } - } trait Align {