use crate::regs::{RegisterR, RegisterRW}; use super::slcr; #[cfg(feature = "target_zc706")] const PS_CLK: u32 = 33_333_333; #[cfg(feature = "target_cora_z7_10")] const PS_CLK: u32 = 50_000_000; enum CpuClockMode { /// Clocks run in 4:2:2:1 mode C421, /// Clocks run in 6:3:2:1 mode C621, } impl CpuClockMode { pub fn get() -> Self { let regs = slcr::RegisterBlock::new(); if regs.clk_621_true.read().clk_621_true() { CpuClockMode::C621 } else { CpuClockMode::C421 } } } #[derive(Debug, Clone)] pub struct CpuClocks { /// ARM PLL: Recommended clock source for the CPUs and the interconnect pub arm: u32, /// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces pub ddr: u32, /// I/O PLL: Recommended clock for I/O peripherals pub io: u32, } impl CpuClocks { pub fn get() -> Self { let regs = slcr::RegisterBlock::new(); let arm = u32::from(regs.arm_pll_ctrl.read().pll_fdiv()) * PS_CLK; let ddr = u32::from(regs.ddr_pll_ctrl.read().pll_fdiv()) * PS_CLK; let io = u32::from(regs.io_pll_ctrl.read().pll_fdiv()) * PS_CLK; CpuClocks { arm, ddr, io } } pub fn cpu_6x4x(&self) -> u32 { let regs = slcr::RegisterBlock::new(); let arm_clk_ctrl = regs.arm_clk_ctrl.read(); let pll = match arm_clk_ctrl.srcsel() { slcr::ArmPllSource::ArmPll => self.arm, slcr::ArmPllSource::DdrPll => self.ddr, slcr::ArmPllSource::IoPll => self.io, }; pll / u32::from(arm_clk_ctrl.divisor()) } pub fn cpu_3x2x(&self) -> u32 { self.cpu_6x4x() / 2 } pub fn cpu_2x(&self) -> u32 { match CpuClockMode::get() { CpuClockMode::C421 => self.cpu_6x4x() / 2, CpuClockMode::C621 => self.cpu_6x4x() / 3, } } pub fn cpu_1x(&self) -> u32 { match CpuClockMode::get() { CpuClockMode::C421 => self.cpu_6x4x() / 4, CpuClockMode::C621 => self.cpu_6x4x() / 6, } } pub fn uart_ref_clk(&self) -> u32 { let regs = slcr::RegisterBlock::new(); let uart_clk_ctrl = regs.uart_clk_ctrl.read(); let pll = match uart_clk_ctrl.srcsel() { slcr::PllSource::ArmPll => self.arm, slcr::PllSource::DdrPll => self.ddr, slcr::PllSource::IoPll => self.io, }; pll / u32::from(uart_clk_ctrl.divisor()) } pub fn enable_ddr(target_clock: u32) { let regs = slcr::RegisterBlock::new(); regs.ddr_pll_ctrl.modify(|_, w| w .pll_pwrdwn(false) .pll_reset(true) .pll_bypass_force(true) ); let fdiv = (target_clock / PS_CLK).max(127) as u16; regs.ddr_pll_ctrl.modify(|_, w| w .pll_pwrdwn(false) .pll_reset(false) .pll_fdiv(fdiv) ); while ! regs.pll_status.read().ddr_pll_lock() {} regs.ddr_pll_ctrl.modify(|_, w| w .pll_bypass_force(false) .pll_bypass_qual(false) ); } }