#[allow(unused_imports)]
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::usb::error::{UsbError, UsbResult};
use crate::usb;
#[allow(unused_imports)]
use super::regs::{
Dwc2Regs, GAHBCFG, GDFIFOCFG, GHWCFG2, GHWCFG3, GHWCFG4, GINTMSK, GINTSTS, GOTGCTL, GRSTCTL,
GUSBCFG, HCFG,
};
#[inline]
fn base() -> usize {
usb::dwc2_base_virt()
}
#[inline]
fn regs() -> &'static Dwc2Regs {
usb::dwc2_regs().expect("DWC2 base not set (call set_dwc2_base_virt)")
}
fn dbg_dwc2_init_timeout(phase: &'static str) {
if base() == 0 {
return;
}
let r = regs();
let grst = r.grstctl.get();
let gint = r.gintsts.get();
let gahb = r.gahbcfg.get();
let hprt = r.hprt0.get();
let ahb_idle = r.grstctl.is_set(GRSTCTL::AHBIDLE);
let csftrst = r.grstctl.is_set(GRSTCTL::CSFTRST);
let rst_done = r.grstctl.is_set(GRSTCTL::CSFTRST_DONE);
let rx_flush = r.grstctl.is_set(GRSTCTL::RXFFLSH);
let tx_flush = r.grstctl.is_set(GRSTCTL::TXFFLSH);
log::warn!("USB-TOUT dwc2-init [{}] GRSTCTL={:#010x} AHBIDLE={} CSFTRST={} CSFTRST_DONE={} RXFFLSH={} TXFFLSH={}",
phase, grst, ahb_idle, csftrst, rst_done, rx_flush, tx_flush);
log::warn!("USB-TOUT dwc2-init [{}] GINTSTS={:#010x} GAHBCFG={:#010x} HPRT0={:#010x}",
phase, gint, gahb, hprt);
}
const DWC2_CORE_REV_2_91A: u32 = 0x4f54_291a;
const DWC2_CORE_REV_4_20A: u32 = 0x4f54_420a;
const DWC2_CORE_REV_MASK: u32 = 0xffff;
#[inline]
fn spin_delay(iterations: u32) {
for _ in 0..iterations {
core::hint::spin_loop();
}
}
pub unsafe fn dwc2_probe() -> UsbResult<(u32, u32, u32)> {
if base() == 0 {
return Err(UsbError::Hardware("DWC2 base not set (call set_dwc2_base_virt)"));
}
let r = regs();
let h1 = r.ghwcfg1.get();
let h2 = r.ghwcfg2.get();
let h3 = r.ghwcfg3.get();
if h2 == 0 && h3 == 0 {
return Err(UsbError::Hardware("DWC2 GHWCFG2/3 zero (no controller?)"));
}
Ok((h1, h2, h3))
}
fn wait_ahb_idle() -> UsbResult<()> {
let r = regs();
for _ in 0..3_000_000u32 {
if r.grstctl.is_set(GRSTCTL::AHBIDLE) {
return Ok(());
}
spin_delay(32);
}
dbg_dwc2_init_timeout("wait_ahb_idle");
Err(UsbError::Timeout)
}
fn core_soft_reset() -> UsbResult<()> {
wait_ahb_idle()?;
let r = regs();
let snpsid = r.gsnpsid.get();
let core_rev = snpsid & DWC2_CORE_REV_MASK;
let new_rst_seq = core_rev >= (DWC2_CORE_REV_4_20A & DWC2_CORE_REV_MASK);
r.grstctl.modify(GRSTCTL::CSFTRST::SET);
if !new_rst_seq {
for _ in 0..3_000_000u32 {
if !r.grstctl.is_set(GRSTCTL::CSFTRST) {
spin_delay(4096);
return Ok(());
}
spin_delay(32);
}
dbg_dwc2_init_timeout("core_soft_reset CSFTRST (legacy)");
return Err(UsbError::Timeout);
}
for _ in 0..3_000_000u32 {
if r.grstctl.is_set(GRSTCTL::CSFTRST_DONE) {
r.grstctl
.modify(GRSTCTL::CSFTRST::CLEAR + GRSTCTL::CSFTRST_DONE::SET);
spin_delay(4096);
return Ok(());
}
spin_delay(32);
}
dbg_dwc2_init_timeout("core_soft_reset CSFTRST_DONE");
Err(UsbError::Timeout)
}
fn force_host_mode() -> UsbResult<()> {
let r = regs();
r.gusbcfg.modify(GUSBCFG::FORCEHOSTMODE::SET);
spin_delay(100_000);
for _ in 0..500_000u32 {
if r.gintsts.is_set(GINTSTS::CURMODE_HOST) {
return Ok(());
}
spin_delay(32);
}
Err(UsbError::Hardware("CURMODE_HOST not set after FORCEHOSTMODE"))
}
#[cfg(not(feature = "cv182x-host"))]
fn init_fifos() {
const RX_DEPTH: u32 = 0x210;
const NPTX_DEPTH: u32 = 0x200;
const PTX_DEPTH: u32 = 0x200;
let nptx_start = RX_DEPTH;
let ptx_start = nptx_start + NPTX_DEPTH;
let r = regs();
r.grxfsiz.set(RX_DEPTH);
r.gnptxfsiz.set((NPTX_DEPTH << 16) | nptx_start);
r.hptxfsiz.set((PTX_DEPTH << 16) | ptx_start);
}
#[cfg(not(feature = "cv182x-host"))]
fn init_gahb() {
let r = regs();
let arch = r.ghwcfg2.read(GHWCFG2::ARCH);
r.gahbcfg.modify(
GAHBCFG::DMA_EN::CLEAR
+ GAHBCFG::GLBL_INTR_EN::SET
+ GAHBCFG::HBSTLEN.val(3),
);
if arch == 2 {
r.gahbcfg.modify(GAHBCFG::DMA_EN::SET);
}
}
#[cfg(not(feature = "cv182x-host"))]
fn init_hcfg_fs_ls() {
regs().hcfg.modify(
HCFG::FSLSSUPP::SET + HCFG::FSLSPCLKSEL::Pll48Mhz,
);
}
pub unsafe fn dwc2_hprt0_read() -> u32 {
if base() == 0 {
return 0;
}
regs().hprt0.get()
}
#[inline]
pub fn hprt_connsts(hprt: u32) -> bool {
hprt & (1 << 0) != 0
}
#[inline]
pub fn hprt_pwr(hprt: u32) -> bool {
hprt & (1 << 12) != 0
}
#[inline]
#[allow(dead_code)]
pub fn hprt_enabled(hprt: u32) -> bool {
hprt & (1 << 2) != 0
}
#[inline]
pub fn hprt_speed_bits(hprt: u32) -> u32 {
(hprt >> 17) & 3
}
#[inline]
pub fn suggested_bulk_mps(hprt: u32) -> u32 {
if hprt_speed_bits(hprt) == 0 {
512
} else {
64
}
}
const HPRT0_W1C_MASK: u32 = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 5);
#[inline]
fn hprt0_rmw_safe() -> u32 {
regs().hprt0.get() & !HPRT0_W1C_MASK
}
fn port_power_on() {
let r = regs();
let mut w = hprt0_rmw_safe();
w |= 1 << 12; r.hprt0.set(w);
}
fn port_reset_pulse() {
let r = regs();
let cur = r.hprt0.get();
if cur & (1 << 1) != 0 {
r.hprt0.set((cur & !HPRT0_W1C_MASK) | (1 << 1));
}
let base = hprt0_rmw_safe() | (1 << 12); r.hprt0.set(base | (1 << 8)); spin_delay(15_000_000); let base2 = hprt0_rmw_safe() | (1 << 12);
r.hprt0.set(base2 & !(1 << 8)); spin_delay(20_000_000);
}
#[inline]
pub fn hprt_lnsts(hprt: u32) -> u32 {
(hprt >> 10) & 3
}
pub fn dwc2_host_root_bus_reset_pulse() -> UsbResult<()> {
if base() == 0 {
return Err(UsbError::Hardware("DWC2 base not set (call set_dwc2_base_virt)"));
}
let r = regs();
let cur = r.hprt0.get();
if cur & (1 << 1) != 0 {
r.hprt0.set((cur & !HPRT0_W1C_MASK) | (1 << 1));
}
port_reset_pulse();
Ok(())
}
pub fn debug_dump_root_port_hw(tag: &str) {
if base() == 0 {
return;
}
let r = regs();
let hprt = r.hprt0.get();
let ln = hprt_lnsts(hprt);
log::debug!("USB-DBG {} HPRT0={:#010x} LNSTS={} CONNSTS={} CONNDET={} RST={} PWR={} SPD={}",
tag,
hprt,
ln,
hprt & 1,
(hprt >> 1) & 1,
(hprt >> 8) & 1,
(hprt >> 12) & 1,
hprt_speed_bits(hprt),);
log::debug!("USB-DBG {} GOTGCTL={:#010x} GUSBCFG={:#010x} GAHBCFG={:#010x}",
tag,
r.gotgctl.get(),
r.gusbcfg.get(),
r.gahbcfg.get());
log::debug!("USB-DBG {} GINTSTS={:#010x} PCGCTL={:#010x} HCFG={:#010x}",
tag,
r.gintsts.get(),
r.pcgctl.get(),
r.hcfg.get());
}
#[cfg(feature = "cv182x-host")]
fn wait_grstctl_handshake(field: tock_registers::fields::Field<u32, GRSTCTL::Register>, set: bool) -> UsbResult<()> {
let r = regs();
for _ in 0..3_000_000u32 {
let on = r.grstctl.is_set(field);
if on == set {
spin_delay(64);
return Ok(());
}
spin_delay(8);
}
let label = "wait_grstctl handshake";
dbg_dwc2_init_timeout(label);
Err(UsbError::Timeout)
}
#[cfg(feature = "cv182x-host")]
fn flush_rx_fifo_host() -> UsbResult<()> {
wait_ahb_idle()?;
regs().grstctl.write(GRSTCTL::RXFFLSH::SET);
wait_grstctl_handshake(GRSTCTL::RXFFLSH, false)?;
spin_delay(2_000);
Ok(())
}
#[cfg(feature = "cv182x-host")]
fn flush_tx_fifo_host_all() -> UsbResult<()> {
wait_ahb_idle()?;
regs()
.grstctl
.write(GRSTCTL::TXFFLSH::SET + GRSTCTL::TXFNUM.val(0x10));
wait_grstctl_handshake(GRSTCTL::TXFFLSH, false)?;
spin_delay(2_000);
Ok(())
}
#[cfg(feature = "cv182x-host")]
#[inline]
fn total_dfifo_depth_words() -> u32 {
regs().ghwcfg3.read(GHWCFG3::DFIFO_DEPTH)
}
#[cfg(feature = "cv182x-host")]
#[inline]
fn host_channel_count() -> u32 {
1 + regs().ghwcfg2.read(GHWCFG2::NUM_HOST_CHAN)
}
#[cfg(feature = "cv182x-host")]
fn init_host_fifos_cv182x() -> UsbResult<()> {
let total = total_dfifo_depth_words();
let hc = host_channel_count();
let mut rx: u32 = 536;
let mut nptx: u32 = 32;
let mut ptx: u32 = 768;
if rx.saturating_add(nptx).saturating_add(ptx) > total {
rx = 516 + hc;
nptx = 256;
ptx = 768;
}
let sum = rx.saturating_add(nptx).saturating_add(ptx);
if sum > total {
ptx = total.saturating_sub(rx).saturating_sub(nptx);
}
let r = regs();
r.grxfsiz.set(rx & 0xffff);
r.gnptxfsiz
.set(((nptx << 16) & 0xffff_0000) | (rx & 0xffff));
r.hptxfsiz
.set(((ptx << 16) & 0xffff_0000) | ((rx + nptx) & 0xffff));
let snpsid = r.gsnpsid.get();
let ded = r.ghwcfg4.is_set(GHWCFG4::DED_FIFO_EN);
if ded && snpsid >= DWC2_CORE_REV_2_91A {
let epbase = rx.wrapping_add(nptx).wrapping_add(ptx);
r.gdfifocfg.modify(GDFIFOCFG::EPINFOBASE.val(epbase));
}
Ok(())
}
#[cfg(feature = "cv182x-host")]
fn init_gotgctl_otg_host_session_overrides() {
regs().gotgctl.modify(
GOTGCTL::DBNCE_FLTR_BYPASS::SET
+ GOTGCTL::AVALOEN::SET
+ GOTGCTL::AVALOVAL::SET
+ GOTGCTL::VBVALOEN::SET
+ GOTGCTL::VBVALOVAL::SET,
);
spin_delay(200_000);
}
#[cfg(feature = "cv182x-host")]
fn init_gusbcfg_cv182x_utmi16_hs() {
let r = regs();
let utmi_w = r.ghwcfg4.read(GHWCFG4::UTMI_PHY_DATA_WIDTH);
let want_16bit = utmi_w == 1; log::debug!("USB-DBG GHWCFG4.UTMI_PHY_DATA_WIDTH={utmi_w} (0=8 only, 1=16 only, 2=programmable) => PHYIF16={}",
if want_16bit { 1 } else { 0 });
let mut field = GUSBCFG::FORCEHOSTMODE::SET
+ GUSBCFG::ULPI_UTMI_SEL::CLEAR
+ GUSBCFG::TOUTCAL.val(0x7);
if want_16bit {
field += GUSBCFG::PHYIF16::SET;
} else {
field += GUSBCFG::PHYIF16::CLEAR;
}
r.gusbcfg.modify(field);
}
#[cfg(feature = "cv182x-host")]
fn init_gahb_dma_cv182x() {
let r = regs();
let arch = r.ghwcfg2.read(GHWCFG2::ARCH);
r.gahbcfg.modify(
GAHBCFG::HBSTLEN::Incr16 + GAHBCFG::GLBL_INTR_EN::SET,
);
if arch == 2 {
r.gahbcfg.modify(GAHBCFG::DMA_EN::SET);
}
#[cfg(feature = "usb-force-no-dma")]
{
r.gahbcfg.modify(GAHBCFG::DMA_EN::CLEAR);
log::warn!("USB-DBG usb-force-no-dma: DMA_EN cleared (ARCH={arch}, expect fail on int-DMA IP)");
}
}
#[cfg(feature = "cv182x-host")]
fn cv182x_usb2_phy_host_clear_utmi_override() {
let phy = usb::cv182x_phy_regs()
.expect("CV182x USB2 PHY base not set (call set_cv182x_phy_base_virt)");
let old = phy.reg014.get();
phy.reg014.set(0);
spin_delay(200_000);
let now = phy.reg014.get();
log::debug!("USB-DBG REG014 {:#06x}->{:#06x} (UTMI_OVERRIDE cleared, DWC2 drives pulldowns)",
old, now);
}
#[cfg(feature = "cv182x-host")]
fn hcfg_clear_fs_ls_for_high_speed() {
regs().hcfg.modify(
HCFG::FSLSSUPP::CLEAR + HCFG::FSLSPCLKSEL.val(0),
);
}
pub fn dwc2_host_init() -> UsbResult<()> {
if base() == 0 {
return Err(UsbError::Hardware("DWC2 base not set (call set_dwc2_base_virt)"));
}
let r = regs();
r.gintmsk.set(0);
r.gintsts.set(0xFFFF_FFFF);
core_soft_reset()?;
force_host_mode()?;
core_soft_reset()?;
#[cfg(feature = "cv182x-host")]
{
init_gotgctl_otg_host_session_overrides();
init_gusbcfg_cv182x_utmi16_hs();
r.pcgctl.set(0);
init_gahb_dma_cv182x();
hcfg_clear_fs_ls_for_high_speed();
init_host_fifos_cv182x()?;
flush_tx_fifo_host_all()?;
flush_rx_fifo_host()?;
}
#[cfg(not(feature = "cv182x-host"))]
{
init_fifos();
init_gahb();
init_hcfg_fs_ls();
}
r.haintmsk.set((1 << 0) | (1 << 1));
r.gintmsk.modify(GINTMSK::HCHINT::SET);
r.gintsts.set(0xFFFF_FFFF);
port_power_on();
#[cfg(feature = "cv182x-host")]
{
cv182x_usb2_phy_host_clear_utmi_override();
init_gotgctl_otg_host_session_overrides();
}
Ok(())
}