zynq-rs/libboard_zynq/src/clocks/source.rs

175 lines
4.8 KiB
Rust

use log::debug;
use libregister::{RegisterR, RegisterW, RegisterRW};
use super::slcr;
#[cfg(feature = "target_zc706")]
pub const PS_CLK: u32 = 33_333_333;
#[cfg(feature = "target_cora_z7_10")]
pub const PS_CLK: u32 = 50_000_000;
#[cfg(feature = "target_redpitaya")]
pub const PS_CLK: u32 = 33_333_333;
/// (pll_fdiv_max, (pll_cp, pll_res, lock_cnt))
const PLL_FDIV_LOCK_PARAM: &[(u16, (u8, u8, u16))] = &[
(13, (2, 6, 750)),
(14, (2, 6, 700)),
(15, (2, 6, 650)),
(16, (2, 10, 625)),
(17, (2, 10, 575)),
(18, (2, 10, 550)),
(19, (2, 10, 525)),
(20, (2, 12, 500)),
(21, (2, 12, 475)),
(22, (2, 12, 450)),
(23, (2, 12, 425)),
(25, (2, 12, 400)),
(26, (2, 12, 375)),
(28, (2, 12, 350)),
(30, (2, 12, 325)),
(33, (2, 2, 300)),
(36, (2, 2, 275)),
(40, (2, 2, 250)),
(47, (3, 12, 250)),
(66, (2, 4, 250)),
];
pub trait ClockSource {
/// picks this ClockSource's registers from the SLCR block
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
);
/// query PLL lock status
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool;
/// get configured frequency
fn freq() -> u32 {
let mut slcr = slcr::RegisterBlock::slcr();
let (pll_ctrl, _, _) = Self::pll_regs(&mut slcr);
u32::from(pll_ctrl.read().pll_fdiv()) * PS_CLK
}
fn name() -> &'static str;
/// Zynq-7000 AP SoC Technical Reference Manual:
/// 25.10.4 PLLs
fn setup(target_freq: u32) {
let fdiv = (target_freq / PS_CLK).min(66) as u16;
let (pll_res, pll_cp, lock_cnt) = PLL_FDIV_LOCK_PARAM.iter()
.filter(|(fdiv_max, _)| fdiv <= *fdiv_max)
.nth(0)
.expect("PLL_FDIV_LOCK_PARAM")
.1.clone();
debug!("Set {} to {} Hz", Self::name(), target_freq);
slcr::RegisterBlock::unlocked(|slcr| {
let (pll_ctrl, pll_cfg, pll_status) = Self::pll_regs(slcr);
// Bypass
pll_ctrl.modify(|_, w| w
.pll_pwrdwn(false)
.pll_bypass_force(true)
.pll_fdiv(fdiv)
);
// Configure
pll_cfg.write(
slcr::PllCfg::zeroed()
.pll_res(pll_res)
.pll_cp(pll_cp)
.lock_cnt(lock_cnt)
);
// Reset
pll_ctrl.modify(|_, w| w.pll_reset(true));
pll_ctrl.modify(|_, w| w.pll_reset(false));
// Wait for PLL lock
while ! Self::pll_locked(pll_status) {}
// Remove bypass
pll_ctrl.modify(|_, w| w
.pll_bypass_force(false)
.pll_bypass_qual(false)
);
});
}
}
/// ARM PLL: Recommended clock source for the CPUs and the interconnect
pub struct ArmPll;
impl ClockSource for ArmPll {
#[inline]
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
) {
(&mut slcr.arm_pll_ctrl,
&mut slcr.arm_pll_cfg,
&mut slcr.pll_status
)
}
#[inline]
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool {
pll_status.read().arm_pll_lock()
}
fn name() -> &'static str {
&"ARM_PLL"
}
}
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
pub struct DdrPll;
impl ClockSource for DdrPll {
#[inline]
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
) {
(&mut slcr.ddr_pll_ctrl,
&mut slcr.ddr_pll_cfg,
&mut slcr.pll_status
)
}
#[inline]
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool {
pll_status.read().ddr_pll_lock()
}
fn name() -> &'static str {
&"DDR_PLL"
}
}
/// I/O PLL: Recommended clock for I/O peripherals
pub struct IoPll;
impl ClockSource for IoPll {
#[inline]
fn pll_regs(slcr: &mut crate::slcr::RegisterBlock)
-> (&mut crate::slcr::PllCtrl,
&mut crate::slcr::PllCfg,
&mut crate::slcr::PllStatus
) {
(&mut slcr.io_pll_ctrl,
&mut slcr.io_pll_cfg,
&mut slcr.pll_status
)
}
#[inline]
fn pll_locked(pll_status: &mut crate::slcr::PllStatus) -> bool {
pll_status.read().io_pll_lock()
}
fn name() -> &'static str {
&"IO_PLL"
}
}