aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/clocks/config.rs199
-rw-r--r--src/clocks/mod.rs981
-rw-r--r--src/clocks/periph_helpers.rs160
-rw-r--r--src/config.rs3
4 files changed, 821 insertions, 522 deletions
diff --git a/src/clocks/config.rs b/src/clocks/config.rs
new file mode 100644
index 000000000..a517afcca
--- /dev/null
+++ b/src/clocks/config.rs
@@ -0,0 +1,199 @@
1//! Clock Configuration
2//!
3//! This module holds configuration types used for the system clocks. For
4//! configuration of individual peripherals, see [`super::periph_helpers`].
5
6use super::PoweredClock;
7
8/// This type represents a divider in the range 1..=256.
9///
10/// At a hardware level, this is an 8-bit register from 0..=255,
11/// which adds one.
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub struct Div8(pub(super) u8);
14
15impl Div8 {
16 /// Store a "raw" divisor value that will divide the source by
17 /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source
18 /// by 1, and `Div8::from_raw(255)` will divide the source by
19 /// 256.
20 pub const fn from_raw(n: u8) -> Self {
21 Self(n)
22 }
23
24 /// Store a specific divisor value that will divide the source
25 /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source
26 /// by 1, and `Div8::from_divisor(256)` will divide the source
27 /// by 256.
28 ///
29 /// Will return `None` if `n` is not in the range `1..=256`.
30 /// Consider [`Self::from_raw`] for an infallible version.
31 pub const fn from_divisor(n: u16) -> Option<Self> {
32 let Some(n) = n.checked_sub(1) else {
33 return None;
34 };
35 if n > (u8::MAX as u16) {
36 return None;
37 }
38 Some(Self(n as u8))
39 }
40
41 /// Convert into "raw" bits form
42 #[inline(always)]
43 pub const fn into_bits(self) -> u8 {
44 self.0
45 }
46
47 /// Convert into "divisor" form, as a u32 for convenient frequency math
48 #[inline(always)]
49 pub const fn into_divisor(self) -> u32 {
50 self.0 as u32 + 1
51 }
52}
53
54/// ```text
55/// ┌─────────────────────────────────────────────────────────┐
56/// │ │
57/// │ ┌───────────┐ clk_out ┌─────────┐ │
58/// XTAL ──────┼──▷│ System │───────────▷│ │ clk_in │
59/// │ │ OSC │ clkout_byp │ MUX │──────────────────┼──────▷
60/// EXTAL ──────┼──▷│ │───────────▷│ │ │
61/// │ └───────────┘ └─────────┘ │
62/// │ │
63/// │ ┌───────────┐ fro_hf_root ┌────┐ fro_hf │
64/// │ │ FRO180 ├───────┬─────▷│ CG │─────────────────────┼──────▷
65/// │ │ │ │ ├────┤ clk_45m │
66/// │ │ │ └─────▷│ CG │─────────────────────┼──────▷
67/// │ └───────────┘ └────┘ │
68/// │ ┌───────────┐ fro_12m_root ┌────┐ fro_12m │
69/// │ │ FRO12M │────────┬─────▷│ CG │────────────────────┼──────▷
70/// │ │ │ │ ├────┤ clk_1m │
71/// │ │ │ └─────▷│1/12│────────────────────┼──────▷
72/// │ └───────────┘ └────┘ │
73/// │ │
74/// │ ┌──────────┐ │
75/// │ │000 │ │
76/// │ clk_in │ │ │
77/// │ ───────────────▷│001 │ │
78/// │ fro_12m │ │ │
79/// │ ───────────────▷│010 │ │
80/// │ fro_hf_root │ │ │
81/// │ ───────────────▷│011 │ main_clk │
82/// │ │ │───────────────────────────┼──────▷
83/// clk_16k ──────┼─────────────────▷│100 │ │
84/// │ none │ │ │
85/// │ ───────────────▷│101 │ │
86/// │ pll1_clk │ │ │
87/// │ ───────────────▷│110 │ │
88/// │ none │ │ │
89/// │ ───────────────▷│111 │ │
90/// │ └──────────┘ │
91/// │ ▲ │
92/// │ │ │
93/// │ SCG SCS │
94/// │ SCG-Lite │
95/// └─────────────────────────────────────────────────────────┘
96///
97///
98/// clk_in ┌─────┐
99/// ───────────────▷│00 │
100/// clk_45m │ │
101/// ───────────────▷│01 │ ┌───────────┐ pll1_clk
102/// none │ │─────▷│ SPLL │───────────────▷
103/// ───────────────▷│10 │ └───────────┘
104/// fro_12m │ │
105/// ───────────────▷│11 │
106/// └─────┘
107/// ```
108#[non_exhaustive]
109pub struct ClocksConfig {
110 /// FIRC, FRO180, 45/60/90/180M clock source
111 pub firc: Option<FircConfig>,
112 /// SIRC, FRO12M, clk_12m clock source
113 // NOTE: I don't think we *can* disable the SIRC?
114 pub sirc: SircConfig,
115 /// FRO16K clock source
116 pub fro16k: Option<Fro16KConfig>,
117}
118
119// FIRC/FRO180M
120
121/// ```text
122/// ┌───────────┐ fro_hf_root ┌────┐ fro_hf
123/// │ FRO180M ├───────┬─────▷│GATE│──────────▷
124/// │ │ │ ├────┤ clk_45m
125/// │ │ └─────▷│GATE│──────────▷
126/// └───────────┘ └────┘
127/// ```
128#[non_exhaustive]
129pub struct FircConfig {
130 /// Selected clock frequency
131 pub frequency: FircFreqSel,
132 /// Selected power state of the clock
133 pub power: PoweredClock,
134 /// Is the "fro_hf" gated clock enabled?
135 pub fro_hf_enabled: bool,
136 /// Is the "clk_45m" gated clock enabled?
137 pub clk_45m_enabled: bool,
138 /// Is the "fro_hf_div" clock enabled? Requires `fro_hf`!
139 pub fro_hf_div: Option<Div8>,
140}
141
142/// Selected FIRC frequency
143pub enum FircFreqSel {
144 /// 45MHz Output
145 Mhz45,
146 /// 60MHz Output
147 Mhz60,
148 /// 90MHz Output
149 Mhz90,
150 /// 180MHz Output
151 Mhz180,
152}
153
154// SIRC/FRO12M
155
156/// ```text
157/// ┌───────────┐ fro_12m_root ┌────┐ fro_12m
158/// │ FRO12M │────────┬─────▷│ CG │──────────▷
159/// │ │ │ ├────┤ clk_1m
160/// │ │ └─────▷│1/12│──────────▷
161/// └───────────┘ └────┘
162/// ```
163#[non_exhaustive]
164pub struct SircConfig {
165 pub power: PoweredClock,
166 // peripheral output, aka sirc_12mhz
167 pub fro_12m_enabled: bool,
168 /// Is the "fro_lf_div" clock enabled? Requires `fro_12m`!
169 pub fro_lf_div: Option<Div8>,
170}
171
172#[non_exhaustive]
173pub struct Fro16KConfig {
174 pub vsys_domain_active: bool,
175 pub vdd_core_domain_active: bool,
176}
177
178impl Default for ClocksConfig {
179 fn default() -> Self {
180 Self {
181 firc: Some(FircConfig {
182 frequency: FircFreqSel::Mhz45,
183 power: PoweredClock::NormalEnabledDeepSleepDisabled,
184 fro_hf_enabled: true,
185 clk_45m_enabled: true,
186 fro_hf_div: None,
187 }),
188 sirc: SircConfig {
189 power: PoweredClock::AlwaysEnabled,
190 fro_12m_enabled: true,
191 fro_lf_div: None,
192 },
193 fro16k: Some(Fro16KConfig {
194 vsys_domain_active: true,
195 vdd_core_domain_active: true,
196 }),
197 }
198 }
199}
diff --git a/src/clocks/mod.rs b/src/clocks/mod.rs
index e04f63b8e..c6606b1b6 100644
--- a/src/clocks/mod.rs
+++ b/src/clocks/mod.rs
@@ -1,47 +1,335 @@
1//! Clock control helpers (no magic numbers, PAC field access only). 1//! # Clock Module
2//! Provides reusable gate abstractions for peripherals used by the examples. 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
3use core::cell::RefCell; 38use core::cell::RefCell;
4 39
40use config::{ClocksConfig, FircConfig, FircFreqSel, Fro16KConfig, SircConfig};
5use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten}; 41use mcxa_pac::scg0::firccsr::{FircFclkPeriphEn, FircSclkPeriphEn, Fircsten};
6use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten}; 42use mcxa_pac::scg0::sirccsr::{SircClkPeriphEn, Sircsten};
7use periph_helpers::SPConfHelper; 43use periph_helpers::SPConfHelper;
8 44
9use crate::pac; 45use crate::pac;
46pub mod config;
10pub mod periph_helpers; 47pub mod periph_helpers;
11 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 // TODO, everything downstream
91
92 critical_section::with(|cs| {
93 let mut clks = CLOCKS.borrow_ref_mut(cs);
94 assert!(clks.is_none(), "Clock setup race!");
95 *clks = Some(clocks);
96 });
97
98 Ok(())
99}
100
101/// Obtain the full clocks structure, calling the given closure in a critical section.
102///
103/// The given closure will be called with read-only access to the state of the system
104/// clocks. This can be used to query and return the state of a given clock.
105///
106/// As the caller's closure will be called in a critical section, care must be taken
107/// not to block or cause any other undue delays while accessing.
108///
109/// Calls to this function will not succeed until after a successful call to `init()`,
110/// and will always return None.
111pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
112 critical_section::with(|cs| {
113 let c = CLOCKS.borrow_ref(cs);
114 let c = c.as_ref()?;
115 Some(f(c))
116 })
117}
118
119//
120// Structs/Enums
121//
122
123/// The `Clocks` structure contains the initialized state of the core system clocks
124///
125/// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function
126/// at boot time.
127#[derive(Default, Debug, Clone)]
128#[non_exhaustive]
129pub struct Clocks {
130 /// The `clk_in` is a clock provided by an external oscillator
131 pub clk_in: Option<Clock>,
132
133 // FRO180M stuff
134 //
135 /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator
136 ///
137 /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`,
138 /// and `fro_hf_div`.
139 pub fro_hf_root: Option<Clock>,
140
141 /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate.
142 pub fro_hf: Option<Clock>,
143
144 /// `clk_45` is a 45MHz clock, sourced from `fro_hf`.
145 pub clk_45m: Option<Clock>,
146
147 /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`.
148 pub fro_hf_div: Option<Clock>,
149
150 //
151 // End FRO180M
152
153 // FRO12M stuff
154 //
155 /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator
156 ///
157 /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`,
158 /// `and `fro_lf_div`.
159 pub fro_12m_root: Option<Clock>,
160
161 /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate.
162 pub fro_12m: Option<Clock>,
163
164 /// `clk_1m` is a 1MHz clock, sourced from `fro_12m`
165 pub clk_1m: Option<Clock>,
166
167 /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m`
168 pub fro_lf_div: Option<Clock>,
169 //
170 // End FRO12M stuff
171 /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator.
172 ///
173 /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in
174 /// the system domain, such as the CMP and RTC.
175 pub clk_16k_vsys: Option<Clock>,
176
177 /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator.
178 ///
179 /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in
180 /// the VDD Core domain, such as the OSTimer or LPUarts.
181 pub clk_16k_vdd_core: Option<Clock>,
182
183 /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some
184 /// peripherals.
185 pub main_clk: Option<Clock>,
186
187 /// `pll1_clk` is the output of the main system PLL, `pll1`.
188 pub pll1_clk: Option<Clock>,
189}
190
191/// `ClockError` is the main error returned when configuring or checking clock state
192#[derive(Debug, Copy, Clone, Eq, PartialEq)]
193#[cfg_attr(feature = "defmt", derive(defmt::Format))]
194#[non_exhaustive]
195pub enum ClockError {
196 /// The system clocks were never initialized by calling [`init()`]
197 NeverInitialized,
198 /// The [`init()`] function was called more than once
199 AlreadyInitialized,
200 /// The requested configuration was not possible to fulfill, as the system clocks
201 /// were not configured in a compatible way
202 BadConfig { clock: &'static str, reason: &'static str },
203 /// The requested configuration was not possible to fulfill, as the required system
204 /// clocks have not yet been implemented.
205 NotImplemented { clock: &'static str },
206 /// The requested peripheral could not be configured, as the steps necessary to
207 /// enable it have not yet been implemented.
208 UnimplementedConfig,
209}
210
211/// Information regarding a system clock
212#[derive(Debug, Clone)]
213pub struct Clock {
214 /// The frequency, in Hz, of the given clock
215 pub frequency: u32,
216 /// The power state of the clock, e.g. whether it is active in deep sleep mode
217 /// or not.
218 pub power: PoweredClock,
219}
220
221/// The power state of a given clock.
222///
223/// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep
224/// mode will be stopped. This means that any downstream usage, e.g. by peripherals,
225/// will also stop.
226///
227/// In the future, we will provide an API for entering Deep Sleep, and if there are
228/// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into
229/// Deep Sleep will be prevented, in order to avoid misbehaving peripherals.
230#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231pub enum PoweredClock {
232 /// The given clock will NOT continue running in Deep Sleep mode
233 NormalEnabledDeepSleepDisabled,
234 /// The given clock WILL continue running in Deep Sleep mode
235 AlwaysEnabled,
236}
237
238/// The ClockOperator is a private helper type that contains the methods used
239/// during system clock initialization.
240///
241/// # SAFETY
242///
243/// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`,
244/// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function.
245struct ClockOperator<'a> {
246 /// A mutable reference to the current state of system clocks
247 clocks: &'a mut Clocks,
248 /// A reference to the requested configuration provided by the caller of [`init()`]
249 config: &'a ClocksConfig,
250
251 // We hold on to stolen peripherals
252 _mrcc0: pac::Mrcc0,
253 scg0: pac::Scg0,
254 syscon: pac::Syscon,
255 vbat0: pac::Vbat0,
256}
257
12/// Trait describing an AHB clock gate that can be toggled through MRCC. 258/// Trait describing an AHB clock gate that can be toggled through MRCC.
13pub trait Gate { 259pub trait Gate {
14 type MrccPeriphConfig: SPConfHelper; 260 type MrccPeriphConfig: SPConfHelper;
15 261
16 /// Enable the clock gate. 262 /// Enable the clock gate.
263 ///
264 /// # SAFETY
265 ///
266 /// The current peripheral must be disabled prior to calling this method
17 unsafe fn enable_clock(); 267 unsafe fn enable_clock();
18 268
19 /// Disable the clock gate. 269 /// Disable the clock gate.
270 ///
271 /// # SAFETY
272 ///
273 /// There must be no active user of this peripheral when calling this method
20 unsafe fn disable_clock(); 274 unsafe fn disable_clock();
21 275
22 /// Drive the peripheral into reset. 276 /// Drive the peripheral into reset.
277 ///
278 /// # SAFETY
279 ///
280 /// There must be no active user of this peripheral when calling this method
23 unsafe fn assert_reset(); 281 unsafe fn assert_reset();
24 282
25 /// Drive the peripheral out of reset. 283 /// Drive the peripheral out of reset.
284 ///
285 /// # SAFETY
286 ///
287 /// There must be no active user of this peripheral when calling this method
26 unsafe fn release_reset(); 288 unsafe fn release_reset();
27 289
28 /// Return whether the clock gate is currently enabled. 290 /// Return whether the clock gate for this peripheral is currently enabled.
29 fn is_clock_enabled() -> bool; 291 fn is_clock_enabled() -> bool;
30 292
31 /// . 293 /// Return whether the peripheral is currently held in reset.
32 fn is_reset_released() -> bool; 294 fn is_reset_released() -> bool;
33} 295}
34 296
297/// This is the primary helper method HAL drivers are expected to call when creating
298/// an instance of the peripheral.
299///
300/// This method:
301///
302/// 1. Enables the MRCC clock gate for this peripheral
303/// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error
304/// and re-disabling the peripheral if this fails.
305/// 3. Pulses the MRCC reset line, to reset the peripheral to the default state
306/// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account
307/// the selected upstream clock, as well as any division specified by `cfg`.
308///
309/// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method
310/// may return `Ok(0)`. In the future, this might be updated to return the correct
311/// "ambient" clock, e.g. the AHB/APB frequency.
312///
313/// # SAFETY
314///
315/// This peripheral must not yet be in use prior to calling `enable_and_reset`.
35#[inline] 316#[inline]
36pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { 317pub(crate) unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
37 let freq = enable::<G>(cfg)?; 318 let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?;
38 pulse_reset::<G>(); 319 pulse_reset::<G>();
39 Ok(freq) 320 Ok(freq)
40} 321}
41 322
42/// Enable a clock gate for the given peripheral set. 323/// Enable the clock gate for the given peripheral.
324///
325/// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need
326/// to control the duration of the pulse more directly.
327///
328/// # SAFETY
329///
330/// This peripheral must not yet be in use prior to calling `enable`.
43#[inline] 331#[inline]
44pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> { 332pub(crate) unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
45 G::enable_clock(); 333 G::enable_clock();
46 while !G::is_clock_enabled() {} 334 while !G::is_clock_enabled() {}
47 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags)); 335 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
@@ -57,239 +345,184 @@ pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockErr
57 }) 345 })
58} 346}
59 347
60pub unsafe fn disable<G: Gate>() { 348/// Disable the clock gate for the given peripheral.
349///
350/// # SAFETY
351///
352/// This peripheral must no longer be in use prior to calling `enable`.
353#[allow(dead_code)]
354#[inline]
355pub(crate) unsafe fn disable<G: Gate>() {
61 G::disable_clock(); 356 G::disable_clock();
62} 357}
63 358
64/// Check whether a gate is currently enabled. 359/// Check whether a gate is currently enabled.
360#[allow(dead_code)]
65#[inline] 361#[inline]
66pub fn is_clock_enabled<G: Gate>() -> bool { 362pub(crate) fn is_clock_enabled<G: Gate>() -> bool {
67 G::is_clock_enabled() 363 G::is_clock_enabled()
68} 364}
69 365
70/// Release a reset line for the given peripheral set. 366/// Release a reset line for the given peripheral set.
367///
368/// Prefer [`enable_and_reset`].
369///
370/// # SAFETY
371///
372/// This peripheral must not yet be in use prior to calling `release_reset`.
71#[inline] 373#[inline]
72pub unsafe fn release_reset<G: Gate>() { 374pub(crate) unsafe fn release_reset<G: Gate>() {
73 G::release_reset(); 375 G::release_reset();
74} 376}
75 377
76/// Assert a reset line for the given peripheral set. 378/// Assert a reset line for the given peripheral set.
379///
380/// Prefer [`enable_and_reset`].
381///
382/// # SAFETY
383///
384/// This peripheral must not yet be in use prior to calling `assert_reset`.
77#[inline] 385#[inline]
78pub unsafe fn assert_reset<G: Gate>() { 386pub(crate) unsafe fn assert_reset<G: Gate>() {
79 G::assert_reset(); 387 G::assert_reset();
80} 388}
81 389
390/// Check whether the peripheral is held in reset.
82#[inline] 391#[inline]
83pub unsafe fn is_reset_released<G: Gate>() -> bool { 392pub(crate) unsafe fn is_reset_released<G: Gate>() -> bool {
84 G::is_reset_released() 393 G::is_reset_released()
85} 394}
86 395
87/// Pulse a reset line (assert then release) with a short delay. 396/// Pulse a reset line (assert then release) with a short delay.
397///
398/// Prefer [`enable_and_reset`].
399///
400/// # SAFETY
401///
402/// This peripheral must not yet be in use prior to calling `release_reset`.
88#[inline] 403#[inline]
89pub unsafe fn pulse_reset<G: Gate>() { 404pub(crate) unsafe fn pulse_reset<G: Gate>() {
90 G::assert_reset(); 405 G::assert_reset();
91 cortex_m::asm::nop(); 406 cortex_m::asm::nop();
92 cortex_m::asm::nop(); 407 cortex_m::asm::nop();
93 G::release_reset(); 408 G::release_reset();
94} 409}
95 410
96macro_rules! impl_cc_gate { 411//
97 ($name:ident, $reg:ident, $field:ident, $config:ty) => { 412// `impl`s for structs/enums
98 impl Gate for crate::peripherals::$name { 413//
99 type MrccPeriphConfig = $config;
100
101 #[inline]
102 unsafe fn enable_clock() {
103 let mrcc = unsafe { pac::Mrcc0::steal() };
104 mrcc.$reg().modify(|_, w| w.$field().enabled());
105 }
106
107 #[inline]
108 unsafe fn disable_clock() {
109 let mrcc = unsafe { pac::Mrcc0::steal() };
110 mrcc.$reg().modify(|_r, w| w.$field().disabled());
111 }
112
113 #[inline]
114 fn is_clock_enabled() -> bool {
115 let mrcc = unsafe { pac::Mrcc0::steal() };
116 mrcc.$reg().read().$field().is_enabled()
117 }
118
119 #[inline]
120 unsafe fn release_reset() {
121 let mrcc = unsafe { pac::Mrcc0::steal() };
122 mrcc.$reg().modify(|_, w| w.$field().enabled());
123 }
124 414
125 #[inline] 415/// The [`Clocks`] type's methods generally take the form of "ensure X clock is active".
126 unsafe fn assert_reset() { 416///
127 let mrcc = unsafe { pac::Mrcc0::steal() }; 417/// These methods are intended to be used by HAL peripheral implementors to ensure that their
128 mrcc.$reg().modify(|_, w| w.$field().disabled()); 418/// selected clocks are active at a suitable level at time of construction. These methods
129 } 419/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].
130 420impl Clocks {
131 #[inline] 421 /// Ensure the `fro_lf_div` clock is active and valid at the given power state.
132 fn is_reset_released() -> bool { 422 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
133 let mrcc = unsafe { pac::Mrcc0::steal() }; 423 let Some(clk) = self.fro_lf_div.as_ref() else {
134 mrcc.$reg().read().$field().is_enabled() 424 return Err(ClockError::BadConfig {
135 } 425 clock: "fro_lf_div",
426 reason: "required but not active",
427 });
428 };
429 if !clk.power.meets_requirement_of(at_level) {
430 return Err(ClockError::BadConfig {
431 clock: "fro_lf_div",
432 reason: "not low power active",
433 });
136 } 434 }
137 }; 435 Ok(clk.frequency)
138}
139
140pub struct UnimplementedConfig;
141impl SPConfHelper for UnimplementedConfig {
142 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
143 Err(ClockError::UnimplementedConfig)
144 } 436 }
145}
146 437
147pub struct NoConfig; 438 /// Ensure the `fro_hf` clock is active and valid at the given power state.
148impl SPConfHelper for NoConfig { 439 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
149 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> { 440 let Some(clk) = self.fro_hf.as_ref() else {
150 Ok(0) 441 return Err(ClockError::BadConfig {
442 clock: "fro_hf",
443 reason: "required but not active",
444 });
445 };
446 if !clk.power.meets_requirement_of(at_level) {
447 return Err(ClockError::BadConfig {
448 clock: "fro_hf",
449 reason: "not low power active",
450 });
451 }
452 Ok(clk.frequency)
151 } 453 }
152}
153 454
154pub mod gate { 455 /// Ensure the `fro_hf_div` clock is active and valid at the given power state.
155 use super::periph_helpers::{AdcConfig, LpuartConfig, OsTimerConfig}; 456 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
156 use super::*; 457 let Some(clk) = self.fro_hf_div.as_ref() else {
458 return Err(ClockError::BadConfig {
459 clock: "fro_hf_div",
460 reason: "required but not active",
461 });
462 };
463 if !clk.power.meets_requirement_of(at_level) {
464 return Err(ClockError::BadConfig {
465 clock: "fro_hf_div",
466 reason: "not low power active",
467 });
468 }
469 Ok(clk.frequency)
470 }
157 471
158 // These peripherals have no additional upstream clocks or configuration required 472 /// Ensure the `clk_in` clock is active and valid at the given power state.
159 // other than enabling through the MRCC gate. 473 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
160 impl_cc_gate!(PORT1, mrcc_glb_cc1, port1, NoConfig); 474 Err(ClockError::NotImplemented { clock: "clk_in" })
161 impl_cc_gate!(PORT2, mrcc_glb_cc1, port2, NoConfig); 475 }
162 impl_cc_gate!(PORT3, mrcc_glb_cc1, port3, NoConfig);
163 impl_cc_gate!(GPIO3, mrcc_glb_cc2, gpio3, NoConfig);
164 476
165 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, ostimer0, OsTimerConfig); 477 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
166 impl_cc_gate!(LPUART2, mrcc_glb_cc0, lpuart2, LpuartConfig); 478 pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
167 impl_cc_gate!(ADC1, mrcc_glb_cc1, adc1, AdcConfig); 479 // NOTE: clk_16k is always active in low power mode
168} 480 Ok(self
481 .clk_16k_vsys
482 .as_ref()
483 .ok_or(ClockError::BadConfig {
484 clock: "clk_16k_vsys",
485 reason: "required but not active",
486 })?
487 .frequency)
488 }
169 489
170// /// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART. 490 /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state.
171// pub unsafe fn enable_uart2_port2(peripherals: &pac::Peripherals) { 491 pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
172// enable::<gate::Port2>(peripherals); 492 // NOTE: clk_16k is always active in low power mode
173// enable::<gate::Lpuart2>(peripherals); 493 Ok(self
174// } 494 .clk_16k_vdd_core
175 495 .as_ref()
176// /// Convenience helper enabling the PORT3 and GPIO3 gates used by the LED in the examples. 496 .ok_or(ClockError::BadConfig {
177// pub unsafe fn enable_led_port(peripherals: &pac::Peripherals) { 497 clock: "clk_16k_vdd_core",
178// enable::<gate::Port3>(peripherals); 498 reason: "required but not active",
179// enable::<gate::Gpio3>(peripherals); 499 })?
180// } 500 .frequency)
181
182// /// Convenience helper enabling the OSTIMER0 clock gate.
183// pub unsafe fn enable_ostimer0(peripherals: &pac::Peripherals) {
184// enable::<gate::Ostimer0>(peripherals);
185// }
186
187// pub unsafe fn select_uart2_clock(peripherals: &pac::Peripherals) {
188// // Use FRO_LF_DIV (already running) MUX=0 DIV=0
189// let mrcc = &peripherals.mrcc0;
190// mrcc.mrcc_lpuart2_clksel().write(|w| w.mux().clkroot_func_0());
191// mrcc.mrcc_lpuart2_clkdiv().write(|w| unsafe { w.bits(0) });
192// }
193
194// pub unsafe fn ensure_frolf_running(peripherals: &pac::Peripherals) {
195// // Ensure FRO_LF divider clock is running (reset default HALT=1 stops it)
196// let sys = &peripherals.syscon;
197// sys.frolfdiv().modify(|_, w| {
198// // DIV defaults to 0; keep it explicit and clear HALT
199// unsafe { w.div().bits(0) }.halt().run()
200// });
201// }
202
203// /// Compute the FRO_LF_DIV output frequency currently selected for LPUART2.
204// /// Assumes select_uart2_clock() has chosen MUX=0 (FRO_LF_DIV) and DIV is set in SYSCON.FRO_LF_DIV.
205// pub unsafe fn uart2_src_hz(peripherals: &pac::Peripherals) -> u32 {
206// // SYSCON.FRO_LF_DIV: DIV field is simple divider: freq_out = 12_000_000 / (DIV+1) for many NXP parts.
207// // On MCXA276 FRO_LF base is 12 MHz; our init keeps DIV=0, so result=12_000_000.
208// // Read it anyway for future generality.
209// let div = peripherals.syscon.frolfdiv().read().div().bits() as u32;
210// let base = 12_000_000u32;
211// base / (div + 1)
212// }
213
214// /// Enable clock gate and release reset for OSTIMER0.
215// /// Select OSTIMER0 clock source = 1 MHz root (working bring-up configuration).
216// pub unsafe fn select_ostimer0_clock_1m(peripherals: &pac::Peripherals) {
217// let mrcc = &peripherals.mrcc0;
218// mrcc.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
219// }
220
221// pub unsafe fn enable_adc(peripherals: &pac::Peripherals) {
222// enable::<gate::Port1>(peripherals);
223// enable::<gate::Adc1>(peripherals);
224// }
225
226// pub unsafe fn select_adc_clock(peripherals: &pac::Peripherals) {
227// // Use FRO_LF_DIV (already running) MUX=0 DIV=0
228// let mrcc = &peripherals.mrcc0;
229// mrcc.mrcc_adc_clksel().write(|w| w.mux().clkroot_func_0());
230// mrcc.mrcc_adc_clkdiv().write(|w| unsafe { w.bits(0) });
231// }
232
233// ==============================================
234
235/// This type represents a divider in the range 1..=256.
236///
237/// At a hardware level, this is an 8-bit register from 0..=255,
238/// which adds one.
239#[derive(Copy, Clone, Debug, PartialEq, Eq)]
240pub struct Div8(pub(super) u8);
241
242impl Div8 {
243 /// Store a "raw" divisor value that will divide the source by
244 /// `(n + 1)`, e.g. `Div8::from_raw(0)` will divide the source
245 /// by 1, and `Div8::from_raw(255)` will divide the source by
246 /// 256.
247 pub const fn from_raw(n: u8) -> Self {
248 Self(n)
249 } 501 }
250 502
251 /// Store a specific divisor value that will divide the source 503 /// Ensure the `clk_1m` clock is active and valid at the given power state.
252 /// by `n`. e.g. `Div8::from_divisor(1)` will divide the source 504 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
253 /// by 1, and `Div8::from_divisor(256)` will divide the source 505 let Some(clk) = self.clk_1m.as_ref() else {
254 /// by 256. 506 return Err(ClockError::BadConfig {
255 /// 507 clock: "clk_1m",
256 /// Will return `None` if `n` is not in the range `1..=256`. 508 reason: "required but not active",
257 /// Consider [`Self::from_raw`] for an infallible version. 509 });
258 pub const fn from_divisor(n: u16) -> Option<Self> {
259 let Some(n) = n.checked_sub(1) else {
260 return None;
261 }; 510 };
262 if n > (u8::MAX as u16) { 511 if !clk.power.meets_requirement_of(at_level) {
263 return None; 512 return Err(ClockError::BadConfig {
513 clock: "clk_1m",
514 reason: "not low power active",
515 });
264 } 516 }
265 Some(Self(n as u8)) 517 Ok(clk.frequency)
266 }
267
268 /// Convert into "raw" bits form
269 #[inline(always)]
270 pub const fn into_bits(self) -> u8 {
271 self.0
272 } 518 }
273 519
274 /// Convert into "divisor" form, as a u32 for convenient frequency math 520 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.
275 #[inline(always)] 521 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
276 pub const fn into_divisor(self) -> u32 { 522 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
277 self.0 as u32 + 1
278 } 523 }
279} 524}
280 525
281#[derive(Debug, Clone)]
282pub struct Clock {
283 pub frequency: u32,
284 pub power: PoweredClock,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
288pub enum PoweredClock {
289 NormalEnabledDeepSleepDisabled,
290 AlwaysEnabled,
291}
292
293impl PoweredClock { 526impl PoweredClock {
294 /// Does THIS clock meet the power requirements of the OTHER clock? 527 /// Does THIS clock meet the power requirements of the OTHER clock?
295 pub fn meets_requirement_of(&self, other: &Self) -> bool { 528 pub fn meets_requirement_of(&self, other: &Self) -> bool {
@@ -302,172 +535,11 @@ impl PoweredClock {
302 } 535 }
303} 536}
304 537
305/// ```text
306/// ┌─────────────────────────────────────────────────────────┐
307/// │ │
308/// │ ┌───────────┐ clk_out ┌─────────┐ │
309/// XTAL ──────┼──▷│ System │───────────▷│ │ clk_in │
310/// │ │ OSC │ clkout_byp │ MUX │──────────────────┼──────▷
311/// EXTAL ──────┼──▷│ │───────────▷│ │ │
312/// │ └───────────┘ └─────────┘ │
313/// │ │
314/// │ ┌───────────┐ fro_hf_root ┌────┐ fro_hf │
315/// │ │ FRO180 ├───────┬─────▷│ CG │─────────────────────┼──────▷
316/// │ │ │ │ ├────┤ clk_45m │
317/// │ │ │ └─────▷│ CG │─────────────────────┼──────▷
318/// │ └───────────┘ └────┘ │
319/// │ ┌───────────┐ fro_12m_root ┌────┐ fro_12m │
320/// │ │ FRO12M │────────┬─────▷│ CG │────────────────────┼──────▷
321/// │ │ │ │ ├────┤ clk_1m │
322/// │ │ │ └─────▷│1/12│────────────────────┼──────▷
323/// │ └───────────┘ └────┘ │
324/// │ │
325/// │ ┌──────────┐ │
326/// │ │000 │ │
327/// │ clk_in │ │ │
328/// │ ───────────────▷│001 │ │
329/// │ fro_12m │ │ │
330/// │ ───────────────▷│010 │ │
331/// │ fro_hf_root │ │ │
332/// │ ───────────────▷│011 │ main_clk │
333/// │ │ │───────────────────────────┼──────▷
334/// clk_16k ──────┼─────────────────▷│100 │ │
335/// │ none │ │ │
336/// │ ───────────────▷│101 │ │
337/// │ pll1_clk │ │ │
338/// │ ───────────────▷│110 │ │
339/// │ none │ │ │
340/// │ ───────────────▷│111 │ │
341/// │ └──────────┘ │
342/// │ ▲ │
343/// │ │ │
344/// │ SCG SCS │
345/// │ SCG-Lite │
346/// └─────────────────────────────────────────────────────────┘
347///
348///
349/// clk_in ┌─────┐
350/// ───────────────▷│00 │
351/// clk_45m │ │
352/// ───────────────▷│01 │ ┌───────────┐ pll1_clk
353/// none │ │─────▷│ SPLL │───────────────▷
354/// ───────────────▷│10 │ └───────────┘
355/// fro_12m │ │
356/// ───────────────▷│11 │
357/// └─────┘
358/// ```
359#[non_exhaustive]
360pub struct ClocksConfig {
361 // FIRC, FRO180, 45/60/90/180M clock source
362 pub firc: Option<FircConfig>,
363 // NOTE: I don't think we *can* disable the SIRC?
364 pub sirc: SircConfig,
365 pub fro16k: Option<Fro16KConfig>,
366}
367
368// FIRC/FRO180M
369
370/// ```text
371/// ┌───────────┐ fro_hf_root ┌────┐ fro_hf
372/// │ FRO180M ├───────┬─────▷│GATE│──────────▷
373/// │ │ │ ├────┤ clk_45m
374/// │ │ └─────▷│GATE│──────────▷
375/// └───────────┘ └────┘
376/// ```
377#[non_exhaustive]
378pub struct FircConfig {
379 pub frequency: FircFreqSel,
380 pub power: PoweredClock,
381 /// Is the "fro_hf" gated clock enabled?
382 pub fro_hf_enabled: bool,
383 /// Is the "clk_45m" gated clock enabled?
384 pub clk_45m_enabled: bool,
385 /// Is the "fro_hf_div" clock enabled? Requires `fro_hf`!
386 pub fro_hf_div: Option<Div8>,
387}
388
389pub enum FircFreqSel {
390 Mhz45,
391 Mhz60,
392 Mhz90,
393 Mhz180,
394}
395
396// SIRC/FRO12M
397
398/// ```text
399/// ┌───────────┐ fro_12m_root ┌────┐ fro_12m
400/// │ FRO12M │────────┬─────▷│ CG │──────────▷
401/// │ │ │ ├────┤ clk_1m
402/// │ │ └─────▷│1/12│──────────▷
403/// └───────────┘ └────┘
404/// ```
405#[non_exhaustive]
406pub struct SircConfig {
407 pub power: PoweredClock,
408 // peripheral output, aka sirc_12mhz
409 pub fro_12m_enabled: bool,
410 /// Is the "fro_lf_div" clock enabled? Requires `fro_12m`!
411 pub fro_lf_div: Option<Div8>,
412}
413
414#[derive(Default, Debug, Clone)]
415#[non_exhaustive]
416pub struct Clocks {
417 pub clk_in: Option<Clock>,
418
419 // FRO180M stuff
420 //
421 pub fro_hf_root: Option<Clock>,
422 pub fro_hf: Option<Clock>,
423 pub clk_45m: Option<Clock>,
424 pub fro_hf_div: Option<Clock>,
425 //
426 // End FRO180M
427
428 // FRO12M stuff
429 pub fro_12m_root: Option<Clock>,
430 pub fro_12m: Option<Clock>,
431 pub clk_1m: Option<Clock>,
432 pub fro_lf_div: Option<Clock>,
433 //
434 // End FRO12M stuff
435 pub clk_16k_vsys: Option<Clock>,
436 pub clk_16k_vdd_core: Option<Clock>,
437 pub main_clk: Option<Clock>,
438 pub pll1_clk: Option<Clock>,
439}
440
441#[non_exhaustive]
442pub struct Fro16KConfig {
443 pub vsys_domain_active: bool,
444 pub vdd_core_domain_active: bool,
445}
446
447static CLOCKS: critical_section::Mutex<RefCell<Option<Clocks>>> = critical_section::Mutex::new(RefCell::new(None));
448
449#[derive(Debug, Copy, Clone, Eq, PartialEq)]
450#[cfg_attr(feature = "defmt", derive(defmt::Format))]
451#[non_exhaustive]
452pub enum ClockError {
453 NeverInitialized,
454 AlreadyInitialized,
455 BadConfig { clock: &'static str, reason: &'static str },
456 NotImplemented { clock: &'static str },
457 UnimplementedConfig,
458}
459
460struct ClockOperator<'a> {
461 clocks: &'a mut Clocks,
462 config: &'a ClocksConfig,
463
464 _mrcc0: pac::Mrcc0,
465 scg0: pac::Scg0,
466 syscon: pac::Syscon,
467 vbat0: pac::Vbat0,
468}
469
470impl ClockOperator<'_> { 538impl ClockOperator<'_> {
539 /// Configure the FIRC/FRO180M clock family
540 ///
541 /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used
542 /// as the main clock used for the CPU, AHB, APB, etc.
471 fn configure_firc_clocks(&mut self) -> Result<(), ClockError> { 543 fn configure_firc_clocks(&mut self) -> Result<(), ClockError> {
472 const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig { 544 const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig {
473 clock: "firc", 545 clock: "firc",
@@ -484,7 +556,7 @@ impl ClockOperator<'_> {
484 } 556 }
485 let base_freq = 45_000_000; 557 let base_freq = 45_000_000;
486 558
487 // Is the FIRC as expected? 559 // Now, check if the FIRC as expected for our hardcoded value
488 let mut firc_ok = true; 560 let mut firc_ok = true;
489 561
490 // Is the hardware currently set to the default 45MHz? 562 // Is the hardware currently set to the default 45MHz?
@@ -604,6 +676,7 @@ impl ClockOperator<'_> {
604 Ok(()) 676 Ok(())
605 } 677 }
606 678
679 /// Configure the SIRC/FRO12M clock family
607 fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> { 680 fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> {
608 let SircConfig { 681 let SircConfig {
609 power, 682 power,
@@ -694,6 +767,7 @@ impl ClockOperator<'_> {
694 Ok(()) 767 Ok(())
695 } 768 }
696 769
770 /// Configure the FRO16K/clk_16k clock family
697 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> { 771 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
698 let Some(fro16k) = self.config.fro16k.as_ref() else { 772 let Some(fro16k) = self.config.fro16k.as_ref() else {
699 return Ok(()); 773 return Ok(());
@@ -735,145 +809,74 @@ impl ClockOperator<'_> {
735 } 809 }
736} 810}
737 811
738pub fn init(settings: ClocksConfig) -> Result<(), ClockError> { 812//
739 critical_section::with(|cs| { 813// Macros/macro impls
740 if CLOCKS.borrow_ref(cs).is_some() { 814//
741 Err(ClockError::AlreadyInitialized)
742 } else {
743 Ok(())
744 }
745 })?;
746
747 let mut clocks = Clocks::default();
748 let mut operator = ClockOperator {
749 clocks: &mut clocks,
750 config: &settings,
751
752 _mrcc0: unsafe { pac::Mrcc0::steal() },
753 scg0: unsafe { pac::Scg0::steal() },
754 syscon: unsafe { pac::Syscon::steal() },
755 vbat0: unsafe { pac::Vbat0::steal() },
756 };
757 815
758 operator.configure_firc_clocks()?; 816/// This macro is used to implement the [`Gate`] trait for a given peripheral
759 operator.configure_sirc_clocks()?; 817/// that is controlled by the MRCC peripheral.
760 operator.configure_fro16k_clocks()?; 818macro_rules! impl_cc_gate {
761 // TODO, everything downstream 819 ($name:ident, $reg:ident, $field:ident, $config:ty) => {
820 impl Gate for crate::peripherals::$name {
821 type MrccPeriphConfig = $config;
762 822
763 critical_section::with(|cs| { 823 #[inline]
764 let mut clks = CLOCKS.borrow_ref_mut(cs); 824 unsafe fn enable_clock() {
765 assert!(clks.is_none(), "Clock setup race!"); 825 let mrcc = unsafe { pac::Mrcc0::steal() };
766 *clks = Some(clocks); 826 mrcc.$reg().modify(|_, w| w.$field().enabled());
767 }); 827 }
768 828
769 Ok(()) 829 #[inline]
770} 830 unsafe fn disable_clock() {
831 let mrcc = unsafe { pac::Mrcc0::steal() };
832 mrcc.$reg().modify(|_r, w| w.$field().disabled());
833 }
771 834
772/// Obtain the full clocks structure, calling the given closure in a critical section 835 #[inline]
773/// 836 fn is_clock_enabled() -> bool {
774/// NOTE: Clocks implements `Clone`, 837 let mrcc = unsafe { pac::Mrcc0::steal() };
775pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> { 838 mrcc.$reg().read().$field().is_enabled()
776 critical_section::with(|cs| { 839 }
777 let c = CLOCKS.borrow_ref(cs);
778 let c = c.as_ref()?;
779 Some(f(c))
780 })
781}
782 840
783impl Clocks { 841 #[inline]
784 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 842 unsafe fn release_reset() {
785 let Some(clk) = self.fro_lf_div.as_ref() else { 843 let mrcc = unsafe { pac::Mrcc0::steal() };
786 return Err(ClockError::BadConfig { 844 mrcc.$reg().modify(|_, w| w.$field().enabled());
787 clock: "fro_lf_div", 845 }
788 reason: "required but not active",
789 });
790 };
791 if !clk.power.meets_requirement_of(at_level) {
792 return Err(ClockError::BadConfig {
793 clock: "fro_lf_div",
794 reason: "not low power active",
795 });
796 }
797 Ok(clk.frequency)
798 }
799 846
800 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 847 #[inline]
801 let Some(clk) = self.fro_hf.as_ref() else { 848 unsafe fn assert_reset() {
802 return Err(ClockError::BadConfig { 849 let mrcc = unsafe { pac::Mrcc0::steal() };
803 clock: "fro_hf", 850 mrcc.$reg().modify(|_, w| w.$field().disabled());
804 reason: "required but not active", 851 }
805 });
806 };
807 if !clk.power.meets_requirement_of(at_level) {
808 return Err(ClockError::BadConfig {
809 clock: "fro_hf",
810 reason: "not low power active",
811 });
812 }
813 Ok(clk.frequency)
814 }
815 852
816 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 853 #[inline]
817 let Some(clk) = self.fro_hf_div.as_ref() else { 854 fn is_reset_released() -> bool {
818 return Err(ClockError::BadConfig { 855 let mrcc = unsafe { pac::Mrcc0::steal() };
819 clock: "fro_hf_div", 856 mrcc.$reg().read().$field().is_enabled()
820 reason: "required but not active", 857 }
821 });
822 };
823 if !clk.power.meets_requirement_of(at_level) {
824 return Err(ClockError::BadConfig {
825 clock: "fro_hf_div",
826 reason: "not low power active",
827 });
828 } 858 }
829 Ok(clk.frequency) 859 };
830 } 860}
831
832 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
833 Err(ClockError::NotImplemented { clock: "clk_in" })
834 }
835
836 pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
837 // NOTE: clk_16k is always active in low power mode
838 Ok(self
839 .clk_16k_vsys
840 .as_ref()
841 .ok_or(ClockError::BadConfig {
842 clock: "clk_16k_vsys",
843 reason: "required but not active",
844 })?
845 .frequency)
846 }
847 861
848 pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { 862/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,
849 // NOTE: clk_16k is always active in low power mode 863/// for various low level peripherals.
850 Ok(self 864pub(crate) mod gate {
851 .clk_16k_vdd_core 865 use super::periph_helpers::{AdcConfig, LpuartConfig, NoConfig, OsTimerConfig};
852 .as_ref() 866 use super::*;
853 .ok_or(ClockError::BadConfig {
854 clock: "clk_16k_vdd_core",
855 reason: "required but not active",
856 })?
857 .frequency)
858 }
859 867
860 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 868 // These peripherals have no additional upstream clocks or configuration required
861 let Some(clk) = self.clk_1m.as_ref() else { 869 // other than enabling through the MRCC gate. Currently, these peripherals will
862 return Err(ClockError::BadConfig { 870 // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or
863 clock: "clk_1m", 871 // [`SPConfHelper::post_enable_config()`].
864 reason: "required but not active", 872 impl_cc_gate!(PORT1, mrcc_glb_cc1, port1, NoConfig);
865 }); 873 impl_cc_gate!(PORT2, mrcc_glb_cc1, port2, NoConfig);
866 }; 874 impl_cc_gate!(PORT3, mrcc_glb_cc1, port3, NoConfig);
867 if !clk.power.meets_requirement_of(at_level) { 875 impl_cc_gate!(GPIO3, mrcc_glb_cc2, gpio3, NoConfig);
868 return Err(ClockError::BadConfig {
869 clock: "clk_1m",
870 reason: "not low power active",
871 });
872 }
873 Ok(clk.frequency)
874 }
875 876
876 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { 877 // These peripherals DO have meaningful configuration, and could fail if the system
877 Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) 878 // clocks do not match their needs.
878 } 879 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, ostimer0, OsTimerConfig);
880 impl_cc_gate!(LPUART2, mrcc_glb_cc0, lpuart2, LpuartConfig);
881 impl_cc_gate!(ADC1, mrcc_glb_cc1, adc1, AdcConfig);
879} 882}
diff --git a/src/clocks/periph_helpers.rs b/src/clocks/periph_helpers.rs
index 1657bd7eb..e5b234c5b 100644
--- a/src/clocks/periph_helpers.rs
+++ b/src/clocks/periph_helpers.rs
@@ -1,7 +1,43 @@
1//! Peripheral Helpers
2//!
3//! The purpose of this module is to define the per-peripheral special handling
4//! required from a clocking perspective. Different peripherals have different
5//! selectable source clocks, and some peripherals have additional pre-dividers
6//! that can be used.
7//!
8//! See the docs of [`SPConfHelper`] for more details.
9
1use super::{ClockError, Clocks, PoweredClock}; 10use super::{ClockError, Clocks, PoweredClock};
2use crate::pac; 11use crate::pac;
3 12
13/// Sealed Peripheral Configuration Helper
14///
15/// NOTE: the name "sealed" doesn't *totally* make sense because its not sealed yet in the
16/// embassy-mcxa project, but it derives from embassy-imxrt where it is. We should
17/// fix the name, or actually do the sealing of peripherals.
18///
19/// This trait serves to act as a per-peripheral customization for clocking behavior.
20///
21/// This trait should be implemented on a configuration type for a given peripheral, and
22/// provide the methods that will be called by the higher level operations like
23/// `embassy_mcxa::clocks::enable_and_reset()`.
4pub trait SPConfHelper { 24pub trait SPConfHelper {
25 /// This method is called AFTER a given MRCC peripheral has been enabled (e.g. un-gated),
26 /// but BEFORE the peripheral reset line is reset.
27 ///
28 /// This function should check that any relevant upstream clocks are enabled, are in a
29 /// reasonable power state, and that the requested configuration can be made. If any of
30 /// these checks fail, an `Err(ClockError)` should be returned, likely `ClockError::BadConfig`.
31 ///
32 /// This function SHOULD NOT make any changes to the system clock configuration, even
33 /// unsafely, as this should remain static for the duration of the program.
34 ///
35 /// This function WILL be called in a critical section, care should be taken not to delay
36 /// for an unreasonable amount of time.
37 ///
38 /// On success, this function MUST return an `Ok(freq)`, where `freq` is the frequency
39 /// fed into the peripheral, taking into account the selected source clock, as well as
40 /// any pre-divisors.
5 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>; 41 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>;
6} 42}
7 43
@@ -65,6 +101,33 @@ impl Div4 {
65 } 101 }
66} 102}
67 103
104/// A basic type that always returns an error when `post_enable_config` is called.
105///
106/// Should only be used as a placeholder.
107pub struct UnimplementedConfig;
108
109impl SPConfHelper for UnimplementedConfig {
110 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
111 Err(ClockError::UnimplementedConfig)
112 }
113}
114
115/// A basic type that always returns `Ok(0)` when `post_enable_config` is called.
116///
117/// This should only be used for peripherals that are "ambiently" clocked, like `PORTn`
118/// peripherals, which have no selectable/configurable source clock.
119pub struct NoConfig;
120impl SPConfHelper for NoConfig {
121 fn post_enable_config(&self, _clocks: &Clocks) -> Result<u32, ClockError> {
122 Ok(0)
123 }
124}
125
126//
127// LPUart
128//
129
130/// Selectable clocks for Lpuart peripherals
68#[derive(Debug, Clone, Copy)] 131#[derive(Debug, Clone, Copy)]
69pub enum LpuartClockSel { 132pub enum LpuartClockSel {
70 /// FRO12M/FRO_LF/SIRC clock source, passed through divider 133 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
@@ -86,16 +149,26 @@ pub enum LpuartClockSel {
86 None, 149 None,
87} 150}
88 151
152/// Which instance of the Lpuart is this?
153///
154/// Should not be directly selectable by end-users.
89#[derive(Copy, Clone, Debug, PartialEq, Eq)] 155#[derive(Copy, Clone, Debug, PartialEq, Eq)]
90pub enum LpuartInstance { 156pub enum LpuartInstance {
157 /// Instance 0
91 Lpuart0, 158 Lpuart0,
159 /// Instance 1
92 Lpuart1, 160 Lpuart1,
161 /// Instance 2
93 Lpuart2, 162 Lpuart2,
163 /// Instance 3
94 Lpuart3, 164 Lpuart3,
165 /// Instance 4
95 Lpuart4, 166 Lpuart4,
167 /// Instance 5
96 Lpuart5, 168 Lpuart5,
97} 169}
98 170
171/// Top level configuration for `Lpuart` instances.
99pub struct LpuartConfig { 172pub struct LpuartConfig {
100 /// Power state required for this peripheral 173 /// Power state required for this peripheral
101 pub power: PoweredClock, 174 pub power: PoweredClock,
@@ -108,39 +181,6 @@ pub struct LpuartConfig {
108 pub(crate) instance: LpuartInstance, 181 pub(crate) instance: LpuartInstance,
109} 182}
110 183
111#[derive(Copy, Clone, Debug, PartialEq, Eq)]
112pub enum OstimerClockSel {
113 /// 16k clock, sourced from FRO16K (Vdd Core)
114 Clk16kVddCore,
115 /// 1 MHz Clock sourced from FRO12M
116 Clk1M,
117 /// Disabled
118 None,
119}
120
121pub struct OsTimerConfig {
122 pub power: PoweredClock,
123 pub source: OstimerClockSel,
124}
125
126#[derive(Copy, Clone, Debug, PartialEq, Eq)]
127pub enum AdcClockSel {
128 FroLfDiv,
129 FroHf,
130 ClkIn,
131 Clk1M,
132 Pll1ClkDiv,
133 None,
134}
135
136pub struct AdcConfig {
137 pub power: PoweredClock,
138 pub source: AdcClockSel,
139 pub div: Div4,
140}
141
142// impls
143
144impl SPConfHelper for LpuartConfig { 184impl SPConfHelper for LpuartConfig {
145 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { 185 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
146 // check that source is suitable 186 // check that source is suitable
@@ -215,6 +255,29 @@ impl SPConfHelper for LpuartConfig {
215 } 255 }
216} 256}
217 257
258//
259// OSTimer
260//
261
262/// Selectable clocks for the OSTimer peripheral
263#[derive(Copy, Clone, Debug, PartialEq, Eq)]
264pub enum OstimerClockSel {
265 /// 16k clock, sourced from FRO16K (Vdd Core)
266 Clk16kVddCore,
267 /// 1 MHz Clock sourced from FRO12M
268 Clk1M,
269 /// Disabled
270 None,
271}
272
273/// Top level configuration for the `OSTimer` peripheral
274pub struct OsTimerConfig {
275 /// Power state required for this peripheral
276 pub power: PoweredClock,
277 /// Selected clock source for this peripheral
278 pub source: OstimerClockSel,
279}
280
218impl SPConfHelper for OsTimerConfig { 281impl SPConfHelper for OsTimerConfig {
219 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { 282 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
220 let mrcc0 = unsafe { pac::Mrcc0::steal() }; 283 let mrcc0 = unsafe { pac::Mrcc0::steal() };
@@ -237,6 +300,37 @@ impl SPConfHelper for OsTimerConfig {
237 } 300 }
238} 301}
239 302
303//
304// Adc
305//
306
307/// Selectable clocks for the ADC peripheral
308#[derive(Copy, Clone, Debug, PartialEq, Eq)]
309pub enum AdcClockSel {
310 /// Divided `fro_lf`/`clk_12m`/FRO12M source
311 FroLfDiv,
312 /// Gated `fro_hf`/`FRO180M` source
313 FroHf,
314 /// External Clock Source
315 ClkIn,
316 /// 1MHz clock sourced by a divided `fro_lf`/`clk_12m`
317 Clk1M,
318 /// Internal PLL output, with configurable divisor
319 Pll1ClkDiv,
320 /// No clock/disabled
321 None,
322}
323
324/// Top level configuration for the ADC peripheral
325pub struct AdcConfig {
326 /// Power state required for this peripheral
327 pub power: PoweredClock,
328 /// Selected clock-source for this peripheral
329 pub source: AdcClockSel,
330 /// Pre-divisor, applied to the upstream clock output
331 pub div: Div4,
332}
333
240impl SPConfHelper for AdcConfig { 334impl SPConfHelper for AdcConfig {
241 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> { 335 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
242 use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux; 336 use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux;
diff --git a/src/config.rs b/src/config.rs
index 93aed5a99..0939c11f1 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,5 +1,6 @@
1// HAL configuration (minimal), mirroring embassy-imxrt style 1// HAL configuration (minimal), mirroring embassy-imxrt style
2 2
3use crate::clocks::config::ClocksConfig;
3use crate::interrupt::Priority; 4use crate::interrupt::Priority;
4 5
5#[non_exhaustive] 6#[non_exhaustive]
@@ -7,6 +8,7 @@ pub struct Config {
7 pub time_interrupt_priority: Priority, 8 pub time_interrupt_priority: Priority,
8 pub rtc_interrupt_priority: Priority, 9 pub rtc_interrupt_priority: Priority,
9 pub adc_interrupt_priority: Priority, 10 pub adc_interrupt_priority: Priority,
11 pub clock_cfg: ClocksConfig,
10} 12}
11 13
12impl Default for Config { 14impl Default for Config {
@@ -15,6 +17,7 @@ impl Default for Config {
15 time_interrupt_priority: Priority::from(0), 17 time_interrupt_priority: Priority::from(0),
16 rtc_interrupt_priority: Priority::from(0), 18 rtc_interrupt_priority: Priority::from(0),
17 adc_interrupt_priority: Priority::from(0), 19 adc_interrupt_priority: Priority::from(0),
20 clock_cfg: ClocksConfig::default(),
18 } 21 }
19 } 22 }
20} 23}