1use core::sync::atomic::AtomicBool;
2
3use arbitrary_int::{u4, u7, u10};
4
5use crate::{BootMode, time::Hertz};
6
7pub const PLL_MUL_MIN: u32 = 13;
9pub const PLL_MUL_MAX: u32 = 66;
11
12static ARM_PLL_INIT: AtomicBool = AtomicBool::new(false);
13static IO_PLL_INIT: AtomicBool = AtomicBool::new(false);
14static DDR_PLL_INIT: AtomicBool = AtomicBool::new(false);
15
16#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
17#[error("pll muliplier value {0} is out of range ({PLL_MUL_MIN}..={PLL_MUL_MAX})")]
18pub struct MulOutOfRangeError(pub u32);
19
20#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
21pub enum PllConfigCtorError {
22 #[error("invalid input")]
23 InvalidInput,
24 #[error("pll multiplier out of range: {0}")]
25 MulOutOfRange(#[from] MulOutOfRangeError),
26}
27
28pub struct PllConfig {
29 fdiv: u7,
30 charge_pump: u4,
31 loop_resistor: u4,
32 lock_count: u10,
33}
34
35impl PllConfig {
36 pub fn new_from_target_clock(
37 ps_clk: Hertz,
38 target_clk: Hertz,
39 ) -> Result<Self, PllConfigCtorError> {
40 if ps_clk.raw() == 0 {
41 return Err(PllConfigCtorError::InvalidInput);
42 }
43 let mul = target_clk / ps_clk;
44 Self::new(mul).map_err(PllConfigCtorError::MulOutOfRange)
45 }
46 pub fn new(pll_mul: u32) -> Result<Self, MulOutOfRangeError> {
50 if !(PLL_MUL_MIN..=PLL_MUL_MAX).contains(&pll_mul) {
51 return Err(MulOutOfRangeError(pll_mul));
52 }
53
54 Ok(match pll_mul {
55 13 => Self::new_raw(
56 u7::new(pll_mul as u8),
57 u4::new(2),
58 u4::new(6),
59 u10::new(750),
60 ),
61 14 => Self::new_raw(
62 u7::new(pll_mul as u8),
63 u4::new(2),
64 u4::new(6),
65 u10::new(700),
66 ),
67 15 => Self::new_raw(
68 u7::new(pll_mul as u8),
69 u4::new(2),
70 u4::new(6),
71 u10::new(650),
72 ),
73 16 => Self::new_raw(
74 u7::new(pll_mul as u8),
75 u4::new(2),
76 u4::new(10),
77 u10::new(625),
78 ),
79 17 => Self::new_raw(
80 u7::new(pll_mul as u8),
81 u4::new(2),
82 u4::new(10),
83 u10::new(575),
84 ),
85 18 => Self::new_raw(
86 u7::new(pll_mul as u8),
87 u4::new(2),
88 u4::new(10),
89 u10::new(550),
90 ),
91 19 => Self::new_raw(
92 u7::new(pll_mul as u8),
93 u4::new(2),
94 u4::new(10),
95 u10::new(525),
96 ),
97 20 => Self::new_raw(
98 u7::new(pll_mul as u8),
99 u4::new(2),
100 u4::new(12),
101 u10::new(500),
102 ),
103 21 => Self::new_raw(
104 u7::new(pll_mul as u8),
105 u4::new(2),
106 u4::new(12),
107 u10::new(475),
108 ),
109 22 => Self::new_raw(
110 u7::new(pll_mul as u8),
111 u4::new(2),
112 u4::new(12),
113 u10::new(450),
114 ),
115 23 => Self::new_raw(
116 u7::new(pll_mul as u8),
117 u4::new(2),
118 u4::new(12),
119 u10::new(425),
120 ),
121 24..=25 => Self::new_raw(
122 u7::new(pll_mul as u8),
123 u4::new(2),
124 u4::new(12),
125 u10::new(400),
126 ),
127 26 => Self::new_raw(
128 u7::new(pll_mul as u8),
129 u4::new(2),
130 u4::new(12),
131 u10::new(375),
132 ),
133 27..=28 => Self::new_raw(
134 u7::new(pll_mul as u8),
135 u4::new(2),
136 u4::new(12),
137 u10::new(350),
138 ),
139
140 29..=30 => Self::new_raw(
141 u7::new(pll_mul as u8),
142 u4::new(2),
143 u4::new(12),
144 u10::new(325),
145 ),
146 31..=33 => Self::new_raw(
147 u7::new(pll_mul as u8),
148 u4::new(2),
149 u4::new(2),
150 u10::new(300),
151 ),
152 34..=36 => Self::new_raw(
153 u7::new(pll_mul as u8),
154 u4::new(2),
155 u4::new(2),
156 u10::new(275),
157 ),
158 37..=40 => Self::new_raw(
159 u7::new(pll_mul as u8),
160 u4::new(2),
161 u4::new(2),
162 u10::new(250),
163 ),
164 41..=47 => Self::new_raw(
165 u7::new(pll_mul as u8),
166 u4::new(3),
167 u4::new(12),
168 u10::new(250),
169 ),
170 48..=66 => Self::new_raw(
171 u7::new(pll_mul as u8),
172 u4::new(2),
173 u4::new(4),
174 u10::new(250),
175 ),
176 _ => {
177 unreachable!()
178 }
179 })
180 }
181
182 pub fn new_raw(fdiv: u7, charge_pump: u4, loop_resistor: u4, lock_count: u10) -> Self {
187 Self {
188 fdiv,
189 charge_pump,
190 loop_resistor,
191 lock_count,
192 }
193 }
194}
195
196pub fn configure_arm_pll(boot_mode: BootMode, pll_config: PllConfig) {
198 if ARM_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
199 return;
200 }
201 unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
203}
204
205pub fn configure_io_pll(boot_mode: BootMode, pll_config: PllConfig) {
207 if IO_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
208 return;
209 }
210 unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
212}
213
214pub fn configure_ddr_pll(boot_mode: BootMode, pll_config: PllConfig) {
216 if DDR_PLL_INIT.swap(true, core::sync::atomic::Ordering::SeqCst) {
217 return;
218 }
219 unsafe { configure_arm_pll_unchecked(boot_mode, pll_config) };
221}
222
223pub unsafe fn configure_arm_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
230 unsafe {
231 crate::slcr::Slcr::with(|slcr| {
232 let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_arm_pll_ctrl();
233 let pll_cfg_reg = slcr.clk_ctrl().pointer_to_arm_pll_cfg();
234 configure_pll_unchecked(
235 boot_mode,
236 pll_config,
237 PllType::Arm,
238 slcr,
239 pll_ctrl_reg,
240 pll_cfg_reg,
241 );
242 });
243 }
244}
245
246pub unsafe fn configure_io_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
253 unsafe {
254 crate::slcr::Slcr::with(|slcr| {
255 let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_io_pll_ctrl();
256 let pll_cfg_reg = slcr.clk_ctrl().pointer_to_io_pll_cfg();
257 configure_pll_unchecked(
258 boot_mode,
259 pll_config,
260 PllType::Io,
261 slcr,
262 pll_ctrl_reg,
263 pll_cfg_reg,
264 );
265 });
266 }
267}
268
269pub unsafe fn configure_ddr_pll_unchecked(boot_mode: BootMode, pll_config: PllConfig) {
276 unsafe {
277 crate::slcr::Slcr::with(|slcr| {
278 let pll_ctrl_reg = slcr.clk_ctrl().pointer_to_ddr_pll_ctrl();
279 let pll_cfg_reg = slcr.clk_ctrl().pointer_to_ddr_pll_cfg();
280 configure_pll_unchecked(
281 boot_mode,
282 pll_config,
283 PllType::Ddr,
284 slcr,
285 pll_ctrl_reg,
286 pll_cfg_reg,
287 );
288 });
289 }
290}
291
292enum PllType {
293 Io,
294 Ddr,
295 Arm,
296}
297
298impl PllType {
299 pub const fn bit_offset_pll_locked(&self) -> usize {
300 match self {
301 PllType::Io => 2,
302 PllType::Ddr => 1,
303 PllType::Arm => 0,
304 }
305 }
306}
307
308unsafe fn configure_pll_unchecked(
309 boot_mode: BootMode,
310 cfg: PllConfig,
311 pll_type: PllType,
312 slcr: &mut zynq7000::slcr::MmioSlcr<'static>,
313 pll_ctrl_reg: *mut zynq7000::slcr::clocks::PllControl,
314 pll_cfg_reg: *mut zynq7000::slcr::clocks::PllConfig,
315) {
316 let mut pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
319 pll_ctrl.set_fdiv(cfg.fdiv);
320 unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
321
322 let mut pll_cfg = unsafe { core::ptr::read_volatile(pll_cfg_reg) };
323 pll_cfg.set_charge_pump(cfg.charge_pump);
324 pll_cfg.set_loop_resistor(cfg.loop_resistor);
325 pll_cfg.set_lock_count(cfg.lock_count);
326 unsafe { core::ptr::write_volatile(pll_cfg_reg, pll_cfg) };
327
328 pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
332 if boot_mode.pll_config() == zynq7000::slcr::BootPllConfig::Bypassed {
333 pll_ctrl.set_bypass_qual(false);
334 }
335 pll_ctrl.set_bypass_force(true);
336 pll_ctrl.set_pwrdwn(false);
337 unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
338
339 pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
341 pll_ctrl.set_reset(true);
342 unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
343 pll_ctrl.set_reset(false);
344 unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
345
346 while ((slcr.clk_ctrl().read_pll_status().raw_value() >> pll_type.bit_offset_pll_locked())
347 & 0b1)
348 != 1
349 {
350 cortex_ar::asm::nop();
351 }
352
353 pll_ctrl = unsafe { core::ptr::read_volatile(pll_ctrl_reg) };
354 pll_ctrl.set_bypass_force(false);
355 unsafe { core::ptr::write_volatile(pll_ctrl_reg, pll_ctrl) };
356}