use alloc::{collections::BTreeMap, string::String, sync::Arc};
use super::{
super::osal::Kernel,
CruOp,
consts::genmask,
udphy::{config::UdphyGrfReg, regmap::Regmap},
};
use crate::{Mmio, err::Result};
pub mod reg_offset {
pub const HS_DC_LEVEL: u32 = 0x0004;
pub const CLK_CONTROL: u32 = 0x0008;
pub const SUSPEND_CONTROL: u32 = 0x000c;
}
pub type Usb2PhyGrfReg = UdphyGrfReg;
#[derive(Debug, Clone, Copy)]
#[repr(usize)]
pub enum Usb2PhyPortId {
Otg,
Host,
Ports,
}
impl Usb2PhyPortId {
pub fn from_node_name(name: &str) -> Option<Self> {
match name {
"otg-port" => Some(Usb2PhyPortId::Otg),
"host-port" => Some(Usb2PhyPortId::Host),
_ => None,
}
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub struct Usb2PhyPortCfg {
pub phy_sus: Usb2PhyGrfReg,
pub utmi_ls: Usb2PhyGrfReg,
pub utmi_iddig: Usb2PhyGrfReg,
}
impl Usb2PhyPortCfg {
pub const fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
#[derive(Clone)]
#[allow(dead_code)]
pub struct Usb2PhyCfg {
pub reg: usize,
pub clkout_ctl: Usb2PhyGrfReg,
pub port_cfg: [Usb2PhyPortCfg; Usb2PhyPortId::Ports as usize],
pub phy_tuning: fn(&Usb2Phy) -> Result<()>,
}
pub struct Usb2PhyParam<'a> {
pub reg: usize,
pub port_kind: Usb2PhyPortId,
pub usb_grf: Mmio,
pub rst_list: &'a [(&'a str, u64)],
}
pub struct Usb2Phy {
grf: Regmap,
port_kind: Usb2PhyPortId,
cfg: &'static Usb2PhyCfg,
cru: Arc<dyn CruOp>,
rsts: BTreeMap<String, u64>,
kernel: Kernel,
}
impl Usb2Phy {
pub fn new(cru: Arc<dyn CruOp>, param: Usb2PhyParam<'_>, kernel: Kernel) -> Self {
let cfg = find_usb2phy_cfg(param.reg);
let mut rsts = BTreeMap::new();
for &(name, id) in param.rst_list.iter() {
rsts.insert(String::from(name), id);
}
Usb2Phy {
grf: Regmap::new(param.usb_grf),
cfg,
cru,
rsts,
port_kind: param.port_kind,
kernel,
}
}
fn write_reg(&self, offset: u32, value: u32) {
self.grf.reg_write(offset, value);
}
fn read_reg(&self, offset: u32) -> u32 {
self.grf.reg_read(offset)
}
pub async fn setup(&mut self) -> Result<()> {
info!("USB2PHY: Starting initialization");
(self.cfg.phy_tuning)(self)?;
self.init();
self.power_on();
Ok(())
}
fn init(&self) {
info!("USB2PHY: init with port kind {:?}", self.port_kind);
let port_cfg = match self.port_kind {
Usb2PhyPortId::Otg => &self.cfg.port_cfg[Usb2PhyPortId::Otg as usize],
Usb2PhyPortId::Host => &self.cfg.port_cfg[Usb2PhyPortId::Host as usize],
Usb2PhyPortId::Ports => {
unreachable!()
}
};
self.property_enable(&port_cfg.phy_sus, false);
info!("USB2PHY: Waiting for UTMI clock to stabilize",);
self.kernel.delay(core::time::Duration::from_micros(2000));
}
fn property_enable(&self, reg: &Usb2PhyGrfReg, en: bool) {
let tmp = if en { reg.enable } else { reg.disable };
let mask = genmask(reg.bitend, reg.bitstart) as u32;
let val = (tmp << reg.bitstart) | (mask << 16);
self.write_reg(reg.offset, val);
}
fn reset(&self) {
if let Some(&rst_id) = self.rsts.get("phy") {
self.cru.reset_assert(rst_id);
self.kernel.delay(core::time::Duration::from_micros(20));
self.cru.reset_deassert(rst_id);
self.kernel.delay(core::time::Duration::from_micros(100));
}
}
fn power_on(&self) {}
pub fn dump_registers(&self) {
info!("=== USB2 PHY Register Dump ===");
info!("PHY Base: 0x{:08x}", self.grf.base());
let clk_ctrl = self.read_reg(reg_offset::CLK_CONTROL);
info!("CLK_CONTROL (0x0008) = 0x{:08x}", clk_ctrl);
info!(
" IDDQ (bit 29) = {} ({})",
(clk_ctrl >> 29) & 0x1,
if (clk_ctrl >> 29) & 0x1 == 0 {
"正常工作模式 ✅"
} else {
"IDDQ 低功耗模式 ❌"
}
);
info!(
" HS_TX_PREEMP (bits 20:19) = {} ({})",
(clk_ctrl >> 19) & 0x3,
match (clk_ctrl >> 19) & 0x3 {
0b00 => "0x",
0b01 => "1x",
0b10 => "2x (推荐) ✅",
0b11 => "3x",
_ => "未知",
}
);
let hs_dc = self.read_reg(reg_offset::HS_DC_LEVEL);
info!("HS_DC_LEVEL (0x0004) = 0x{:08x}", hs_dc);
info!(
" HS_DC_LEVEL (bits 27:24) = 0x{:x} ({})",
(hs_dc >> 24) & 0xf,
match (hs_dc >> 24) & 0xf {
0x9 => "+5.89% (推荐) ✅",
_ => "其他值",
}
);
let suspend = self.read_reg(reg_offset::SUSPEND_CONTROL);
info!("SUSPEND_CONTROL (0x000c) = 0x{:08x}", suspend);
info!(" PHY_SUSPEND (bit 11) = {}", (suspend >> 11) & 0x1);
info!("=========================");
}
}
fn rk3588_usb2phy_tuning(phy: &Usb2Phy) -> Result<()> {
info!("USB2PHY: Applying RK3588-specific tuning");
phy.write_reg(
reg_offset::CLK_CONTROL,
genmask(29, 29) as u32, );
phy.reset();
phy.write_reg(
reg_offset::HS_DC_LEVEL,
genmask(27, 24) as u32 | 0x0900, );
phy.write_reg(
reg_offset::CLK_CONTROL,
genmask(20, 19) as u32 | 0x0010, );
info!("USB2PHY: HS transmitter pre-emphasis set to 2x",);
phy.dump_registers();
Ok(())
}
fn find_usb2phy_cfg(reg: usize) -> &'static Usb2PhyCfg {
for c in RK3588_PHY_CFGS.iter() {
if c.reg == reg {
return c;
} else {
continue;
}
}
unreachable!("unsupported USB2PHY reg: {:#x}", reg)
}
const RK3588_PHY_CFGS: &[Usb2PhyCfg] = &[
Usb2PhyCfg {
reg: 0x0,
clkout_ctl: Usb2PhyGrfReg::new(0x0000, 0, 0, 1, 0),
port_cfg: [
Usb2PhyPortCfg {
phy_sus: Usb2PhyGrfReg::new(0x000c, 11, 11, 0, 1),
utmi_ls: Usb2PhyGrfReg::new(0x00c0, 10, 9, 0, 1),
utmi_iddig: Usb2PhyGrfReg::new(0x00c0, 5, 5, 0, 1),
},
Usb2PhyPortCfg::default(),
],
phy_tuning: rk3588_usb2phy_tuning,
},
Usb2PhyCfg {
reg: 0x4000,
clkout_ctl: Usb2PhyGrfReg::new(0x0000, 0, 0, 1, 0),
port_cfg: [
Usb2PhyPortCfg {
phy_sus: Usb2PhyGrfReg::new(0x000c, 11, 11, 0, 0),
utmi_ls: Usb2PhyGrfReg::new(0x00c0, 10, 9, 0, 1),
utmi_iddig: Usb2PhyGrfReg::default(),
},
Usb2PhyPortCfg::default(),
],
phy_tuning: rk3588_usb2phy_tuning,
},
];