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 use libboard_zynq::smoltcp;
|
||||||
|
|
||||||
pub mod slcr;
|
pub mod slcr;
|
||||||
pub mod uart;
|
pub mod clocks;
|
||||||
|
|
Loading…
Reference in New Issue