forked from M-Labs/zynq-rs
zynq_us: clocks (WIP)
This commit is contained in:
parent
0f2376410f
commit
188954ddc7
|
@ -0,0 +1,89 @@
|
|||
use libregister::{RegisterR, RegisterRW};
|
||||
use super::slcr::{crf_apb, crl_apb, common::SlcrRegisterBlock};
|
||||
pub use super::slcr::crf_apb::ApuClkSource;
|
||||
|
||||
pub mod source;
|
||||
use source::*;
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Clocks {
|
||||
/// ARM PLL: Recommended clock source for the APUs and the FPD interconnect
|
||||
pub apu: u32,
|
||||
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
|
||||
pub ddr: u32,
|
||||
/// Video PLL: Recommended clock for display port
|
||||
pub video: u32,
|
||||
/// I/O PLL: Recommended clock for I/O peripherals
|
||||
pub io: u32,
|
||||
/// RPU PLL: Recommended clock for RPUs and LPD interconnect
|
||||
pub rpu: u32,
|
||||
}
|
||||
|
||||
impl Clocks {
|
||||
pub fn get() -> Self {
|
||||
let fpd_regs = crf_apb::RegisterBlock::slcr();
|
||||
let lpd_regs = crl_apb::RegisterBlock::slcr();
|
||||
Clocks {
|
||||
apu: ApuPll::freq(&mut fpd_regs.apu_pll_ctrl),
|
||||
ddr: DdrPll::freq(&mut fpd_regs.ddr_pll_ctrl),
|
||||
video: VideoPll::freq(&mut fpd_regs.video_pll_ctrl),
|
||||
io: IoPll::freq(&mut lpd_regs.io_pll_ctrl),
|
||||
rpu: RpuPll::freq(&mut lpd_regs.rpu_pll_ctrl),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cpu_freq(target_freq: u32) {
|
||||
let fpd_regs = crf_apb::RegisterBlock::slcr();
|
||||
let apu_pll = ApuPll::freq(&mut fpd_regs.apu_pll_ctrl);
|
||||
let mut div = 1u8;
|
||||
while div < 63 && apu_pll / u32::from(div) > target_freq {
|
||||
div += 1;
|
||||
}
|
||||
|
||||
crf_apb::RegisterBlock::unlocked(|slcr| {
|
||||
slcr.apu_clk_ctrl.modify(|_, w| w
|
||||
.srcsel(ApuClkSource::ApuPll)
|
||||
.divisor0(div)
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn uart0_ref_clk(&self) -> u32 {
|
||||
let lpd_regs = crl_apb::RegisterBlock::slcr();
|
||||
self.uart_ref_clk(&mut lpd_regs.uart0_clk_ctrl)
|
||||
}
|
||||
|
||||
pub fn uart1_ref_clk(&self) -> u32 {
|
||||
let lpd_regs = crl_apb::RegisterBlock::slcr();
|
||||
self.uart_ref_clk(&mut lpd_regs.uart1_clk_ctrl)
|
||||
}
|
||||
|
||||
fn uart_ref_clk(&self, uart_regs: &mut crl_apb::UartClkCtrl) -> u32 {
|
||||
let uart_clk_ctrl = uart_regs.read();
|
||||
let pll = match uart_clk_ctrl.srcsel() {
|
||||
crl_apb::IoClkSource::IoPll => self.io,
|
||||
crl_apb::IoClkSource::RpuPll => self.rpu,
|
||||
crl_apb::IoClkSource::DdrPllToLpd => {
|
||||
let fpd_regs = crf_apb::RegisterBlock::slcr();
|
||||
let divisor = u32::from(fpd_regs.ddr_pll_to_lpd_ctrl.read().divisor0());
|
||||
self.ddr / divisor
|
||||
}
|
||||
};
|
||||
pll / (u32::from(uart_clk_ctrl.divisor0()) * u32::from(uart_clk_ctrl.divisor1()))
|
||||
}
|
||||
|
||||
// pub fn sdio_ref_clk(&self) -> u32 {
|
||||
// let regs = slcr::RegisterBlock::slcr();
|
||||
// let sdio_clk_ctrl = regs.sdio_clk_ctrl.read();
|
||||
// let pll = match sdio_clk_ctrl.srcsel() {
|
||||
// slcr::PllSource::ArmPll =>
|
||||
// self.arm,
|
||||
// slcr::PllSource::DdrPll =>
|
||||
// self.ddr,
|
||||
// slcr::PllSource::IoPll =>
|
||||
// self.io,
|
||||
// };
|
||||
// pll / u32::from(sdio_clk_ctrl.divisor())
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
use crate::slcr::common::{PllCfg, PllCtrl, PllFracCfg, SlcrRegisterBlock};
|
||||
use crate::slcr::{crf_apb, crl_apb};
|
||||
use libregister::{RegisterR, RegisterRW};
|
||||
use log::debug;
|
||||
|
||||
pub const PS_CLK: u32 = 33_333_000;
|
||||
|
||||
// DS926 Table: PS PLL Switching Characteristics (same for both speed grades)
|
||||
// const PS_PLL_MAX_LOCK_TIME: f32 = 100e-6; // 100 us
|
||||
const PS_PLL_MAX_OUT_FREQ: u32 = 1_600_000_000;
|
||||
const PS_PLL_MIN_OUT_FREQ: u32 = 750_000_000;
|
||||
// const PS_PLL_MAX_VCO_FREQ: u32 = 3_000_000_000;
|
||||
const PS_PLL_MIN_VCO_FREQ: u32 = 1_500_000_000;
|
||||
|
||||
/// UG1085 table 37-1
|
||||
/// (pll_fdiv_max, (pll_cp, pll_res, lfhf, lock_dly, lock_cnt))
|
||||
const PLL_FDIV_LOCK_PARAM: &[(u8, (u8, u8, u8, u8, u16))] = &[
|
||||
(25, (3, 10, 3, 63, 1000)),
|
||||
(26, (3, 10, 3, 63, 1000)),
|
||||
(27, (4, 6, 3, 63, 1000)),
|
||||
(28, (4, 6, 3, 63, 1000)),
|
||||
(29, (4, 6, 3, 63, 1000)),
|
||||
(30, (4, 6, 3, 63, 1000)),
|
||||
(31, (6, 1, 3, 63, 1000)),
|
||||
(32, (6, 1, 3, 63, 1000)),
|
||||
(33, (4, 10, 3, 63, 1000)),
|
||||
(34, (5, 6, 3, 63, 1000)),
|
||||
(35, (5, 6, 3, 63, 1000)),
|
||||
(36, (5, 6, 3, 63, 1000)),
|
||||
(37, (5, 6, 3, 63, 1000)),
|
||||
(38, (5, 6, 3, 63, 975)),
|
||||
(39, (3, 12, 3, 63, 950)),
|
||||
(40, (3, 12, 3, 63, 925)),
|
||||
(41, (3, 12, 3, 63, 900)),
|
||||
(42, (3, 12, 3, 63, 875)),
|
||||
(43, (3, 12, 3, 63, 850)),
|
||||
(44, (3, 12, 3, 63, 850)),
|
||||
(45, (3, 12, 3, 63, 825)),
|
||||
(46, (3, 12, 3, 63, 800)),
|
||||
(47, (3, 12, 3, 63, 775)),
|
||||
(48, (3, 12, 3, 63, 775)),
|
||||
(49, (3, 12, 3, 63, 750)),
|
||||
(50, (3, 12, 3, 63, 750)),
|
||||
(51, (3, 2, 3, 63, 725)),
|
||||
(52, (3, 2, 3, 63, 700)),
|
||||
(53, (3, 2, 3, 63, 700)),
|
||||
(54, (3, 2, 3, 63, 675)),
|
||||
(55, (3, 2, 3, 63, 675)),
|
||||
(56, (3, 2, 3, 63, 650)),
|
||||
(57, (3, 2, 3, 63, 650)),
|
||||
(58, (3, 2, 3, 63, 625)),
|
||||
(59, (3, 2, 3, 63, 625)),
|
||||
(60, (3, 2, 3, 63, 625)),
|
||||
(82, (3, 2, 3, 63, 600)), // 61-82
|
||||
(102, (4, 2, 3, 63, 600)), // 83-102
|
||||
(103, (5, 2, 3, 63, 600)),
|
||||
(104, (5, 2, 3, 63, 600)),
|
||||
(105, (5, 2, 3, 63, 600)),
|
||||
(106, (5, 2, 3, 63, 600)),
|
||||
(125, (3, 4, 3, 63, 600)), // 107-125
|
||||
];
|
||||
|
||||
pub trait ClockSource<T: SlcrRegisterBlock> {
|
||||
/// picks this ClockSource's registers from the SLCR block
|
||||
fn pll_ctrl_regs(slcr: &mut T) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg);
|
||||
|
||||
/// query PLL lock status
|
||||
fn pll_locked() -> bool;
|
||||
|
||||
// todo: is there any situation in which we'll actually want to use fractional mode?
|
||||
/// query fraction mode enable bit
|
||||
fn frac_enabled(pll_frac_cfg: &mut PllFracCfg) -> bool {
|
||||
bool::from(pll_frac_cfg.read().enabled())
|
||||
}
|
||||
|
||||
/// get configured frequency
|
||||
fn freq(pll_ctrl: &mut PllCtrl) -> u32 {
|
||||
// todo: take into account fractional part (if enabled)
|
||||
u32::from(pll_ctrl.read().pll_fdiv()) * PS_CLK
|
||||
}
|
||||
|
||||
fn name() -> &'static str;
|
||||
|
||||
// UG1085 Chapter 37: PS Clock Subsystem
|
||||
fn setup(target_freq: u32) {
|
||||
assert!(target_freq >= PS_PLL_MIN_OUT_FREQ && target_freq <= PS_PLL_MAX_OUT_FREQ);
|
||||
let div2 = target_freq < PS_PLL_MIN_VCO_FREQ;
|
||||
let divisor = u32::from(div2) + 1;
|
||||
let fdiv = (target_freq * divisor / PS_CLK).min(125) as u8;
|
||||
let (pll_cp, pll_res, lfhf, lock_dly, 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);
|
||||
T::unlocked(|slcr| {
|
||||
let (pll_ctrl, pll_cfg, _) = Self::pll_ctrl_regs(slcr);
|
||||
|
||||
// Write fdiv, div2
|
||||
pll_ctrl.modify(|_, w| w.pll_fdiv(fdiv).pll_div2(div2));
|
||||
// Configure
|
||||
// no need to zero as we're writing every field
|
||||
pll_cfg.modify(|_, w| {
|
||||
w.lock_dly(lock_dly)
|
||||
.lock_cnt(lock_cnt)
|
||||
.lfhf(lfhf)
|
||||
.pll_cp(pll_cp)
|
||||
.pll_res(pll_res)
|
||||
});
|
||||
// Bypass
|
||||
pll_ctrl.modify(|_, w| w.pll_bypass_force(true));
|
||||
// Reset
|
||||
pll_ctrl.modify(|_, w| w.pll_reset(true));
|
||||
pll_ctrl.modify(|_, w| w.pll_reset(false));
|
||||
// Wait for PLL lock
|
||||
// todo: add timeout here according to the 100 us spec?
|
||||
while !Self::pll_locked() {}
|
||||
// Remove bypass
|
||||
pll_ctrl.modify(|_, w| w.pll_bypass_force(false));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// APU PLL: Recommended clock source for the APUs and the FPD interconnect
|
||||
pub struct ApuPll;
|
||||
|
||||
impl ClockSource<crf_apb::RegisterBlock> for ApuPll {
|
||||
#[inline]
|
||||
fn pll_ctrl_regs(
|
||||
slcr: &mut crf_apb::RegisterBlock,
|
||||
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
|
||||
(
|
||||
&mut slcr.apu_pll_ctrl,
|
||||
&mut slcr.apu_pll_cfg,
|
||||
&mut slcr.apu_pll_frac_cfg,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pll_locked() -> bool {
|
||||
let slcr = crf_apb::RegisterBlock::slcr();
|
||||
slcr.pll_status.read().apu_pll_lock()
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
&"APU_PLL"
|
||||
}
|
||||
}
|
||||
|
||||
/// DDR PLL: Recommended clock for the DDR DRAM controller and AXI_HP interfaces
|
||||
pub struct DdrPll;
|
||||
|
||||
impl ClockSource<crf_apb::RegisterBlock> for DdrPll {
|
||||
#[inline]
|
||||
fn pll_ctrl_regs(
|
||||
slcr: &mut crf_apb::RegisterBlock,
|
||||
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
|
||||
(
|
||||
&mut slcr.ddr_pll_ctrl,
|
||||
&mut slcr.ddr_pll_cfg,
|
||||
&mut slcr.ddr_pll_frac_cfg,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pll_locked() -> bool {
|
||||
let slcr = crf_apb::RegisterBlock::slcr();
|
||||
slcr.pll_status.read().ddr_pll_lock()
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
&"DDR_PLL"
|
||||
}
|
||||
}
|
||||
|
||||
/// Video PLL: Recommended clock for DisplayPort
|
||||
pub struct VideoPll;
|
||||
|
||||
impl ClockSource<crf_apb::RegisterBlock> for VideoPll {
|
||||
#[inline]
|
||||
fn pll_ctrl_regs(
|
||||
slcr: &mut crf_apb::RegisterBlock,
|
||||
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
|
||||
(
|
||||
&mut slcr.video_pll_ctrl,
|
||||
&mut slcr.video_pll_cfg,
|
||||
&mut slcr.video_pll_frac_cfg,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pll_locked() -> bool {
|
||||
let slcr = crf_apb::RegisterBlock::slcr();
|
||||
slcr.pll_status.read().video_pll_lock()
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
&"VIDEO_PLL"
|
||||
}
|
||||
}
|
||||
|
||||
/// I/O PLL: Recommended clock for I/O peripherals
|
||||
pub struct IoPll;
|
||||
|
||||
impl ClockSource<crl_apb::RegisterBlock> for IoPll {
|
||||
#[inline]
|
||||
fn pll_ctrl_regs(
|
||||
slcr: &mut crl_apb::RegisterBlock,
|
||||
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
|
||||
(
|
||||
&mut slcr.io_pll_ctrl,
|
||||
&mut slcr.io_pll_cfg,
|
||||
&mut slcr.io_pll_frac_cfg,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pll_locked() -> bool {
|
||||
let slcr = crl_apb::RegisterBlock::slcr();
|
||||
slcr.pll_status.read().io_pll_lock()
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
&"IO_PLL"
|
||||
}
|
||||
}
|
||||
|
||||
/// RPU PLL: Recommended clock for RPUs and LPD interconnect
|
||||
pub struct RpuPll;
|
||||
|
||||
impl ClockSource<crl_apb::RegisterBlock> for RpuPll {
|
||||
#[inline]
|
||||
fn pll_ctrl_regs(
|
||||
slcr: &mut crl_apb::RegisterBlock,
|
||||
) -> (&mut PllCtrl, &mut PllCfg, &mut PllFracCfg) {
|
||||
(
|
||||
&mut slcr.io_pll_ctrl,
|
||||
&mut slcr.io_pll_cfg,
|
||||
&mut slcr.io_pll_frac_cfg,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pll_locked() -> bool {
|
||||
let slcr = crl_apb::RegisterBlock::slcr();
|
||||
slcr.pll_status.read().rpu_pll_lock()
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
&"RPU_PLL"
|
||||
}
|
||||
}
|
|
@ -6,4 +6,4 @@ extern crate alloc;
|
|||
pub use libboard_zynq::smoltcp;
|
||||
|
||||
pub mod slcr;
|
||||
pub mod uart;
|
||||
pub mod clocks;
|
||||
|
|
Loading…
Reference in New Issue