aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/clocks/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa/src/clocks/mod.rs')
-rw-r--r--embassy-mcxa/src/clocks/mod.rs943
1 files changed, 943 insertions, 0 deletions
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
new file mode 100644
index 000000000..9c9e6ef3d
--- /dev/null
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -0,0 +1,943 @@
1//! # Clock Module
2//!
3//! For the MCX-A, we separate clock and peripheral control into two main stages:
4//!
5//! 1. At startup, e.g. when `embassy_mcxa::init()` is called, we configure the
6//! core system clocks, including external and internal oscillators. This
7//! configuration is then largely static for the duration of the program.
8//! 2. When HAL drivers are created, e.g. `Lpuart::new()` is called, the driver
9//! is responsible for two main things:
10//! * Ensuring that any required "upstream" core system clocks necessary for
11//! clocking the peripheral is active and configured to a reasonable value
12//! * Enabling the clock gates for that peripheral, and resetting the peripheral
13//!
14//! From a user perspective, only step 1 is visible. Step 2 is automatically handled
15//! by HAL drivers, using interfaces defined in this module.
16//!
17//! It is also possible to *view* the state of the clock configuration after [`init()`]
18//! has been called, using the [`with_clocks()`] function, which provides a view of the
19//! [`Clocks`] structure.
20//!
21//! ## For HAL driver implementors
22//!
23//! The majority of peripherals in the MCXA chip are fed from either a "hard-coded" or
24//! configurable clock source, e.g. selecting the FROM12M or `clk_1m` as a source. This
25//! selection, as well as often any pre-scaler division from that source clock, is made
26//! through MRCC registers.
27//!
28//! Any peripheral that is controlled through the MRCC register can automatically implement
29//! the necessary APIs using the `impl_cc_gate!` macro in this module. You will also need
30//! to define the configuration surface and steps necessary to fully configure that peripheral
31//! from a clocks perspective by:
32//!
33//! 1. Defining a configuration type in the [`periph_helpers`] module that contains any selects
34//! or divisions available to the HAL driver
35//! 2. Implementing the [`periph_helpers::SPConfHelper`] trait, which should check that the
36//! necessary input clocks are reasonable
37
38use core::cell::RefCell;
39
40use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig};
41use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten};
42use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten};
43use periph_helpers::SPConfHelper;
44
45use crate::pac;
46pub mod config;
47pub mod periph_helpers;
48
49//
50// Statics/Consts
51//
52
53/// The state of system core clocks.
54///
55/// Initialized by [`init()`], and then unchanged for the remainder of the program.
56static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None));
57
58//
59// Free functions
60//
61
62/// Initialize the core system clocks with the given [`ClocksConfig`].
63///
64/// This function should be called EXACTLY once at start-up, usually via a
65/// call to [`embassy_mcxa::init()`](crate::init()). Subsequent calls will
66/// return an error.
67pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
68 critical_section::with(|cs| {
69 if CLOCKS.borrow_ref(cs).is_some() {
70 Err(ClockError::AlreadyInitialized)
71 } else {
72 Ok(())
73 }
74 })?;
75
76 let mut clocks = Clocks::default();
77 let mut operator = ClockOperator {
78 clocks: &mut clocks,
79 config: &settings,
80
81 _mrcc0: unsafe { pac::Mrcc0::steal() },
82 scg0: unsafe { pac::Scg0::steal() },
83 syscon: unsafe { pac::Syscon::steal() },
84 vbat0: unsafe { pac::Vbat0::steal() },
85 };
86
87 operator.configure_firc_clocks()?;
88 operator.configure_sirc_clocks()?;
89 operator.configure_fro16k_clocks()?;
90
91 // For now, just use FIRC as the main/cpu clock, which should already be
92 // the case on reset
93 assert!(operator.scg0.rccr().read().scs().is_firc());
94 let input = operator.clocks.fro_hf_root.clone().unwrap();
95 operator.clocks.main_clk = Some(input.clone());
96 // We can also assume cpu/system clk == fro_hf because div is /1.
97 assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0);
98 operator.clocks.cpu_system_clk = Some(input);
99
100 critical_section::with(|cs| {
101 let mut clks = CLOCKS.borrow_ref_mut(cs);
102 assert!(clks.is_none(), "Clock setup race!");
103 *clks = Some(clocks);
104 });
105
106 Ok(())
107}
108
109/// Obtain the full clocks structure, calling the given closure in a critical section.
110///
111/// The given closure will be called with read-only access to the state of the system
112/// clocks. This can be used to query and return the state of a given clock.
113///
114/// As the caller's closure will be called in a critical section, care must be taken
115/// not to block or cause any other undue delays while accessing.
116///
117/// Calls to this function will not succeed until after a successful call to `init()`,
118/// and will always return None.
119pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
120 critical_section::with(|cs| {
121 let c = CLOCKS.borrow_ref(cs);
122 let c = c.as_ref()?;
123 Some(f(c))
124 })
125}
126
127//
128// Structs/Enums
129//
130
131/// The `Clocks` structure contains the initialized state of the core system clocks
132///
133/// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function
134/// at boot time.
135#[derive(Default, Debug, Clone)]
136#[non_exhaustive]
137pub struct Clocks {
138 /// The `clk_in` is a clock provided by an external oscillator
139 pub clk_in: Option<Clock>,
140
141 // FRO180M stuff
142 //
143 /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator
144 ///
145 /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`,
146 /// and `fro_hf_div`.
147 pub fro_hf_root: Option<Clock>,
148
149 /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate.
150 pub fro_hf: Option<Clock>,
151
152 /// `clk_45` is a 45MHz clock, sourced from `fro_hf`.
153 pub clk_45m: Option<Clock>,
154
155 /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`.
156 pub fro_hf_div: Option<Clock>,
157
158 //
159 // End FRO180M
160
161 // FRO12M stuff
162 //
163 /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator
164 ///
165 /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`,
166 /// `and `fro_lf_div`.
167 pub fro_12m_root: Option<Clock>,
168
169 /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate.
170 pub fro_12m: Option<Clock>,
171
172 /// `clk_1m` is a 1MHz clock, sourced from `fro_12m`
173 pub clk_1m: Option<Clock>,
174
175 /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m`
176 pub fro_lf_div: Option<Clock>,
177 //
178 // End FRO12M stuff
179 /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator.
180 ///
181 /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in
182 /// the system domain, such as the CMP and RTC.
183 pub clk_16k_vsys: Option<Clock>,
184
185 /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator.
186 ///
187 /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in
188 /// the VDD Core domain, such as the OSTimer or LPUarts.
189 pub clk_16k_vdd_core: Option<Clock>,
190
191 /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some
192 /// peripherals.
193 pub main_clk: Option<Clock>,
194
195 /// `CPU_CLK` or `SYSTEM_CLK` is the output of `main_clk`, run through the `AHBCLKDIV`
196 pub cpu_system_clk: Option<Clock>,
197
198 /// `pll1_clk` is the output of the main system PLL, `pll1`.
199 pub pll1_clk: Option<Clock>,
200}
201
202/// `ClockError` is the main error returned when configuring or checking clock state
203#[derive(Debug, Copy, Clone, Eq, PartialEq)]
204#[cfg_attr(feature = "defmt", derive(defmt::Format))]
205#[non_exhaustive]
206pub enum ClockError {
207 /// The system clocks were never initialized by calling [`init()`]
208 NeverInitialized,
209 /// The [`init()`] function was called more than once
210 AlreadyInitialized,
211 /// The requested configuration was not possible to fulfill, as the system clocks
212 /// were not configured in a compatible way
213 BadConfig { clock: &'static str, reason: &'static str },
214 /// The requested configuration was not possible to fulfill, as the required system
215 /// clocks have not yet been implemented.
216 NotImplemented { clock: &'static str },
217 /// The requested peripheral could not be configured, as the steps necessary to
218 /// enable it have not yet been implemented.
219 UnimplementedConfig,
220}
221
222/// Information regarding a system clock
223#[derive(Debug, Clone)]
224pub struct Clock {
225 /// The frequency, in Hz, of the given clock
226 pub frequency: u32,
227 /// The power state of the clock, e.g. whether it is active in deep sleep mode
228 /// or not.
229 pub power: PoweredClock,
230}
231
232/// The power state of a given clock.
233///
234/// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep
235/// mode will be stopped. This means that any downstream usage, e.g. by peripherals,
236/// will also stop.
237///
238/// In the future, we will provide an API for entering Deep Sleep, and if there are
239/// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into
240/// Deep Sleep will be prevented, in order to avoid misbehaving peripherals.
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub enum PoweredClock {
243 /// The given clock will NOT continue running in Deep Sleep mode
244 NormalEnabledDeepSleepDisabled,
245 /// The given clock WILL continue running in Deep Sleep mode
246 AlwaysEnabled,
247}
248
249/// The ClockOperator is a private helper type that contains the methods used
250/// during system clock initialization.
251///
252/// # SAFETY
253///
254/// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`,
255/// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function.
256struct ClockOperator<'a> {
257 /// A mutable reference to the current state of system clocks
258 clocks: &'a mut Clocks,
259 /// A reference to the requested configuration provided by the caller of [`init()`]
260 config: &'a ClocksConfig,
261
262 // We hold on to stolen peripherals
263 _mrcc0: pac::Mrcc0,
264 scg0: pac::Scg0,
265 syscon: pac::Syscon,
266 vbat0: pac::Vbat0,
267}
268
269/// Trait describing an AHB clock gate that can be toggled through MRCC.
270pub trait Gate {
271 type MrccPeriphConfig: SPConfHelper;
272
273 /// Enable the clock gate.
274 ///
275 /// # SAFETY
276 ///
277 /// The current peripheral must be disabled prior to calling this method
278 unsafe fn enable_clock();
279
280 /// Disable the clock gate.
281 ///
282 /// # SAFETY
283 ///
284 /// There must be no active user of this peripheral when calling this method
285 unsafe fn disable_clock();
286
287 /// Drive the peripheral into reset.
288 ///
289 /// # SAFETY
290 ///
291 /// There must be no active user of this peripheral when calling this method
292 unsafe fn assert_reset();
293
294 /// Drive the peripheral out of reset.
295 ///
296 /// # SAFETY
297 ///
298 /// There must be no active user of this peripheral when calling this method
299 unsafe fn release_reset();
300
301 /// Return whether the clock gate for this peripheral is currently enabled.
302 fn is_clock_enabled() -> bool;
303
304 /// Return whether the peripheral is currently held in reset.
305 fn is_reset_released() -> bool;
306}
307
308/// This is the primary helper method HAL drivers are expected to call when creating
309/// an instance of the peripheral.
310///
311/// This method:
312///
313/// 1. Enables the MRCC clock gate for this peripheral
314/// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error
315/// and re-disabling the peripheral if this fails.
316/// 3. Pulses the MRCC reset line, to reset the peripheral to the default state
317/// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account
318/// the selected upstream clock, as well as any division specified by `cfg`.
319///
320/// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method
321/// may return `Ok(0)`. In the future, this might be updated to return the correct
322/// "ambient" clock, e.g. the AHB/APB frequency.
323///
324/// # SAFETY
325///
326/// This peripheral must not yet be in use prior to calling `enable_and_reset`.
327#[inline]
328pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
329 let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?;
330 pulse_reset::<G>();
331 Ok(freq)
332}
333
334/// Enable the clock gate for the given peripheral.
335///
336/// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need
337/// to control the duration of the pulse more directly.
338///
339/// # SAFETY
340///
341/// This peripheral must not yet be in use prior to calling `enable`.
342#[inline]
343pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
344 G::enable_clock();
345 while !G::is_clock_enabled() {}
346 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
347
348 let freq = critical_section::with(|cs| {
349 let clocks = CLOCKS.borrow_ref(cs);
350 let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?;
351 cfg.post_enable_config(clocks)
352 });
353
354 freq.inspect_err(|_e| {
355 G::disable_clock();
356 })
357}
358
359/// Disable the clock gate for the given peripheral.
360///
361/// # SAFETY
362///
363/// This peripheral must no longer be in use prior to calling `enable`.
364#[allow(dead_code)]
365#[inline]
366pub unsafe fn disable<G: Gate>() {
367 G::disable_clock();
368}
369
370/// Check whether a gate is currently enabled.
371#[allow(dead_code)]
372#[inline]
373pub fn is_clock_enabled<G: Gate>() -> bool {
374 G::is_clock_enabled()
375}
376
377/// Release a reset line for the given peripheral set.
378///
379/// Prefer [`enable_and_reset`].
380///
381/// # SAFETY
382///
383/// This peripheral must not yet be in use prior to calling `release_reset`.
384#[inline]
385pub unsafe fn release_reset<G: Gate>() {
386 G::release_reset();
387}
388
389/// Assert a reset line for the given peripheral set.
390///
391/// Prefer [`enable_and_reset`].
392///
393/// # SAFETY
394///
395/// This peripheral must not yet be in use prior to calling `assert_reset`.
396#[inline]
397pub unsafe fn assert_reset<G: Gate>() {
398 G::assert_reset();
399}
400
401/// Check whether the peripheral is held in reset.
402#[inline]
403pub unsafe fn is_reset_released<G: Gate>() -> bool {
404 G::is_reset_released()
405}
406
407/// Pulse a reset line (assert then release) with a short delay.
408///
409/// Prefer [`enable_and_reset`].
410///
411/// # SAFETY
412///
413/// This peripheral must not yet be in use prior to calling `release_reset`.
414#[inline]
415pub unsafe fn pulse_reset<G: Gate>() {
416 G::assert_reset();
417 cortex_m::asm::nop();
418 cortex_m::asm::nop();
419 G::release_reset();
420}
421
422//
423// `impl`s for structs/enums
424//
425
426/// The [`Clocks`] type's methods generally take the form of "ensure X clock is active".
427///
428/// These methods are intended to be used by HAL peripheral implementors to ensure that their
429/// selected clocks are active at a suitable level at time of construction. These methods
430/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].
431impl Clocks {
432 /// Ensure the `fro_lf_div` clock is active and valid at the given power state.
433 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
434 let Some(clk) = self.fro_lf_div.as_ref() else {
435 return Err(ClockError::BadConfig {
436 clock: "fro_lf_div",
437 reason: "required but not active",
438 });
439 };
440 if !clk.power.meets_requirement_of(at_level) {
441 return Err(ClockError::BadConfig {
442 clock: "fro_lf_div",
443 reason: "not low power active",
444 });
445 }
446 Ok(clk.frequency)
447 }
448
449 /// Ensure the `fro_hf` clock is active and valid at the given power state.
450 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
451 let Some(clk) = self.fro_hf.as_ref() else {
452 return Err(ClockError::BadConfig {
453 clock: "fro_hf",
454 reason: "required but not active",
455 });
456 };
457 if !clk.power.meets_requirement_of(at_level) {
458 return Err(ClockError::BadConfig {
459 clock: "fro_hf",
460 reason: "not low power active",
461 });
462 }
463 Ok(clk.frequency)
464 }
465
466 /// Ensure the `fro_hf_div` clock is active and valid at the given power state.
467 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
468 let Some(clk) = self.fro_hf_div.as_ref() else {
469 return Err(ClockError::BadConfig {
470 clock: "fro_hf_div",
471 reason: "required but not active",
472 });
473 };
474 if !clk.power.meets_requirement_of(at_level) {
475 return Err(ClockError::BadConfig {
476 clock: "fro_hf_div",
477 reason: "not low power active",
478 });
479 }
480 Ok(clk.frequency)
481 }
482
483 /// Ensure the `clk_in` clock is active and valid at the given power state.
484 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
485 Err(ClockError::NotImplemented { clock: "clk_in" })
486 }
487
488 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
489 pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
490 // NOTE: clk_16k is always active in low power mode
491 Ok(self
492 .clk_16k_vsys
493 .as_ref()
494 .ok_or(ClockError::BadConfig {
495 clock: "clk_16k_vsys",
496 reason: "required but not active",
497 })?
498 .frequency)
499 }
500
501 /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state.
502 pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
503 // NOTE: clk_16k is always active in low power mode
504 Ok(self
505 .clk_16k_vdd_core
506 .as_ref()
507 .ok_or(ClockError::BadConfig {
508 clock: "clk_16k_vdd_core",
509 reason: "required but not active",
510 })?
511 .frequency)
512 }
513
514 /// Ensure the `clk_1m` clock is active and valid at the given power state.
515 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
516 let Some(clk) = self.clk_1m.as_ref() else {
517 return Err(ClockError::BadConfig {
518 clock: "clk_1m",
519 reason: "required but not active",
520 });
521 };
522 if !clk.power.meets_requirement_of(at_level) {
523 return Err(ClockError::BadConfig {
524 clock: "clk_1m",
525 reason: "not low power active",
526 });
527 }
528 Ok(clk.frequency)
529 }
530
531 /// Ensure the `pll1_clk` clock is active and valid at the given power state.
532 pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
533 Err(ClockError::NotImplemented { clock: "pll1_clk" })
534 }
535
536 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.
537 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
538 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
539 }
540
541 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active
542 pub fn ensure_cpu_system_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
543 let Some(clk) = self.cpu_system_clk.as_ref() else {
544 return Err(ClockError::BadConfig {
545 clock: "cpu_system_clk",
546 reason: "required but not active",
547 });
548 };
549 // Can the main_clk ever be active in deep sleep? I think it is gated?
550 match at_level {
551 PoweredClock::NormalEnabledDeepSleepDisabled => {}
552 PoweredClock::AlwaysEnabled => {
553 return Err(ClockError::BadConfig {
554 clock: "main_clk",
555 reason: "not low power active",
556 });
557 }
558 }
559
560 Ok(clk.frequency)
561 }
562
563 pub fn ensure_slow_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
564 let freq = self.ensure_cpu_system_clk_active(at_level)?;
565
566 Ok(freq / 6)
567 }
568}
569
570impl PoweredClock {
571 /// Does THIS clock meet the power requirements of the OTHER clock?
572 pub fn meets_requirement_of(&self, other: &Self) -> bool {
573 match (self, other) {
574 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false,
575 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
576 (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
577 (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true,
578 }
579 }
580}
581
582impl ClockOperator<'_> {
583 /// Configure the FIRC/FRO180M clock family
584 ///
585 /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used
586 /// as the main clock used for the CPU, AHB, APB, etc.
587 fn configure_firc_clocks(&mut self) -> Result<(), ClockError> {
588 const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig {
589 clock: "firc",
590 reason: "For now, FIRC must be enabled and in default state!",
591 });
592
593 // Did the user give us a FIRC config?
594 let Some(firc) = self.config.firc.as_ref() else {
595 return HARDCODED_ERR;
596 };
597 // Is the FIRC set to 45MHz (should be reset default)
598 if !matches!(firc.frequency, FircFreqSel::Mhz45) {
599 return HARDCODED_ERR;
600 }
601 let base_freq = 45_000_000;
602
603 // Now, check if the FIRC as expected for our hardcoded value
604 let mut firc_ok = true;
605
606 // Is the hardware currently set to the default 45MHz?
607 //
608 // NOTE: the SVD currently has the wrong(?) values for these:
609 // 45 -> 48
610 // 60 -> 64
611 // 90 -> 96
612 // 180 -> 192
613 // Probably correct-ish, but for a different trim value?
614 firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s();
615
616 // Check some values in the CSR
617 let csr = self.scg0.firccsr().read();
618 // Is it enabled?
619 firc_ok &= csr.fircen().is_enabled();
620 // Is it accurate?
621 firc_ok &= csr.fircacc().is_enabled_and_valid();
622 // Is there no error?
623 firc_ok &= csr.fircerr().is_error_not_detected();
624 // Is the FIRC the system clock?
625 firc_ok &= csr.fircsel().is_firc();
626 // Is it valid?
627 firc_ok &= csr.fircvld().is_enabled_and_valid();
628
629 // Are we happy with the current (hardcoded) state?
630 if !firc_ok {
631 return HARDCODED_ERR;
632 }
633
634 // Note that the fro_hf_root is active
635 self.clocks.fro_hf_root = Some(Clock {
636 frequency: base_freq,
637 power: firc.power,
638 });
639
640 // Okay! Now we're past that, let's enable all the downstream clocks.
641 let FircConfig {
642 frequency: _,
643 power,
644 fro_hf_enabled,
645 clk_45m_enabled,
646 fro_hf_div,
647 } = firc;
648
649 // When is the FRO enabled?
650 let pow_set = match power {
651 PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes,
652 PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes,
653 };
654
655 // Do we enable the `fro_hf` output?
656 let fro_hf_set = if *fro_hf_enabled {
657 self.clocks.fro_hf = Some(Clock {
658 frequency: base_freq,
659 power: *power,
660 });
661 FircFclkPeriphEn::Enabled
662 } else {
663 FircFclkPeriphEn::Disabled
664 };
665
666 // Do we enable the `clk_45m` output?
667 let clk_45m_set = if *clk_45m_enabled {
668 self.clocks.clk_45m = Some(Clock {
669 frequency: 45_000_000,
670 power: *power,
671 });
672 FircSclkPeriphEn::Enabled
673 } else {
674 FircSclkPeriphEn::Disabled
675 };
676
677 self.scg0.firccsr().modify(|_r, w| {
678 w.fircsten().variant(pow_set);
679 w.firc_fclk_periph_en().variant(fro_hf_set);
680 w.firc_sclk_periph_en().variant(clk_45m_set);
681 w
682 });
683
684 // Do we enable the `fro_hf_div` output?
685 if let Some(d) = fro_hf_div.as_ref() {
686 // We need `fro_hf` to be enabled
687 if !*fro_hf_enabled {
688 return Err(ClockError::BadConfig {
689 clock: "fro_hf_div",
690 reason: "fro_hf not enabled",
691 });
692 }
693
694 // Halt and reset the div; then set our desired div.
695 self.syscon.frohfdiv().write(|w| {
696 w.halt().halt();
697 w.reset().asserted();
698 unsafe { w.div().bits(d.into_bits()) };
699 w
700 });
701 // Then unhalt it, and reset it
702 self.syscon.frohfdiv().write(|w| {
703 w.halt().run();
704 w.reset().released();
705 w
706 });
707
708 // Wait for clock to stabilize
709 while self.syscon.frohfdiv().read().unstab().is_ongoing() {}
710
711 // Store off the clock info
712 self.clocks.fro_hf_div = Some(Clock {
713 frequency: base_freq / d.into_divisor(),
714 power: *power,
715 });
716 }
717
718 Ok(())
719 }
720
721 /// Configure the SIRC/FRO12M clock family
722 fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> {
723 let SircConfig {
724 power,
725 fro_12m_enabled,
726 fro_lf_div,
727 } = &self.config.sirc;
728 let base_freq = 12_000_000;
729
730 // Allow writes
731 self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled());
732 self.clocks.fro_12m_root = Some(Clock {
733 frequency: base_freq,
734 power: *power,
735 });
736
737 let deep = match power {
738 PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled,
739 PoweredClock::AlwaysEnabled => Sircsten::Enabled,
740 };
741 let pclk = if *fro_12m_enabled {
742 self.clocks.fro_12m = Some(Clock {
743 frequency: base_freq,
744 power: *power,
745 });
746 self.clocks.clk_1m = Some(Clock {
747 frequency: base_freq / 12,
748 power: *power,
749 });
750 SircClkPeriphEn::Enabled
751 } else {
752 SircClkPeriphEn::Disabled
753 };
754
755 // Set sleep/peripheral usage
756 self.scg0.sirccsr().modify(|_r, w| {
757 w.sircsten().variant(deep);
758 w.sirc_clk_periph_en().variant(pclk);
759 w
760 });
761
762 while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {}
763 if self.scg0.sirccsr().read().sircerr().is_error_detected() {
764 return Err(ClockError::BadConfig {
765 clock: "sirc",
766 reason: "error set",
767 });
768 }
769
770 // reset lock
771 self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled());
772
773 // Do we enable the `fro_lf_div` output?
774 if let Some(d) = fro_lf_div.as_ref() {
775 // We need `fro_lf` to be enabled
776 if !*fro_12m_enabled {
777 return Err(ClockError::BadConfig {
778 clock: "fro_lf_div",
779 reason: "fro_12m not enabled",
780 });
781 }
782
783 // Halt and reset the div; then set our desired div.
784 self.syscon.frolfdiv().write(|w| {
785 w.halt().halt();
786 w.reset().asserted();
787 unsafe { w.div().bits(d.into_bits()) };
788 w
789 });
790 // Then unhalt it, and reset it
791 self.syscon.frolfdiv().modify(|_r, w| {
792 w.halt().run();
793 w.reset().released();
794 w
795 });
796
797 // Wait for clock to stabilize
798 while self.syscon.frolfdiv().read().unstab().is_ongoing() {}
799
800 // Store off the clock info
801 self.clocks.fro_lf_div = Some(Clock {
802 frequency: base_freq / d.into_divisor(),
803 power: *power,
804 });
805 }
806
807 Ok(())
808 }
809
810 /// Configure the FRO16K/clk_16k clock family
811 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
812 let Some(fro16k) = self.config.fro16k.as_ref() else {
813 return Ok(());
814 };
815 // Enable FRO16K oscillator
816 self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit());
817
818 // Lock the control register
819 self.vbat0.frolcka().modify(|_, w| w.lock().set_bit());
820
821 let Fro16KConfig {
822 vsys_domain_active,
823 vdd_core_domain_active,
824 } = fro16k;
825
826 // Enable clock outputs to both VSYS and VDD_CORE domains
827 // Bit 0: clk_16k0 to VSYS domain
828 // Bit 1: clk_16k1 to VDD_CORE domain
829 //
830 // TODO: Define sub-fields for this register with a PAC patch?
831 let mut bits = 0;
832 if *vsys_domain_active {
833 bits |= 0b01;
834 self.clocks.clk_16k_vsys = Some(Clock {
835 frequency: 16_384,
836 power: PoweredClock::AlwaysEnabled,
837 });
838 }
839 if *vdd_core_domain_active {
840 bits |= 0b10;
841 self.clocks.clk_16k_vdd_core = Some(Clock {
842 frequency: 16_384,
843 power: PoweredClock::AlwaysEnabled,
844 });
845 }
846 self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) });
847
848 Ok(())
849 }
850}
851
852//
853// Macros/macro impls
854//
855
856/// This macro is used to implement the [`Gate`] trait for a given peripheral
857/// that is controlled by the MRCC peripheral.
858macro_rules! impl_cc_gate {
859 ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => {
860 impl Gate for crate::peripherals::$name {
861 type MrccPeriphConfig = $config;
862
863 #[inline]
864 unsafe fn enable_clock() {
865 let mrcc = unsafe { pac::Mrcc0::steal() };
866 mrcc.$clk_reg().modify(|_, w| w.$field().enabled());
867 }
868
869 #[inline]
870 unsafe fn disable_clock() {
871 let mrcc = unsafe { pac::Mrcc0::steal() };
872 mrcc.$clk_reg().modify(|_r, w| w.$field().disabled());
873 }
874
875 #[inline]
876 fn is_clock_enabled() -> bool {
877 let mrcc = unsafe { pac::Mrcc0::steal() };
878 mrcc.$clk_reg().read().$field().is_enabled()
879 }
880
881 #[inline]
882 unsafe fn release_reset() {
883 let mrcc = unsafe { pac::Mrcc0::steal() };
884 mrcc.$rst_reg().modify(|_, w| w.$field().enabled());
885 }
886
887 #[inline]
888 unsafe fn assert_reset() {
889 let mrcc = unsafe { pac::Mrcc0::steal() };
890 mrcc.$rst_reg().modify(|_, w| w.$field().disabled());
891 }
892
893 #[inline]
894 fn is_reset_released() -> bool {
895 let mrcc = unsafe { pac::Mrcc0::steal() };
896 mrcc.$rst_reg().read().$field().is_enabled()
897 }
898 }
899 };
900}
901
902/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,
903/// for various low level peripherals.
904pub(crate) mod gate {
905 #[cfg(not(feature = "time"))]
906 use super::periph_helpers::OsTimerConfig;
907 use super::periph_helpers::{AdcConfig, Lpi2cConfig, LpuartConfig, NoConfig};
908 use super::*;
909
910 // These peripherals have no additional upstream clocks or configuration required
911 // other than enabling through the MRCC gate. Currently, these peripherals will
912 // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or
913 // [`SPConfHelper::post_enable_config()`].
914 impl_cc_gate!(PORT0, mrcc_glb_cc1, mrcc_glb_rst1, port0, NoConfig);
915 impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig);
916 impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig);
917 impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig);
918 impl_cc_gate!(PORT4, mrcc_glb_cc1, mrcc_glb_rst1, port4, NoConfig);
919
920 impl_cc_gate!(GPIO0, mrcc_glb_cc2, mrcc_glb_rst2, gpio0, NoConfig);
921 impl_cc_gate!(GPIO1, mrcc_glb_cc2, mrcc_glb_rst2, gpio1, NoConfig);
922 impl_cc_gate!(GPIO2, mrcc_glb_cc2, mrcc_glb_rst2, gpio2, NoConfig);
923 impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig);
924 impl_cc_gate!(GPIO4, mrcc_glb_cc2, mrcc_glb_rst2, gpio4, NoConfig);
925
926 // These peripherals DO have meaningful configuration, and could fail if the system
927 // clocks do not match their needs.
928 #[cfg(not(feature = "time"))]
929 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
930
931 impl_cc_gate!(LPI2C0, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c0, Lpi2cConfig);
932 impl_cc_gate!(LPI2C1, mrcc_glb_cc0, mrcc_glb_rst0, lpi2c1, Lpi2cConfig);
933 impl_cc_gate!(LPI2C2, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c2, Lpi2cConfig);
934 impl_cc_gate!(LPI2C3, mrcc_glb_cc1, mrcc_glb_rst1, lpi2c3, Lpi2cConfig);
935
936 impl_cc_gate!(LPUART0, mrcc_glb_cc0, mrcc_glb_rst0, lpuart0, LpuartConfig);
937 impl_cc_gate!(LPUART1, mrcc_glb_cc0, mrcc_glb_rst0, lpuart1, LpuartConfig);
938 impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig);
939 impl_cc_gate!(LPUART3, mrcc_glb_cc0, mrcc_glb_rst0, lpuart3, LpuartConfig);
940 impl_cc_gate!(LPUART4, mrcc_glb_cc0, mrcc_glb_rst0, lpuart4, LpuartConfig);
941 impl_cc_gate!(LPUART5, mrcc_glb_cc1, mrcc_glb_rst1, lpuart5, LpuartConfig);
942 impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig);
943}