2019-12-18 06:35:58 +08:00
|
|
|
use libregister::{RegisterR, RegisterW, RegisterRW};
|
2019-10-22 04:19:03 +08:00
|
|
|
use super::slcr;
|
2019-08-17 08:55:56 +08:00
|
|
|
|
|
|
|
#[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())
|
|
|
|
}
|
2019-10-26 02:38:10 +08:00
|
|
|
|
2019-11-07 06:21:22 +08:00
|
|
|
/// Zynq-7000 AP SoC Technical Reference Manual:
|
|
|
|
/// 25.10.4 PLLs
|
|
|
|
pub fn enable_io(target_clock: u32) {
|
|
|
|
let fdiv = (target_clock / PS_CLK).min(66) as u16;
|
|
|
|
slcr::RegisterBlock::unlocked(|slcr| {
|
|
|
|
slcr.io_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_pwrdwn(false)
|
|
|
|
.pll_bypass_force(true)
|
|
|
|
.pll_fdiv(fdiv)
|
|
|
|
);
|
|
|
|
slcr.io_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_reset(true)
|
|
|
|
);
|
|
|
|
slcr.io_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_reset(false)
|
|
|
|
);
|
|
|
|
while ! slcr.pll_status.read().io_pll_lock() {}
|
|
|
|
slcr.io_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_bypass_force(false)
|
|
|
|
.pll_bypass_qual(false)
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-10-26 05:19:34 +08:00
|
|
|
/// Zynq-7000 AP SoC Technical Reference Manual:
|
|
|
|
/// 25.10.4 PLLs
|
2019-10-26 02:38:10 +08:00
|
|
|
pub fn enable_ddr(target_clock: u32) {
|
2019-10-26 05:19:34 +08:00
|
|
|
let fdiv = (target_clock / PS_CLK).min(66) as u16;
|
|
|
|
let (pll_res, pll_cp, lock_cnt) = PLL_FDIV_LOCK_PARAM.iter()
|
|
|
|
.filter(|(fdiv_max, _)| fdiv <= *fdiv_max)
|
2019-10-28 03:30:38 +08:00
|
|
|
.nth(0)
|
2019-10-26 05:19:34 +08:00
|
|
|
.expect("PLL_FDIV_LOCK_PARAM")
|
|
|
|
.1.clone();
|
2019-11-07 05:59:17 +08:00
|
|
|
slcr::RegisterBlock::unlocked(|regs| {
|
|
|
|
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_pwrdwn(false)
|
|
|
|
.pll_bypass_force(true)
|
|
|
|
.pll_fdiv(fdiv)
|
|
|
|
);
|
|
|
|
regs.ddr_pll_cfg.write(
|
|
|
|
slcr::PllCfg::zeroed()
|
|
|
|
.pll_res(pll_res)
|
|
|
|
.pll_cp(pll_cp)
|
|
|
|
.lock_cnt(lock_cnt)
|
|
|
|
);
|
|
|
|
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_reset(true)
|
|
|
|
);
|
|
|
|
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_reset(false)
|
|
|
|
);
|
|
|
|
while ! regs.pll_status.read().ddr_pll_lock() {}
|
|
|
|
regs.ddr_pll_ctrl.modify(|_, w| w
|
|
|
|
.pll_bypass_force(false)
|
|
|
|
.pll_bypass_qual(false)
|
|
|
|
);
|
|
|
|
});
|
2019-10-26 02:38:10 +08:00
|
|
|
}
|
2019-08-17 08:55:56 +08:00
|
|
|
}
|
2019-10-26 05:19:34 +08:00
|
|
|
|
|
|
|
/// (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)),
|
|
|
|
];
|