1#[cfg(not(feature = "va41628"))]
14use crate::adc::ADC_MAX_CLK;
15use crate::pac;
16
17use crate::time::Hertz;
18pub use vorago_shared_hal::clock::{Clocks, HBO_FREQ};
19use vorago_shared_hal::{enable_peripheral_clock, PeripheralSelect};
20
21pub const XTAL_OSC_TSTART_MS: u32 = 15;
22
23#[derive(Debug, PartialEq, Eq)]
24#[cfg_attr(feature = "defmt", derive(defmt::Format))]
25pub enum FilterClockSelect {
26 SysClk = 0,
27 Clk1 = 1,
28 Clk2 = 2,
29 Clk3 = 3,
30 Clk4 = 4,
31 Clk5 = 5,
32 Clk6 = 6,
33 Clk7 = 7,
34}
35
36#[derive(Debug, Copy, Clone, PartialEq, Eq)]
38#[cfg_attr(feature = "defmt", derive(defmt::Format))]
39pub enum ClockSelect {
40 Hbo = 0b00,
42 XtalN = 0b01,
44 Pll = 0b10,
46 XtalOsc = 0b11,
48}
49
50#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub enum ReferenceClockSelect {
59 #[default]
60 None = 0b00,
61 XtalOsc = 0b01,
62 XtalN = 0b10,
63}
64
65#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
66#[cfg_attr(feature = "defmt", derive(defmt::Format))]
67pub enum ClockDivisorSelect {
68 #[default]
69 Div1 = 0b00,
70 Div2 = 0b01,
71 Div4 = 0b10,
72 Div8 = 0b11,
73}
74
75#[derive(Debug, Copy, Clone, PartialEq, Eq)]
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77pub enum AdcClockDivisorSelect {
78 Div8 = 0b00,
79 Div4 = 0b01,
80 Div2 = 0b10,
81 Div1 = 0b11,
82}
83
84#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub struct PllConfig {
87 pub clkr: u8,
89 pub clkf: u8,
91 pub clkod: u8,
93 pub bwadj: u8,
95}
96
97#[inline]
98pub const fn clock_after_division(clk: Hertz, div_sel: ClockDivisorSelect) -> Hertz {
99 match div_sel {
100 ClockDivisorSelect::Div1 => clk,
101 ClockDivisorSelect::Div2 => Hertz::from_raw(clk.raw() / 2),
102 ClockDivisorSelect::Div4 => Hertz::from_raw(clk.raw() / 4),
103 ClockDivisorSelect::Div8 => Hertz::from_raw(clk.raw() / 8),
104 }
105}
106
107pub fn pll_setup_delay() {
109 for _ in 0..500 {
110 cortex_m::asm::nop()
111 }
112}
113
114pub trait ClkgenExt {
115 fn constrain(self) -> ClockConfigurator;
116}
117
118impl ClkgenExt for pac::Clkgen {
119 fn constrain(self) -> ClockConfigurator {
120 ClockConfigurator {
121 source_clk: None,
122 ref_clk_sel: ReferenceClockSelect::None,
123 clksel_sys: ClockSelect::Hbo,
124 clk_div_sel: ClockDivisorSelect::Div1,
125 clk_lost_detection: false,
126 pll_lock_lost_detection: false,
127 pll_cfg: None,
128 clkgen: self,
129 }
130 }
131}
132
133#[derive(Debug, PartialEq, Eq)]
134#[cfg_attr(feature = "defmt", derive(defmt::Format))]
135pub struct ClockSourceFrequencyNotSet;
136
137#[derive(Debug, PartialEq, Eq)]
138#[cfg_attr(feature = "defmt", derive(defmt::Format))]
139pub enum ClockConfigError {
140 ClkSourceFreqNotSet,
141 PllConfigNotSet,
142 PllInitError,
143 InconsistentCfg,
144}
145
146pub struct ClockConfigurator {
147 ref_clk_sel: ReferenceClockSelect,
148 clksel_sys: ClockSelect,
149 clk_div_sel: ClockDivisorSelect,
150 source_clk: Option<Hertz>,
153 pll_cfg: Option<PllConfig>,
154 clk_lost_detection: bool,
155 #[cfg(feature = "revb")]
157 pll_lock_lost_detection: bool,
158 clkgen: pac::Clkgen,
159}
160
161pub fn hbo_clock_delay_ms(ms: u32) {
166 let wdt = unsafe { pac::WatchDog::steal() };
167 for _ in 0..ms {
168 for _ in 0..10_000 {
169 cortex_m::asm::nop();
170 }
171 wdt.wdogintclr().write(|w| unsafe { w.bits(1) });
172 }
173}
174
175impl ClockConfigurator {
176 pub fn new(clkgen: pac::Clkgen) -> Self {
178 ClockConfigurator {
179 source_clk: None,
180 ref_clk_sel: ReferenceClockSelect::None,
181 clksel_sys: ClockSelect::Hbo,
182 clk_div_sel: ClockDivisorSelect::Div1,
183 clk_lost_detection: false,
184 pll_lock_lost_detection: false,
185 pll_cfg: None,
186 clkgen,
187 }
188 }
189
190 pub unsafe fn steal() -> Self {
196 Self::new(unsafe { pac::Clkgen::steal() })
197 }
198
199 #[inline]
200 pub fn source_clk(mut self, src_clk: Hertz) -> Self {
201 self.source_clk = Some(src_clk);
202 self
203 }
204
205 #[inline]
210 pub fn xtal_n_clk(mut self) -> Self {
211 self.clksel_sys = ClockSelect::XtalN;
212 self.ref_clk_sel = ReferenceClockSelect::XtalN;
213 self
214 }
215
216 #[inline]
217 pub fn xtal_n_clk_with_src_freq(mut self, src_clk: Hertz) -> Self {
218 self = self.xtal_n_clk();
219 self.source_clk(src_clk)
220 }
221
222 #[inline]
223 pub fn clksel_sys(mut self, clksel_sys: ClockSelect) -> Self {
224 self.clksel_sys = clksel_sys;
225 self
226 }
227
228 #[inline]
229 pub fn pll_cfg(mut self, pll_cfg: PllConfig) -> Self {
230 self.pll_cfg = Some(pll_cfg);
231 self
232 }
233
234 #[inline]
235 pub fn ref_clk_sel(mut self, ref_clk_sel: ReferenceClockSelect) -> Self {
236 self.ref_clk_sel = ref_clk_sel;
237 self
238 }
239
240 pub fn freeze(self) -> Result<Clocks, ClockConfigError> {
250 if self.source_clk.is_none() {
252 return Err(ClockConfigError::ClkSourceFreqNotSet);
253 }
254 if self.clksel_sys == ClockSelect::XtalOsc
255 && self.ref_clk_sel != ReferenceClockSelect::XtalOsc
256 {
257 return Err(ClockConfigError::InconsistentCfg);
258 }
259 if self.clksel_sys == ClockSelect::XtalN && self.ref_clk_sel != ReferenceClockSelect::XtalN
260 {
261 return Err(ClockConfigError::InconsistentCfg);
262 }
263 if self.clksel_sys == ClockSelect::Pll && self.pll_cfg.is_none() {
264 return Err(ClockConfigError::PllConfigNotSet);
265 }
266
267 enable_peripheral_clock(PeripheralSelect::Clkgen);
268 let mut final_sysclk = self.source_clk.unwrap();
269 self.clkgen
274 .ctrl0()
275 .modify(|_, w| unsafe { w.clksel_sys().bits(ClockSelect::Hbo as u8) });
276 pll_setup_delay();
277 self.clkgen
278 .ctrl0()
279 .modify(|_, w| unsafe { w.clk_div_sel().bits(ClockDivisorSelect::Div1 as u8) });
280
281 self.clkgen
283 .ctrl0()
284 .modify(|_, w| unsafe { w.ref_clk_sel().bits(self.ref_clk_sel as u8) });
285 self.clkgen.ctrl1().modify(|_, w| {
286 w.xtal_en().clear_bit();
287 w.xtal_n_en().clear_bit();
288 w
289 });
290 match self.ref_clk_sel {
291 ReferenceClockSelect::None => pll_setup_delay(),
292 ReferenceClockSelect::XtalOsc => {
293 self.clkgen.ctrl1().modify(|_, w| w.xtal_en().set_bit());
294 hbo_clock_delay_ms(XTAL_OSC_TSTART_MS);
295 }
296 ReferenceClockSelect::XtalN => {
297 self.clkgen.ctrl1().modify(|_, w| w.xtal_n_en().set_bit());
298 pll_setup_delay()
299 }
300 }
301
302 match self.pll_cfg {
304 Some(cfg) => {
305 self.clkgen.ctrl0().modify(|_, w| w.pll_pwdn().clear_bit());
306 cortex_m::asm::nop();
308 cortex_m::asm::nop();
309 self.clkgen.ctrl0().modify(|_, w| {
310 unsafe {
311 w.pll_clkf().bits(cfg.clkf);
312 }
313 unsafe {
314 w.pll_clkr().bits(cfg.clkr);
315 }
316 unsafe {
317 w.pll_clkod().bits(cfg.clkod);
318 }
319 unsafe {
320 w.pll_bwadj().bits(cfg.bwadj);
321 }
322 w.pll_test().clear_bit();
323 w.pll_bypass().clear_bit();
324 w.pll_intfb().set_bit()
325 });
326 final_sysclk /= cfg.clkr as u32 + 1;
328 final_sysclk *= cfg.clkf as u32 + 1;
329 final_sysclk /= cfg.clkod as u32 + 1;
330
331 self.clkgen.ctrl0().modify(|_, w| w.pll_reset().set_bit());
333 pll_setup_delay();
336 self.clkgen.ctrl0().modify(|_, w| w.pll_reset().clear_bit());
337 pll_setup_delay();
338
339 let stat = self.clkgen.stat().read();
341 if stat.fbslip().bit() || stat.rfslip().bit() {
342 pll_setup_delay();
343 if stat.fbslip().bit() || stat.rfslip().bit() {
344 return Err(ClockConfigError::PllInitError);
348 }
349 }
350 }
351 None => {
352 self.clkgen.ctrl0().modify(|_, w| w.pll_pwdn().set_bit());
353 }
354 }
355
356 if self.clk_lost_detection {
357 rearm_sysclk_lost_with_periph(&self.clkgen)
358 }
359 #[cfg(feature = "revb")]
360 if self.pll_lock_lost_detection {
361 rearm_pll_lock_lost_with_periph(&self.clkgen)
362 }
363
364 self.clkgen
365 .ctrl0()
366 .modify(|_, w| unsafe { w.clk_div_sel().bits(self.clk_div_sel as u8) });
367 final_sysclk = clock_after_division(final_sysclk, self.clk_div_sel);
368
369 pll_setup_delay();
371
372 self.clkgen
373 .ctrl0()
374 .modify(|_, w| unsafe { w.clksel_sys().bits(self.clksel_sys as u8) });
375
376 Ok(Clocks::__new(
377 final_sysclk,
378 #[cfg(not(feature = "va41628"))]
379 self.cfg_adc_clk_div(final_sysclk),
380 ))
381 }
382
383 #[cfg(not(feature = "va41628"))]
384 fn cfg_adc_clk_div(&self, final_sysclk: Hertz) -> Hertz {
385 if final_sysclk.raw() <= ADC_MAX_CLK.raw() * 4 {
390 self.clkgen.ctrl1().modify(|_, w| unsafe {
391 w.adc_clk_div_sel().bits(AdcClockDivisorSelect::Div4 as u8)
392 });
393 final_sysclk / 4
394 } else {
395 self.clkgen.ctrl1().modify(|_, w| unsafe {
396 w.adc_clk_div_sel().bits(AdcClockDivisorSelect::Div8 as u8)
397 });
398 final_sysclk / 8
399 }
400 }
401}
402
403pub fn rearm_sysclk_lost() {
404 rearm_sysclk_lost_with_periph(&unsafe { pac::Clkgen::steal() })
405}
406
407fn rearm_sysclk_lost_with_periph(clkgen: &pac::Clkgen) {
408 clkgen
409 .ctrl0()
410 .modify(|_, w| w.sys_clk_lost_det_en().set_bit());
411 clkgen
412 .ctrl1()
413 .write(|w| w.sys_clk_lost_det_rearm().set_bit());
414 clkgen
415 .ctrl1()
416 .write(|w| w.sys_clk_lost_det_rearm().clear_bit());
417}
418
419#[cfg(feature = "revb")]
420pub fn rearm_pll_lock_lost() {
421 rearm_pll_lock_lost_with_periph(&unsafe { pac::Clkgen::steal() })
422}
423
424fn rearm_pll_lock_lost_with_periph(clkgen: &pac::Clkgen) {
425 clkgen
426 .ctrl1()
427 .modify(|_, w| w.pll_lost_lock_det_en().set_bit());
428 clkgen.ctrl1().write(|w| w.pll_lck_det_rearm().set_bit());
429 clkgen.ctrl1().write(|w| w.pll_lck_det_rearm().clear_bit());
430}
431
432#[cfg(test)]
433mod tests {
434
435 use super::*;
436
437 #[test]
438 fn test_basic_div() {
439 assert_eq!(
440 clock_after_division(Hertz::from_raw(10_000_000), super::ClockDivisorSelect::Div2),
441 Hertz::from_raw(5_000_000)
442 );
443 }
444}