use crate::peripherals::{CldoCrg, SysCtl0};
use crate::soc::ws63::SYSTEM_CLOCK_HZ;
const HW_CTL: *mut u32 = 0x4000_0014 as *mut u32;
const REG_EXCEP_RO_RG: *mut u32 = 0x4000_319C as *mut u32;
#[allow(dead_code)]
const REG_CMU_FNPLL_SIG: *mut u32 = 0x4000_342C as *mut u32;
const CMU_NEW_CFG1: *mut u32 = 0x4000_34A4 as *mut u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TcxoFreq {
MHz24 = 24_000_000,
MHz40 = 40_000_000,
}
impl TcxoFreq {
pub fn detect() -> Self {
let hw_ctl = unsafe { HW_CTL.read_volatile() };
if hw_ctl & 0x01 == 0 { TcxoFreq::MHz24 } else { TcxoFreq::MHz40 }
}
pub const fn hz(&self) -> u32 {
*self as u32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PllStatus {
Locked,
Unlocked,
}
fn pll_is_locked() -> bool {
(unsafe { REG_EXCEP_RO_RG.read_volatile() } >> 12) & 1 == 1
}
fn wait_pll_lock(retry_count: u32, retry_delay_us: u32) -> PllStatus {
for _ in 0..retry_count {
if pll_is_locked() {
return PllStatus::Locked;
}
let cycles = TcxoFreq::detect().hz() as u64 * retry_delay_us as u64 / 1_000_000;
for _ in 0..cycles / 3 {
core::hint::spin_loop();
}
}
PllStatus::Unlocked
}
#[derive(Debug, Clone, Copy)]
pub struct SystemClocks {
pub cpu_clk: u32,
pub pclk: u32,
pub tcxo_freq: TcxoFreq,
pub pll_locked: bool,
}
impl SystemClocks {
pub const fn assumed() -> Self {
Self { cpu_clk: SYSTEM_CLOCK_HZ, pclk: SYSTEM_CLOCK_HZ, tcxo_freq: TcxoFreq::MHz40, pll_locked: true }
}
}
pub fn init_clocks(_sys_ctl0: &SysCtl0<'_>, _cldo_crg: &CldoCrg<'_>) -> SystemClocks {
let tcxo_freq = TcxoFreq::detect();
let clk_sel_ptr = 0x4400_1134 as *mut u32;
let clk_gate_ptr = 0x4400_1104 as *mut u32;
unsafe { CMU_NEW_CFG1.write_volatile(0x1) }; for _ in 0..tcxo_freq.hz() / 1_000_000 / 3 {
core::hint::spin_loop(); }
unsafe { CMU_NEW_CFG1.write_volatile(0x3) }; unsafe {
let val = clk_sel_ptr.read_volatile();
clk_sel_ptr.write_volatile(val | (1 << 18)); }
unsafe {
let mut gate = clk_gate_ptr.read_volatile();
gate &= !((1 << 18) | (1 << 19) | (1 << 20));
clk_gate_ptr.write_volatile(gate);
let mut sel = clk_sel_ptr.read_volatile();
sel |= (1 << 1) | (1 << 2) | (1 << 3);
clk_sel_ptr.write_volatile(sel);
gate |= (1 << 18) | (1 << 19) | (1 << 20);
clk_gate_ptr.write_volatile(gate);
}
unsafe {
let val = clk_sel_ptr.read_volatile();
clk_sel_ptr.write_volatile(val | (1 << 6)); }
let pll_locked = match wait_pll_lock(30, 1000) {
PllStatus::Locked => true,
PllStatus::Unlocked => false,
};
SystemClocks {
cpu_clk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
pclk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
tcxo_freq,
pll_locked,
}
}
pub fn probe_clocks() -> SystemClocks {
let tcxo_freq = TcxoFreq::detect();
let pll_locked = pll_is_locked();
SystemClocks {
cpu_clk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
pclk: if pll_locked { SYSTEM_CLOCK_HZ } else { tcxo_freq.hz() },
tcxo_freq,
pll_locked,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tcxo_freq_values() {
assert_eq!(TcxoFreq::MHz24.hz(), 24_000_000);
assert_eq!(TcxoFreq::MHz40.hz(), 40_000_000);
}
#[test]
fn test_default_clocks() {
let c = SystemClocks::assumed();
assert_eq!(c.cpu_clk, 240_000_000);
assert_eq!(c.pclk, 240_000_000);
assert!(c.pll_locked);
}
}