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