aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/adc.rs73
-rw-r--r--src/board.rs14
-rw-r--r--src/clocks.rs134
-rw-r--r--src/clocks/config.rs199
-rw-r--r--src/clocks/mod.rs887
-rw-r--r--src/clocks/periph_helpers.rs393
-rw-r--r--src/config.rs3
-rw-r--r--src/lib.rs10
-rw-r--r--src/lpuart/buffered.rs11
-rw-r--r--src/lpuart/mod.rs156
-rw-r--r--src/ostimer.rs73
-rw-r--r--src/reset.rs112
-rw-r--r--src/rtc.rs30
-rw-r--r--src/uart.rs316
14 files changed, 1663 insertions, 748 deletions
diff --git a/src/adc.rs b/src/adc.rs
index 655bf934f..b5ec5983f 100644
--- a/src/adc.rs
+++ b/src/adc.rs
@@ -1,6 +1,10 @@
1//! ADC driver 1//! ADC driver
2use core::sync::atomic::{AtomicBool, Ordering}; 2use core::sync::atomic::{AtomicBool, Ordering};
3 3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::clocks::periph_helpers::{AdcClockSel, AdcConfig, Div4};
7use crate::clocks::{enable_and_reset, Gate, PoweredClock};
4use crate::pac; 8use crate::pac;
5use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres}; 9use crate::pac::adc1::cfg::{HptExdi, Pwrsel, Refsel, Tcmdres, Tprictrl, Tres};
6use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts}; 10use crate::pac::adc1::cmdh1::{Avgs, Cmpen, Next, Sts};
@@ -12,7 +16,7 @@ type Regs = pac::adc1::RegisterBlock;
12 16
13static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false); 17static INTERRUPT_TRIGGERED: AtomicBool = AtomicBool::new(false);
14// Token-based instance pattern like embassy-imxrt 18// Token-based instance pattern like embassy-imxrt
15pub trait Instance { 19pub trait Instance: Gate<MrccPeriphConfig = AdcConfig> + PeripheralType {
16 fn ptr() -> *const Regs; 20 fn ptr() -> *const Regs;
17} 21}
18 22
@@ -26,12 +30,12 @@ impl Instance for crate::peripherals::ADC1 {
26} 30}
27 31
28// Also implement Instance for the Peri wrapper type 32// Also implement Instance for the Peri wrapper type
29impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> { 33// impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::ADC1> {
30 #[inline(always)] 34// #[inline(always)]
31 fn ptr() -> *const Regs { 35// fn ptr() -> *const Regs {
32 pac::Adc1::ptr() 36// pac::Adc1::ptr()
33 } 37// }
34} 38// }
35 39
36#[derive(Debug, Clone, Copy, PartialEq, Eq)] 40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(u8)] 41#[repr(u8)]
@@ -60,6 +64,29 @@ pub struct LpadcConfig {
60 pub enable_conv_pause: bool, 64 pub enable_conv_pause: bool,
61 pub conv_pause_delay: u16, 65 pub conv_pause_delay: u16,
62 pub fifo_watermark: u8, 66 pub fifo_watermark: u8,
67 pub power: PoweredClock,
68 pub source: AdcClockSel,
69 pub div: Div4,
70}
71
72impl Default for LpadcConfig {
73 fn default() -> Self {
74 LpadcConfig {
75 enable_in_doze_mode: true,
76 conversion_average_mode: CalAvgs::NoAverage,
77 enable_analog_preliminary: false,
78 power_up_delay: 0x80,
79 reference_voltage_source: Refsel::Option1,
80 power_level_mode: Pwrsel::Lowest,
81 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
82 enable_conv_pause: false,
83 conv_pause_delay: 0,
84 fifo_watermark: 0,
85 power: PoweredClock::NormalEnabledDeepSleepDisabled,
86 source: AdcClockSel::FroLfDiv,
87 div: Div4::no_div(),
88 }
89 }
63} 90}
64 91
65#[derive(Debug, Clone, Copy, PartialEq, Eq)] 92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -94,15 +121,24 @@ pub struct ConvResult {
94 pub conv_value: u16, 121 pub conv_value: u16,
95} 122}
96 123
97pub struct Adc<I: Instance> { 124pub struct Adc<'a, I: Instance> {
98 _inst: core::marker::PhantomData<I>, 125 _inst: core::marker::PhantomData<&'a mut I>,
99} 126}
100 127
101impl<I: Instance> Adc<I> { 128impl<'a, I: Instance> Adc<'a, I> {
102 /// initialize ADC 129 /// initialize ADC
103 pub fn new(_inst: impl Instance, config: LpadcConfig) -> Self { 130 pub fn new(_inst: Peri<'a, I>, config: LpadcConfig) -> Self {
104 let adc = unsafe { &*I::ptr() }; 131 let adc = unsafe { &*I::ptr() };
105 132
133 let _clock_freq = unsafe {
134 enable_and_reset::<I>(&AdcConfig {
135 power: config.power,
136 source: config.source,
137 div: config.div,
138 })
139 .expect("Adc Init should not fail")
140 };
141
106 /* Reset the module. */ 142 /* Reset the module. */
107 adc.ctrl().modify(|_, w| w.rst().held_in_reset()); 143 adc.ctrl().modify(|_, w| w.rst().held_in_reset());
108 adc.ctrl().modify(|_, w| w.rst().released_from_reset()); 144 adc.ctrl().modify(|_, w| w.rst().released_from_reset());
@@ -194,21 +230,6 @@ impl<I: Instance> Adc<I> {
194 adc.ctrl().modify(|_, w| w.adcen().disabled()); 230 adc.ctrl().modify(|_, w| w.adcen().disabled());
195 } 231 }
196 232
197 pub fn get_default_config() -> LpadcConfig {
198 LpadcConfig {
199 enable_in_doze_mode: true,
200 conversion_average_mode: CalAvgs::NoAverage,
201 enable_analog_preliminary: false,
202 power_up_delay: 0x80,
203 reference_voltage_source: Refsel::Option1,
204 power_level_mode: Pwrsel::Lowest,
205 trigger_priority_policy: TriggerPriorityPolicy::ConvPreemptImmediatelyNotAutoResumed,
206 enable_conv_pause: false,
207 conv_pause_delay: 0,
208 fifo_watermark: 0,
209 }
210 }
211
212 pub fn do_offset_calibration(&self) { 233 pub fn do_offset_calibration(&self) {
213 let adc = unsafe { &*I::ptr() }; 234 let adc = unsafe { &*I::ptr() };
214 // Enable calibration mode 235 // Enable calibration mode
diff --git a/src/board.rs b/src/board.rs
deleted file mode 100644
index fa679e82c..000000000
--- a/src/board.rs
+++ /dev/null
@@ -1,14 +0,0 @@
1use crate::{clocks, pac, pins};
2
3/// Initialize clocks and pin muxing for UART2 debug console.
4pub unsafe fn init_uart2(p: &pac::Peripherals) {
5 clocks::ensure_frolf_running(p);
6 clocks::enable_uart2_port2(p);
7 pins::configure_uart2_pins_port2();
8 clocks::select_uart2_clock(p);
9}
10
11/// Initialize clocks for the LED GPIO/PORT used by the blink example.
12pub unsafe fn init_led(p: &pac::Peripherals) {
13 clocks::enable_led_port(p);
14}
diff --git a/src/clocks.rs b/src/clocks.rs
deleted file mode 100644
index 65a17cef6..000000000
--- a/src/clocks.rs
+++ /dev/null
@@ -1,134 +0,0 @@
1//! Clock control helpers (no magic numbers, PAC field access only).
2//! Provides reusable gate abstractions for peripherals used by the examples.
3use crate::pac;
4
5/// Trait describing an AHB clock gate that can be toggled through MRCC.
6pub trait Gate {
7 /// Enable the clock gate.
8 unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock);
9
10 /// Return whether the clock gate is currently enabled.
11 fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool;
12}
13
14/// Enable a clock gate for the given peripheral set.
15#[inline]
16pub unsafe fn enable<G: Gate>(peripherals: &pac::Peripherals) {
17 let mrcc = &peripherals.mrcc0;
18 G::enable(mrcc);
19 while !G::is_enabled(mrcc) {}
20 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
21}
22
23/// Check whether a gate is currently enabled.
24#[inline]
25pub fn is_enabled<G: Gate>(peripherals: &pac::Peripherals) -> bool {
26 G::is_enabled(&peripherals.mrcc0)
27}
28
29macro_rules! impl_cc_gate {
30 ($name:ident, $reg:ident, $field:ident) => {
31 pub struct $name;
32
33 impl Gate for $name {
34 #[inline]
35 unsafe fn enable(mrcc: &pac::mrcc0::RegisterBlock) {
36 mrcc.$reg().modify(|_, w| w.$field().enabled());
37 }
38
39 #[inline]
40 fn is_enabled(mrcc: &pac::mrcc0::RegisterBlock) -> bool {
41 mrcc.$reg().read().$field().is_enabled()
42 }
43 }
44 };
45}
46
47pub mod gate {
48 use super::*;
49
50 impl_cc_gate!(Port2, mrcc_glb_cc1, port2);
51 impl_cc_gate!(Port3, mrcc_glb_cc1, port3);
52 impl_cc_gate!(Ostimer0, mrcc_glb_cc1, ostimer0);
53 impl_cc_gate!(Lpuart2, mrcc_glb_cc0, lpuart2);
54 impl_cc_gate!(Gpio3, mrcc_glb_cc2, gpio3);
55 impl_cc_gate!(Port1, mrcc_glb_cc1, port1);
56 impl_cc_gate!(Adc1, mrcc_glb_cc1, adc1);
57}
58
59/// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART.
60pub unsafe fn enable_uart2_port2(peripherals: &pac::Peripherals) {
61 enable::<gate::Port2>(peripherals);
62 enable::<gate::Lpuart2>(peripherals);
63}
64
65/// Convenience helper enabling the PORT3 and GPIO3 gates used by the LED in the examples.
66pub unsafe fn enable_led_port(peripherals: &pac::Peripherals) {
67 enable::<gate::Port3>(peripherals);
68 enable::<gate::Gpio3>(peripherals);
69}
70
71/// Convenience helper enabling the OSTIMER0 clock gate.
72pub unsafe fn enable_ostimer0(peripherals: &pac::Peripherals) {
73 enable::<gate::Ostimer0>(peripherals);
74}
75
76pub unsafe fn select_uart2_clock(peripherals: &pac::Peripherals) {
77 // Use FRO_LF_DIV (already running) MUX=0 DIV=0
78 let mrcc = &peripherals.mrcc0;
79 mrcc.mrcc_lpuart2_clksel().write(|w| w.mux().clkroot_func_0());
80 mrcc.mrcc_lpuart2_clkdiv().write(|w| unsafe { w.bits(0) });
81}
82
83pub unsafe fn ensure_frolf_running(peripherals: &pac::Peripherals) {
84 // Ensure FRO_LF divider clock is running (reset default HALT=1 stops it)
85 let sys = &peripherals.syscon;
86 sys.frolfdiv().modify(|_, w| {
87 // DIV defaults to 0; keep it explicit and clear HALT
88 unsafe { w.div().bits(0) }.halt().run()
89 });
90}
91
92/// Compute the FRO_LF_DIV output frequency currently selected for LPUART2.
93/// Assumes select_uart2_clock() has chosen MUX=0 (FRO_LF_DIV) and DIV is set in SYSCON.FRO_LF_DIV.
94pub unsafe fn uart2_src_hz(peripherals: &pac::Peripherals) -> u32 {
95 // SYSCON.FRO_LF_DIV: DIV field is simple divider: freq_out = 12_000_000 / (DIV+1) for many NXP parts.
96 // On MCXA276 FRO_LF base is 12 MHz; our init keeps DIV=0, so result=12_000_000.
97 // Read it anyway for future generality.
98 let div = peripherals.syscon.frolfdiv().read().div().bits() as u32;
99 let base = 12_000_000u32;
100 base / (div + 1)
101}
102
103/// Enable clock gate and release reset for OSTIMER0.
104/// Select OSTIMER0 clock source = 1 MHz root (working bring-up configuration).
105pub unsafe fn select_ostimer0_clock_1m(peripherals: &pac::Peripherals) {
106 let mrcc = &peripherals.mrcc0;
107 mrcc.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
108}
109
110pub unsafe fn init_fro16k(peripherals: &pac::Peripherals) {
111 let vbat = &peripherals.vbat0;
112 // Enable FRO16K oscillator
113 vbat.froctla().modify(|_, w| w.fro_en().set_bit());
114
115 // Lock the control register
116 vbat.frolcka().modify(|_, w| w.lock().set_bit());
117
118 // Enable clock outputs to both VSYS and VDD_CORE domains
119 // Bit 0: clk_16k0 to VSYS domain
120 // Bit 1: clk_16k1 to VDD_CORE domain
121 vbat.froclke().modify(|_, w| unsafe { w.clke().bits(0x3) });
122}
123
124pub unsafe fn enable_adc(peripherals: &pac::Peripherals) {
125 enable::<gate::Port1>(peripherals);
126 enable::<gate::Adc1>(peripherals);
127}
128
129pub unsafe fn select_adc_clock(peripherals: &pac::Peripherals) {
130 // Use FRO_LF_DIV (already running) MUX=0 DIV=0
131 let mrcc = &peripherals.mrcc0;
132 mrcc.mrcc_adc_clksel().write(|w| w.mux().clkroot_func_0());
133 mrcc.mrcc_adc_clkdiv().write(|w| unsafe { w.bits(0) });
134}
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
new file mode 100644
index 000000000..e02840592
--- /dev/null
+++ b/src/clocks/mod.rs
@@ -0,0 +1,887 @@
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 assert_eq!(operator.syscon.ahbclkdiv().read().div().bits(), 0);
95 operator.clocks.main_clk = Some(operator.clocks.fro_hf_root.clone().unwrap());
96
97 critical_section::with(|cs| {
98 let mut clks = CLOCKS.borrow_ref_mut(cs);
99 assert!(clks.is_none(), "Clock setup race!");
100 *clks = Some(clocks);
101 });
102
103 Ok(())
104}
105
106/// Obtain the full clocks structure, calling the given closure in a critical section.
107///
108/// The given closure will be called with read-only access to the state of the system
109/// clocks. This can be used to query and return the state of a given clock.
110///
111/// As the caller's closure will be called in a critical section, care must be taken
112/// not to block or cause any other undue delays while accessing.
113///
114/// Calls to this function will not succeed until after a successful call to `init()`,
115/// and will always return None.
116pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
117 critical_section::with(|cs| {
118 let c = CLOCKS.borrow_ref(cs);
119 let c = c.as_ref()?;
120 Some(f(c))
121 })
122}
123
124//
125// Structs/Enums
126//
127
128/// The `Clocks` structure contains the initialized state of the core system clocks
129///
130/// These values are configured by providing [`config::ClocksConfig`] to the [`init()`] function
131/// at boot time.
132#[derive(Default, Debug, Clone)]
133#[non_exhaustive]
134pub struct Clocks {
135 /// The `clk_in` is a clock provided by an external oscillator
136 pub clk_in: Option<Clock>,
137
138 // FRO180M stuff
139 //
140 /// `fro_hf_root` is the direct output of the `FRO180M` internal oscillator
141 ///
142 /// It is used to feed downstream clocks, such as `fro_hf`, `clk_45m`,
143 /// and `fro_hf_div`.
144 pub fro_hf_root: Option<Clock>,
145
146 /// `fro_hf` is the same frequency as `fro_hf_root`, but behind a gate.
147 pub fro_hf: Option<Clock>,
148
149 /// `clk_45` is a 45MHz clock, sourced from `fro_hf`.
150 pub clk_45m: Option<Clock>,
151
152 /// `fro_hf_div` is a configurable frequency clock, sourced from `fro_hf`.
153 pub fro_hf_div: Option<Clock>,
154
155 //
156 // End FRO180M
157
158 // FRO12M stuff
159 //
160 /// `fro_12m_root` is the direct output of the `FRO12M` internal oscillator
161 ///
162 /// It is used to feed downstream clocks, such as `fro_12m`, `clk_1m`,
163 /// `and `fro_lf_div`.
164 pub fro_12m_root: Option<Clock>,
165
166 /// `fro_12m` is the same frequency as `fro_12m_root`, but behind a gate.
167 pub fro_12m: Option<Clock>,
168
169 /// `clk_1m` is a 1MHz clock, sourced from `fro_12m`
170 pub clk_1m: Option<Clock>,
171
172 /// `fro_lf_div` is a configurable frequency clock, sourced from `fro_12m`
173 pub fro_lf_div: Option<Clock>,
174 //
175 // End FRO12M stuff
176 /// `clk_16k_vsys` is one of two outputs of the `FRO16K` internal oscillator.
177 ///
178 /// Also referred to as `clk_16k[0]` in the datasheet, it feeds peripherals in
179 /// the system domain, such as the CMP and RTC.
180 pub clk_16k_vsys: Option<Clock>,
181
182 /// `clk_16k_vdd_core` is one of two outputs of the `FRO16K` internal oscillator.
183 ///
184 /// Also referred to as `clk_16k[1]` in the datasheet, it feeds peripherals in
185 /// the VDD Core domain, such as the OSTimer or LPUarts.
186 pub clk_16k_vdd_core: Option<Clock>,
187
188 /// `main_clk` is the main clock used by the CPU, AHB, APB, IPS bus, and some
189 /// peripherals.
190 pub main_clk: Option<Clock>,
191
192 /// `pll1_clk` is the output of the main system PLL, `pll1`.
193 pub pll1_clk: Option<Clock>,
194}
195
196/// `ClockError` is the main error returned when configuring or checking clock state
197#[derive(Debug, Copy, Clone, Eq, PartialEq)]
198#[cfg_attr(feature = "defmt", derive(defmt::Format))]
199#[non_exhaustive]
200pub enum ClockError {
201 /// The system clocks were never initialized by calling [`init()`]
202 NeverInitialized,
203 /// The [`init()`] function was called more than once
204 AlreadyInitialized,
205 /// The requested configuration was not possible to fulfill, as the system clocks
206 /// were not configured in a compatible way
207 BadConfig { clock: &'static str, reason: &'static str },
208 /// The requested configuration was not possible to fulfill, as the required system
209 /// clocks have not yet been implemented.
210 NotImplemented { clock: &'static str },
211 /// The requested peripheral could not be configured, as the steps necessary to
212 /// enable it have not yet been implemented.
213 UnimplementedConfig,
214}
215
216/// Information regarding a system clock
217#[derive(Debug, Clone)]
218pub struct Clock {
219 /// The frequency, in Hz, of the given clock
220 pub frequency: u32,
221 /// The power state of the clock, e.g. whether it is active in deep sleep mode
222 /// or not.
223 pub power: PoweredClock,
224}
225
226/// The power state of a given clock.
227///
228/// On the MCX-A, when Deep-Sleep is entered, any clock not configured for Deep Sleep
229/// mode will be stopped. This means that any downstream usage, e.g. by peripherals,
230/// will also stop.
231///
232/// In the future, we will provide an API for entering Deep Sleep, and if there are
233/// any peripherals that are NOT using an `AlwaysEnabled` clock active, entry into
234/// Deep Sleep will be prevented, in order to avoid misbehaving peripherals.
235#[derive(Debug, Clone, Copy, PartialEq, Eq)]
236pub enum PoweredClock {
237 /// The given clock will NOT continue running in Deep Sleep mode
238 NormalEnabledDeepSleepDisabled,
239 /// The given clock WILL continue running in Deep Sleep mode
240 AlwaysEnabled,
241}
242
243/// The ClockOperator is a private helper type that contains the methods used
244/// during system clock initialization.
245///
246/// # SAFETY
247///
248/// Concurrent access to clock-relevant peripheral registers, such as `MRCC`, `SCG`,
249/// `SYSCON`, and `VBAT` should not be allowed for the duration of the [`init()`] function.
250struct ClockOperator<'a> {
251 /// A mutable reference to the current state of system clocks
252 clocks: &'a mut Clocks,
253 /// A reference to the requested configuration provided by the caller of [`init()`]
254 config: &'a ClocksConfig,
255
256 // We hold on to stolen peripherals
257 _mrcc0: pac::Mrcc0,
258 scg0: pac::Scg0,
259 syscon: pac::Syscon,
260 vbat0: pac::Vbat0,
261}
262
263/// Trait describing an AHB clock gate that can be toggled through MRCC.
264pub trait Gate {
265 type MrccPeriphConfig: SPConfHelper;
266
267 /// Enable the clock gate.
268 ///
269 /// # SAFETY
270 ///
271 /// The current peripheral must be disabled prior to calling this method
272 unsafe fn enable_clock();
273
274 /// Disable the clock gate.
275 ///
276 /// # SAFETY
277 ///
278 /// There must be no active user of this peripheral when calling this method
279 unsafe fn disable_clock();
280
281 /// Drive the peripheral into reset.
282 ///
283 /// # SAFETY
284 ///
285 /// There must be no active user of this peripheral when calling this method
286 unsafe fn assert_reset();
287
288 /// Drive the peripheral out of reset.
289 ///
290 /// # SAFETY
291 ///
292 /// There must be no active user of this peripheral when calling this method
293 unsafe fn release_reset();
294
295 /// Return whether the clock gate for this peripheral is currently enabled.
296 fn is_clock_enabled() -> bool;
297
298 /// Return whether the peripheral is currently held in reset.
299 fn is_reset_released() -> bool;
300}
301
302/// This is the primary helper method HAL drivers are expected to call when creating
303/// an instance of the peripheral.
304///
305/// This method:
306///
307/// 1. Enables the MRCC clock gate for this peripheral
308/// 2. Calls the `G::MrccPeriphConfig::post_enable_config()` method, returning an error
309/// and re-disabling the peripheral if this fails.
310/// 3. Pulses the MRCC reset line, to reset the peripheral to the default state
311/// 4. Returns the frequency, in Hz that is fed into the peripheral, taking into account
312/// the selected upstream clock, as well as any division specified by `cfg`.
313///
314/// NOTE: if a clock is disabled, sourced from an "ambient" clock source, this method
315/// may return `Ok(0)`. In the future, this might be updated to return the correct
316/// "ambient" clock, e.g. the AHB/APB frequency.
317///
318/// # SAFETY
319///
320/// This peripheral must not yet be in use prior to calling `enable_and_reset`.
321#[inline]
322pub unsafe fn enable_and_reset<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
323 let freq = enable::<G>(cfg).inspect_err(|_| disable::<G>())?;
324 pulse_reset::<G>();
325 Ok(freq)
326}
327
328/// Enable the clock gate for the given peripheral.
329///
330/// Prefer [`enable_and_reset`] unless you are specifically avoiding a pulse of the reset, or need
331/// to control the duration of the pulse more directly.
332///
333/// # SAFETY
334///
335/// This peripheral must not yet be in use prior to calling `enable`.
336#[inline]
337pub unsafe fn enable<G: Gate>(cfg: &G::MrccPeriphConfig) -> Result<u32, ClockError> {
338 G::enable_clock();
339 while !G::is_clock_enabled() {}
340 core::arch::asm!("dsb sy; isb sy", options(nomem, nostack, preserves_flags));
341
342 let freq = critical_section::with(|cs| {
343 let clocks = CLOCKS.borrow_ref(cs);
344 let clocks = clocks.as_ref().ok_or(ClockError::NeverInitialized)?;
345 cfg.post_enable_config(clocks)
346 });
347
348 freq.inspect_err(|_e| {
349 G::disable_clock();
350 })
351}
352
353/// Disable the clock gate for the given peripheral.
354///
355/// # SAFETY
356///
357/// This peripheral must no longer be in use prior to calling `enable`.
358#[allow(dead_code)]
359#[inline]
360pub unsafe fn disable<G: Gate>() {
361 G::disable_clock();
362}
363
364/// Check whether a gate is currently enabled.
365#[allow(dead_code)]
366#[inline]
367pub fn is_clock_enabled<G: Gate>() -> bool {
368 G::is_clock_enabled()
369}
370
371/// Release a reset line for the given peripheral set.
372///
373/// Prefer [`enable_and_reset`].
374///
375/// # SAFETY
376///
377/// This peripheral must not yet be in use prior to calling `release_reset`.
378#[inline]
379pub unsafe fn release_reset<G: Gate>() {
380 G::release_reset();
381}
382
383/// Assert a reset line for the given peripheral set.
384///
385/// Prefer [`enable_and_reset`].
386///
387/// # SAFETY
388///
389/// This peripheral must not yet be in use prior to calling `assert_reset`.
390#[inline]
391pub unsafe fn assert_reset<G: Gate>() {
392 G::assert_reset();
393}
394
395/// Check whether the peripheral is held in reset.
396#[inline]
397pub unsafe fn is_reset_released<G: Gate>() -> bool {
398 G::is_reset_released()
399}
400
401/// Pulse a reset line (assert then release) with a short delay.
402///
403/// Prefer [`enable_and_reset`].
404///
405/// # SAFETY
406///
407/// This peripheral must not yet be in use prior to calling `release_reset`.
408#[inline]
409pub unsafe fn pulse_reset<G: Gate>() {
410 G::assert_reset();
411 cortex_m::asm::nop();
412 cortex_m::asm::nop();
413 G::release_reset();
414}
415
416//
417// `impl`s for structs/enums
418//
419
420/// The [`Clocks`] type's methods generally take the form of "ensure X clock is active".
421///
422/// These methods are intended to be used by HAL peripheral implementors to ensure that their
423/// selected clocks are active at a suitable level at time of construction. These methods
424/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].
425impl Clocks {
426 /// Ensure the `fro_lf_div` clock is active and valid at the given power state.
427 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
428 let Some(clk) = self.fro_lf_div.as_ref() else {
429 return Err(ClockError::BadConfig {
430 clock: "fro_lf_div",
431 reason: "required but not active",
432 });
433 };
434 if !clk.power.meets_requirement_of(at_level) {
435 return Err(ClockError::BadConfig {
436 clock: "fro_lf_div",
437 reason: "not low power active",
438 });
439 }
440 Ok(clk.frequency)
441 }
442
443 /// Ensure the `fro_hf` clock is active and valid at the given power state.
444 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
445 let Some(clk) = self.fro_hf.as_ref() else {
446 return Err(ClockError::BadConfig {
447 clock: "fro_hf",
448 reason: "required but not active",
449 });
450 };
451 if !clk.power.meets_requirement_of(at_level) {
452 return Err(ClockError::BadConfig {
453 clock: "fro_hf",
454 reason: "not low power active",
455 });
456 }
457 Ok(clk.frequency)
458 }
459
460 /// Ensure the `fro_hf_div` clock is active and valid at the given power state.
461 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
462 let Some(clk) = self.fro_hf_div.as_ref() else {
463 return Err(ClockError::BadConfig {
464 clock: "fro_hf_div",
465 reason: "required but not active",
466 });
467 };
468 if !clk.power.meets_requirement_of(at_level) {
469 return Err(ClockError::BadConfig {
470 clock: "fro_hf_div",
471 reason: "not low power active",
472 });
473 }
474 Ok(clk.frequency)
475 }
476
477 /// Ensure the `clk_in` clock is active and valid at the given power state.
478 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
479 Err(ClockError::NotImplemented { clock: "clk_in" })
480 }
481
482 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
483 pub fn ensure_clk_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
484 // NOTE: clk_16k is always active in low power mode
485 Ok(self
486 .clk_16k_vsys
487 .as_ref()
488 .ok_or(ClockError::BadConfig {
489 clock: "clk_16k_vsys",
490 reason: "required but not active",
491 })?
492 .frequency)
493 }
494
495 /// Ensure the `clk_16k_vdd_core` clock is active and valid at the given power state.
496 pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
497 // NOTE: clk_16k is always active in low power mode
498 Ok(self
499 .clk_16k_vdd_core
500 .as_ref()
501 .ok_or(ClockError::BadConfig {
502 clock: "clk_16k_vdd_core",
503 reason: "required but not active",
504 })?
505 .frequency)
506 }
507
508 /// Ensure the `clk_1m` clock is active and valid at the given power state.
509 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
510 let Some(clk) = self.clk_1m.as_ref() else {
511 return Err(ClockError::BadConfig {
512 clock: "clk_1m",
513 reason: "required but not active",
514 });
515 };
516 if !clk.power.meets_requirement_of(at_level) {
517 return Err(ClockError::BadConfig {
518 clock: "clk_1m",
519 reason: "not low power active",
520 });
521 }
522 Ok(clk.frequency)
523 }
524
525 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.
526 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
527 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
528 }
529}
530
531impl PoweredClock {
532 /// Does THIS clock meet the power requirements of the OTHER clock?
533 pub fn meets_requirement_of(&self, other: &Self) -> bool {
534 match (self, other) {
535 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false,
536 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
537 (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
538 (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true,
539 }
540 }
541}
542
543impl ClockOperator<'_> {
544 /// Configure the FIRC/FRO180M clock family
545 ///
546 /// NOTE: Currently we require this to be a fairly hardcoded value, as this clock is used
547 /// as the main clock used for the CPU, AHB, APB, etc.
548 fn configure_firc_clocks(&mut self) -> Result<(), ClockError> {
549 const HARDCODED_ERR: Result<(), ClockError> = Err(ClockError::BadConfig {
550 clock: "firc",
551 reason: "For now, FIRC must be enabled and in default state!",
552 });
553
554 // Did the user give us a FIRC config?
555 let Some(firc) = self.config.firc.as_ref() else {
556 return HARDCODED_ERR;
557 };
558 // Is the FIRC set to 45MHz (should be reset default)
559 if !matches!(firc.frequency, FircFreqSel::Mhz45) {
560 return HARDCODED_ERR;
561 }
562 let base_freq = 45_000_000;
563
564 // Now, check if the FIRC as expected for our hardcoded value
565 let mut firc_ok = true;
566
567 // Is the hardware currently set to the default 45MHz?
568 //
569 // NOTE: the SVD currently has the wrong(?) values for these:
570 // 45 -> 48
571 // 60 -> 64
572 // 90 -> 96
573 // 180 -> 192
574 // Probably correct-ish, but for a different trim value?
575 firc_ok &= self.scg0.firccfg().read().freq_sel().is_firc_48mhz_192s();
576
577 // Check some values in the CSR
578 let csr = self.scg0.firccsr().read();
579 // Is it enabled?
580 firc_ok &= csr.fircen().is_enabled();
581 // Is it accurate?
582 firc_ok &= csr.fircacc().is_enabled_and_valid();
583 // Is there no error?
584 firc_ok &= csr.fircerr().is_error_not_detected();
585 // Is the FIRC the system clock?
586 firc_ok &= csr.fircsel().is_firc();
587 // Is it valid?
588 firc_ok &= csr.fircvld().is_enabled_and_valid();
589
590 // Are we happy with the current (hardcoded) state?
591 if !firc_ok {
592 return HARDCODED_ERR;
593 }
594
595 // Note that the fro_hf_root is active
596 self.clocks.fro_hf_root = Some(Clock {
597 frequency: base_freq,
598 power: firc.power,
599 });
600
601 // Okay! Now we're past that, let's enable all the downstream clocks.
602 let FircConfig {
603 frequency: _,
604 power,
605 fro_hf_enabled,
606 clk_45m_enabled,
607 fro_hf_div,
608 } = firc;
609
610 // When is the FRO enabled?
611 let pow_set = match power {
612 PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes,
613 PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes,
614 };
615
616 // Do we enable the `fro_hf` output?
617 let fro_hf_set = if *fro_hf_enabled {
618 self.clocks.fro_hf = Some(Clock {
619 frequency: base_freq,
620 power: *power,
621 });
622 FircFclkPeriphEn::Enabled
623 } else {
624 FircFclkPeriphEn::Disabled
625 };
626
627 // Do we enable the `clk_45m` output?
628 let clk_45m_set = if *clk_45m_enabled {
629 self.clocks.clk_45m = Some(Clock {
630 frequency: 45_000_000,
631 power: *power,
632 });
633 FircSclkPeriphEn::Enabled
634 } else {
635 FircSclkPeriphEn::Disabled
636 };
637
638 self.scg0.firccsr().modify(|_r, w| {
639 w.fircsten().variant(pow_set);
640 w.firc_fclk_periph_en().variant(fro_hf_set);
641 w.firc_sclk_periph_en().variant(clk_45m_set);
642 w
643 });
644
645 // Do we enable the `fro_hf_div` output?
646 if let Some(d) = fro_hf_div.as_ref() {
647 // We need `fro_hf` to be enabled
648 if !*fro_hf_enabled {
649 return Err(ClockError::BadConfig {
650 clock: "fro_hf_div",
651 reason: "fro_hf not enabled",
652 });
653 }
654
655 // Halt and reset the div
656 self.syscon.frohfdiv().write(|w| {
657 w.halt().halt();
658 w.reset().asserted();
659 w
660 });
661 // Then change the div, unhalt it, and reset it
662 self.syscon.frohfdiv().write(|w| {
663 unsafe {
664 w.div().bits(d.into_bits());
665 }
666 w.halt().run();
667 w.reset().released();
668 w
669 });
670
671 // Wait for clock to stabilize
672 while self.syscon.frohfdiv().read().unstab().is_ongoing() {}
673
674 // Store off the clock info
675 self.clocks.fro_hf_div = Some(Clock {
676 frequency: base_freq / d.into_divisor(),
677 power: *power,
678 });
679 }
680
681 Ok(())
682 }
683
684 /// Configure the SIRC/FRO12M clock family
685 fn configure_sirc_clocks(&mut self) -> Result<(), ClockError> {
686 let SircConfig {
687 power,
688 fro_12m_enabled,
689 fro_lf_div,
690 } = &self.config.sirc;
691 let base_freq = 12_000_000;
692
693 // Allow writes
694 self.scg0.sirccsr().modify(|_r, w| w.lk().write_enabled());
695 self.clocks.fro_12m_root = Some(Clock {
696 frequency: base_freq,
697 power: *power,
698 });
699
700 let deep = match power {
701 PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled,
702 PoweredClock::AlwaysEnabled => Sircsten::Enabled,
703 };
704 let pclk = if *fro_12m_enabled {
705 self.clocks.fro_12m = Some(Clock {
706 frequency: base_freq,
707 power: *power,
708 });
709 self.clocks.clk_1m = Some(Clock {
710 frequency: base_freq / 12,
711 power: *power,
712 });
713 SircClkPeriphEn::Enabled
714 } else {
715 SircClkPeriphEn::Disabled
716 };
717
718 // Set sleep/peripheral usage
719 self.scg0.sirccsr().modify(|_r, w| {
720 w.sircsten().variant(deep);
721 w.sirc_clk_periph_en().variant(pclk);
722 w
723 });
724
725 while self.scg0.sirccsr().read().sircvld().is_disabled_or_not_valid() {}
726 if self.scg0.sirccsr().read().sircerr().is_error_detected() {
727 return Err(ClockError::BadConfig {
728 clock: "sirc",
729 reason: "error set",
730 });
731 }
732
733 // reset lock
734 self.scg0.sirccsr().modify(|_r, w| w.lk().write_disabled());
735
736 // Do we enable the `fro_lf_div` output?
737 if let Some(d) = fro_lf_div.as_ref() {
738 // We need `fro_lf` to be enabled
739 if !*fro_12m_enabled {
740 return Err(ClockError::BadConfig {
741 clock: "fro_lf_div",
742 reason: "fro_12m not enabled",
743 });
744 }
745
746 // Halt and reset the div
747 self.syscon.frolfdiv().write(|w| {
748 w.halt().halt();
749 w.reset().asserted();
750 w
751 });
752 // Then change the div, unhalt it, and reset it
753 self.syscon.frolfdiv().write(|w| {
754 unsafe {
755 w.div().bits(d.into_bits());
756 }
757 w.halt().run();
758 w.reset().released();
759 w
760 });
761
762 // Wait for clock to stabilize
763 while self.syscon.frolfdiv().read().unstab().is_ongoing() {}
764
765 // Store off the clock info
766 self.clocks.fro_lf_div = Some(Clock {
767 frequency: base_freq / d.into_divisor(),
768 power: *power,
769 });
770 }
771
772 Ok(())
773 }
774
775 /// Configure the FRO16K/clk_16k clock family
776 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
777 let Some(fro16k) = self.config.fro16k.as_ref() else {
778 return Ok(());
779 };
780 // Enable FRO16K oscillator
781 self.vbat0.froctla().modify(|_, w| w.fro_en().set_bit());
782
783 // Lock the control register
784 self.vbat0.frolcka().modify(|_, w| w.lock().set_bit());
785
786 let Fro16KConfig {
787 vsys_domain_active,
788 vdd_core_domain_active,
789 } = fro16k;
790
791 // Enable clock outputs to both VSYS and VDD_CORE domains
792 // Bit 0: clk_16k0 to VSYS domain
793 // Bit 1: clk_16k1 to VDD_CORE domain
794 //
795 // TODO: Define sub-fields for this register with a PAC patch?
796 let mut bits = 0;
797 if *vsys_domain_active {
798 bits |= 0b01;
799 self.clocks.clk_16k_vsys = Some(Clock {
800 frequency: 16_384,
801 power: PoweredClock::AlwaysEnabled,
802 });
803 }
804 if *vdd_core_domain_active {
805 bits |= 0b10;
806 self.clocks.clk_16k_vdd_core = Some(Clock {
807 frequency: 16_384,
808 power: PoweredClock::AlwaysEnabled,
809 });
810 }
811 self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) });
812
813 Ok(())
814 }
815}
816
817//
818// Macros/macro impls
819//
820
821/// This macro is used to implement the [`Gate`] trait for a given peripheral
822/// that is controlled by the MRCC peripheral.
823macro_rules! impl_cc_gate {
824 ($name:ident, $clk_reg:ident, $rst_reg:ident, $field:ident, $config:ty) => {
825 impl Gate for crate::peripherals::$name {
826 type MrccPeriphConfig = $config;
827
828 #[inline]
829 unsafe fn enable_clock() {
830 let mrcc = unsafe { pac::Mrcc0::steal() };
831 mrcc.$clk_reg().modify(|_, w| w.$field().enabled());
832 }
833
834 #[inline]
835 unsafe fn disable_clock() {
836 let mrcc = unsafe { pac::Mrcc0::steal() };
837 mrcc.$clk_reg().modify(|_r, w| w.$field().disabled());
838 }
839
840 #[inline]
841 fn is_clock_enabled() -> bool {
842 let mrcc = unsafe { pac::Mrcc0::steal() };
843 mrcc.$clk_reg().read().$field().is_enabled()
844 }
845
846 #[inline]
847 unsafe fn release_reset() {
848 let mrcc = unsafe { pac::Mrcc0::steal() };
849 mrcc.$rst_reg().modify(|_, w| w.$field().enabled());
850 }
851
852 #[inline]
853 unsafe fn assert_reset() {
854 let mrcc = unsafe { pac::Mrcc0::steal() };
855 mrcc.$rst_reg().modify(|_, w| w.$field().disabled());
856 }
857
858 #[inline]
859 fn is_reset_released() -> bool {
860 let mrcc = unsafe { pac::Mrcc0::steal() };
861 mrcc.$rst_reg().read().$field().is_enabled()
862 }
863 }
864 };
865}
866
867/// This module contains implementations of MRCC APIs, specifically of the [`Gate`] trait,
868/// for various low level peripherals.
869pub(crate) mod gate {
870 use super::periph_helpers::{AdcConfig, LpuartConfig, NoConfig, OsTimerConfig};
871 use super::*;
872
873 // These peripherals have no additional upstream clocks or configuration required
874 // other than enabling through the MRCC gate. Currently, these peripherals will
875 // ALWAYS return `Ok(0)` when calling [`enable_and_reset()`] and/or
876 // [`SPConfHelper::post_enable_config()`].
877 impl_cc_gate!(PORT1, mrcc_glb_cc1, mrcc_glb_rst1, port1, NoConfig);
878 impl_cc_gate!(PORT2, mrcc_glb_cc1, mrcc_glb_rst1, port2, NoConfig);
879 impl_cc_gate!(PORT3, mrcc_glb_cc1, mrcc_glb_rst1, port3, NoConfig);
880 impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig);
881
882 // These peripherals DO have meaningful configuration, and could fail if the system
883 // clocks do not match their needs.
884 impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, mrcc_glb_rst1, ostimer0, OsTimerConfig);
885 impl_cc_gate!(LPUART2, mrcc_glb_cc0, mrcc_glb_rst0, lpuart2, LpuartConfig);
886 impl_cc_gate!(ADC1, mrcc_glb_cc1, mrcc_glb_rst1, adc1, AdcConfig);
887}
diff --git a/src/clocks/periph_helpers.rs b/src/clocks/periph_helpers.rs
new file mode 100644
index 000000000..e5b234c5b
--- /dev/null
+++ b/src/clocks/periph_helpers.rs
@@ -0,0 +1,393 @@
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
10use super::{ClockError, Clocks, PoweredClock};
11use crate::pac;
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()`.
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.
41 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>;
42}
43
44// config types
45
46/// This type represents a divider in the range 1..=16.
47///
48/// At a hardware level, this is an 8-bit register from 0..=15,
49/// which adds one.
50///
51/// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain
52/// seems to use 4 bit dividers!
53#[derive(Copy, Clone, Debug, PartialEq, Eq)]
54pub struct Div4(pub(super) u8);
55
56impl Div4 {
57 /// Divide by one, or no division
58 pub const fn no_div() -> Self {
59 Self(0)
60 }
61
62 /// Store a "raw" divisor value that will divide the source by
63 /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source
64 /// by 1, and `Div4::from_raw(15)` will divide the source by
65 /// 16.
66 pub const fn from_raw(n: u8) -> Option<Self> {
67 if n > 0b1111 {
68 None
69 } else {
70 Some(Self(n))
71 }
72 }
73
74 /// Store a specific divisor value that will divide the source
75 /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source
76 /// by 1, and `Div4::from_divisor(16)` will divide the source
77 /// by 16.
78 ///
79 /// Will return `None` if `n` is not in the range `1..=16`.
80 /// Consider [`Self::from_raw`] for an infallible version.
81 pub const fn from_divisor(n: u8) -> Option<Self> {
82 let Some(n) = n.checked_sub(1) else {
83 return None;
84 };
85 if n > 0b1111 {
86 return None;
87 }
88 Some(Self(n))
89 }
90
91 /// Convert into "raw" bits form
92 #[inline(always)]
93 pub const fn into_bits(self) -> u8 {
94 self.0
95 }
96
97 /// Convert into "divisor" form, as a u32 for convenient frequency math
98 #[inline(always)]
99 pub const fn into_divisor(self) -> u32 {
100 self.0 as u32 + 1
101 }
102}
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
131#[derive(Debug, Clone, Copy)]
132pub enum LpuartClockSel {
133 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
134 /// "fro_lf_div"
135 FroLfDiv,
136 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
137 /// "fro_hf_div"
138 FroHfDiv,
139 /// SOSC/XTAL/EXTAL clock source
140 ClkIn,
141 /// FRO16K/clk_16k source
142 Clk16K,
143 /// clk_1m/FRO_LF divided by 12
144 Clk1M,
145 /// Output of PLL1, passed through clock divider,
146 /// "pll1_clk_div", maybe "pll1_lf_div"?
147 Pll1ClkDiv,
148 /// Disabled
149 None,
150}
151
152/// Which instance of the Lpuart is this?
153///
154/// Should not be directly selectable by end-users.
155#[derive(Copy, Clone, Debug, PartialEq, Eq)]
156pub enum LpuartInstance {
157 /// Instance 0
158 Lpuart0,
159 /// Instance 1
160 Lpuart1,
161 /// Instance 2
162 Lpuart2,
163 /// Instance 3
164 Lpuart3,
165 /// Instance 4
166 Lpuart4,
167 /// Instance 5
168 Lpuart5,
169}
170
171/// Top level configuration for `Lpuart` instances.
172pub struct LpuartConfig {
173 /// Power state required for this peripheral
174 pub power: PoweredClock,
175 /// Clock source
176 pub source: LpuartClockSel,
177 /// Clock divisor
178 pub div: Div4,
179 /// Which instance is this?
180 // NOTE: should not be user settable
181 pub(crate) instance: LpuartInstance,
182}
183
184impl SPConfHelper for LpuartConfig {
185 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
186 // check that source is suitable
187 let mrcc0 = unsafe { pac::Mrcc0::steal() };
188 use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux;
189
190 let (clkdiv, clksel) = match self.instance {
191 LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()),
192 LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()),
193 LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()),
194 LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()),
195 LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()),
196 LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()),
197 };
198
199 let (freq, variant) = match self.source {
200 LpuartClockSel::FroLfDiv => {
201 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
202 (freq, Mux::ClkrootFunc0)
203 }
204 LpuartClockSel::FroHfDiv => {
205 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
206 (freq, Mux::ClkrootFunc2)
207 }
208 LpuartClockSel::ClkIn => {
209 let freq = clocks.ensure_clk_in_active(&self.power)?;
210 (freq, Mux::ClkrootFunc3)
211 }
212 LpuartClockSel::Clk16K => {
213 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
214 (freq, Mux::ClkrootFunc4)
215 }
216 LpuartClockSel::Clk1M => {
217 let freq = clocks.ensure_clk_1m_active(&self.power)?;
218 (freq, Mux::ClkrootFunc5)
219 }
220 LpuartClockSel::Pll1ClkDiv => {
221 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
222 (freq, Mux::ClkrootFunc6)
223 }
224 LpuartClockSel::None => unsafe {
225 // no ClkrootFunc7, just write manually for now
226 clksel.write(|w| w.bits(0b111));
227 clkdiv.modify(|_r, w| {
228 w.reset().on();
229 w.halt().on();
230 w
231 });
232 return Ok(0);
233 },
234 };
235
236 // set clksel
237 clksel.modify(|_r, w| w.mux().variant(variant));
238
239 // Set up clkdiv
240 clkdiv.modify(|_r, w| {
241 w.halt().on();
242 w.reset().on();
243 w
244 });
245 clkdiv.modify(|_r, w| {
246 w.halt().off();
247 w.reset().off();
248 unsafe { w.div().bits(self.div.into_bits()) };
249 w
250 });
251
252 while clkdiv.read().unstab().is_on() {}
253
254 Ok(freq / self.div.into_divisor())
255 }
256}
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
281impl SPConfHelper for OsTimerConfig {
282 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
283 let mrcc0 = unsafe { pac::Mrcc0::steal() };
284 Ok(match self.source {
285 OstimerClockSel::Clk16kVddCore => {
286 let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?;
287 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k());
288 freq
289 }
290 OstimerClockSel::Clk1M => {
291 let freq = clocks.ensure_clk_1m_active(&self.power)?;
292 mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m());
293 freq
294 }
295 OstimerClockSel::None => {
296 mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) });
297 0
298 }
299 })
300 }
301}
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
334impl SPConfHelper for AdcConfig {
335 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
336 use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux;
337 let mrcc0 = unsafe { pac::Mrcc0::steal() };
338 let (freq, variant) = match self.source {
339 AdcClockSel::FroLfDiv => {
340 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
341 (freq, Mux::ClkrootFunc0)
342 }
343 AdcClockSel::FroHf => {
344 let freq = clocks.ensure_fro_hf_active(&self.power)?;
345 (freq, Mux::ClkrootFunc1)
346 }
347 AdcClockSel::ClkIn => {
348 let freq = clocks.ensure_clk_in_active(&self.power)?;
349 (freq, Mux::ClkrootFunc3)
350 }
351 AdcClockSel::Clk1M => {
352 let freq = clocks.ensure_clk_1m_active(&self.power)?;
353 (freq, Mux::ClkrootFunc5)
354 }
355 AdcClockSel::Pll1ClkDiv => {
356 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
357 (freq, Mux::ClkrootFunc6)
358 }
359 AdcClockSel::None => {
360 mrcc0.mrcc_adc_clksel().write(|w| unsafe {
361 // no ClkrootFunc7, just write manually for now
362 w.mux().bits(0b111)
363 });
364 mrcc0.mrcc_adc_clkdiv().modify(|_r, w| {
365 w.reset().on();
366 w.halt().on();
367 w
368 });
369 return Ok(0);
370 }
371 };
372
373 // set clksel
374 mrcc0.mrcc_adc_clksel().modify(|_r, w| w.mux().variant(variant));
375
376 // Set up clkdiv
377 mrcc0.mrcc_adc_clkdiv().modify(|_r, w| {
378 w.halt().on();
379 w.reset().on();
380 w
381 });
382 mrcc0.mrcc_adc_clkdiv().modify(|_r, w| {
383 w.halt().off();
384 w.reset().off();
385 unsafe { w.div().bits(self.div.into_bits()) };
386 w
387 });
388
389 while mrcc0.mrcc_adc_clkdiv().read().unstab().is_on() {}
390
391 Ok(freq / self.div.into_divisor())
392 }
393}
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}
diff --git a/src/lib.rs b/src/lib.rs
index 4e5ac0109..86c0dc45b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -6,7 +6,6 @@
6pub mod clocks; // still provide clock helpers 6pub mod clocks; // still provide clock helpers
7pub mod gpio; 7pub mod gpio;
8pub mod pins; // pin mux helpers 8pub mod pins; // pin mux helpers
9pub mod reset; // reset control helpers
10 9
11pub mod adc; 10pub mod adc;
12pub mod config; 11pub mod config;
@@ -14,9 +13,8 @@ pub mod interrupt;
14pub mod lpuart; 13pub mod lpuart;
15pub mod ostimer; 14pub mod ostimer;
16pub mod rtc; 15pub mod rtc;
17pub mod uart;
18 16
19embassy_hal_internal::peripherals!(LPUART2, OSTIMER0, GPIO, RTC0, ADC1,); 17embassy_hal_internal::peripherals!(PORT1, PORT2, PORT3, LPUART2, OSTIMER0, GPIO, PIO2_2, PIO2_3, GPIO3, RTC0, ADC1,);
20 18
21/// Get access to the PAC Peripherals for low-level register access. 19/// Get access to the PAC Peripherals for low-level register access.
22/// This is a lazy-initialized singleton that can be called after init(). 20/// This is a lazy-initialized singleton that can be called after init().
@@ -46,11 +44,9 @@ pub use mcxa_pac as pac;
46pub(crate) use mcxa_pac as pac; 44pub(crate) use mcxa_pac as pac;
47pub use ostimer::Ostimer0 as Ostimer0Token; 45pub use ostimer::Ostimer0 as Ostimer0Token;
48pub use rtc::Rtc0 as Rtc0Token; 46pub use rtc::Rtc0 as Rtc0Token;
49pub use uart::Lpuart2 as Uart2Token;
50 47
51/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals. 48/// Initialize HAL with configuration (mirrors embassy-imxrt style). Minimal: just take peripherals.
52/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling). 49/// Also applies configurable NVIC priority for the OSTIMER OS_EVENT interrupt (no enabling).
53#[allow(unused_variables)]
54pub fn init(cfg: crate::config::Config) -> Peripherals { 50pub fn init(cfg: crate::config::Config) -> Peripherals {
55 let peripherals = Peripherals::take(); 51 let peripherals = Peripherals::take();
56 // Apply user-configured priority early; enabling is left to examples/apps 52 // Apply user-configured priority early; enabling is left to examples/apps
@@ -59,6 +55,10 @@ pub fn init(cfg: crate::config::Config) -> Peripherals {
59 crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority); 55 crate::interrupt::RTC.set_priority(cfg.rtc_interrupt_priority);
60 // Apply user-configured priority early; enabling is left to examples/apps 56 // Apply user-configured priority early; enabling is left to examples/apps
61 crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority); 57 crate::interrupt::ADC1.set_priority(cfg.adc_interrupt_priority);
58
59 // Configure clocks
60 crate::clocks::init(cfg.clock_cfg).unwrap();
61
62 peripherals 62 peripherals
63} 63}
64 64
diff --git a/src/lpuart/buffered.rs b/src/lpuart/buffered.rs
index a617c10a5..7b575c086 100644
--- a/src/lpuart/buffered.rs
+++ b/src/lpuart/buffered.rs
@@ -212,10 +212,19 @@ impl<'a> BufferedLpuart<'a> {
212 config.enable_tx = true; 212 config.enable_tx = true;
213 config.enable_rx = true; 213 config.enable_rx = true;
214 214
215 // Enable clocks
216 let conf = LpuartConfig {
217 power: config.power,
218 source: config.source,
219 div: config.div,
220 instance: T::CLOCK_INSTANCE,
221 };
222 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
223
215 // Perform standard initialization 224 // Perform standard initialization
216 perform_software_reset(regs); 225 perform_software_reset(regs);
217 disable_transceiver(regs); 226 disable_transceiver(regs);
218 configure_baudrate(regs, config.baudrate_bps, config.clock)?; 227 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
219 configure_frame_format(regs, &config); 228 configure_frame_format(regs, &config);
220 configure_control_settings(regs, &config); 229 configure_control_settings(regs, &config);
221 configure_fifo(regs, &config); 230 configure_fifo(regs, &config);
diff --git a/src/lpuart/mod.rs b/src/lpuart/mod.rs
index 35b531421..b3d7c4885 100644
--- a/src/lpuart/mod.rs
+++ b/src/lpuart/mod.rs
@@ -3,6 +3,8 @@ use core::marker::PhantomData;
3use embassy_hal_internal::{Peri, PeripheralType}; 3use embassy_hal_internal::{Peri, PeripheralType};
4use paste::paste; 4use paste::paste;
5 5
6use crate::clocks::periph_helpers::{Div4, LpuartClockSel, LpuartConfig};
7use crate::clocks::{enable_and_reset, ClockError, Gate, PoweredClock};
6use crate::pac::lpuart0::baud::Sbns as StopBits; 8use crate::pac::lpuart0::baud::Sbns as StopBits;
7use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits}; 9use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits};
8use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; 10use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource};
@@ -18,71 +20,6 @@ pub mod buffered;
18// Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API 20// Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API
19// Pin and Clock initialization is currently done at the examples level. 21// Pin and Clock initialization is currently done at the examples level.
20 22
21// --- START LIB ---
22
23// Use our own instance of Peripherals, until we align `lib.rs` with the EMBASSY-IMXRT approach
24// Inlined peripherals_definition! to bypass the `Peripherals::take_with_cs()` check
25// SHOULD NOT BE USED IN THE FINAL VERSION
26pub mod lib {
27 // embassy_hal_internal::peripherals!(LPUART2, PIO2_2, PIO2_3)
28
29 embassy_hal_internal::peripherals_definition!(LPUART2, PIO2_2, PIO2_3,);
30 #[doc = r" Struct containing all the peripheral singletons."]
31 #[doc = r""]
32 #[doc = r" To obtain the peripherals, you must initialize the HAL, by calling [`crate::init`]."]
33 #[allow(non_snake_case)]
34 pub struct Peripherals {
35 #[doc = concat!(stringify!(LPUART2)," peripheral")]
36 pub LPUART2: embassy_hal_internal::Peri<'static, peripherals::LPUART2>,
37 #[doc = concat!(stringify!(PIO2_2)," peripheral")]
38 pub PIO2_2: embassy_hal_internal::Peri<'static, peripherals::PIO2_2>,
39 #[doc = concat!(stringify!(PIO2_3)," peripheral")]
40 pub PIO2_3: embassy_hal_internal::Peri<'static, peripherals::PIO2_3>,
41 }
42 impl Peripherals {
43 #[doc = r"Returns all the peripherals *once*"]
44 #[inline]
45 pub(crate) fn take() -> Self {
46 critical_section::with(Self::take_with_cs)
47 }
48 #[doc = r"Returns all the peripherals *once*"]
49 #[inline]
50 pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self {
51 #[no_mangle]
52 static mut _EMBASSY_DEVICE_PERIPHERALS2: bool = false; // ALIGN: Temporary fix to use stub Peripherals
53 unsafe {
54 if _EMBASSY_DEVICE_PERIPHERALS2 {
55 panic!("init called more than once!")
56 }
57 _EMBASSY_DEVICE_PERIPHERALS2 = true;
58 Self::steal()
59 }
60 }
61 }
62 impl Peripherals {
63 #[doc = r" Unsafely create an instance of this peripheral out of thin air."]
64 #[doc = r""]
65 #[doc = r" # Safety"]
66 #[doc = r""]
67 #[doc = r" You must ensure that you're only using one instance of this type at a time."]
68 #[inline]
69 pub unsafe fn steal() -> Self {
70 Self {
71 LPUART2: peripherals::LPUART2::steal(),
72 PIO2_2: peripherals::PIO2_2::steal(),
73 PIO2_3: peripherals::PIO2_3::steal(),
74 }
75 }
76 }
77
78 /// Initialize HAL
79 pub fn init() -> Peripherals {
80 Peripherals::take()
81 }
82}
83
84// --- END LIB ---
85
86// --- START GPIO --- 23// --- START GPIO ---
87 24
88mod gpio { 25mod gpio {
@@ -101,13 +38,13 @@ mod gpio {
101 macro_rules! impl_gpio_pin { 38 macro_rules! impl_gpio_pin {
102 ($($pin:ident),*) => { 39 ($($pin:ident),*) => {
103 $( 40 $(
104 impl SealedPin for super::lib::peripherals::$pin {} 41 impl SealedPin for crate::peripherals::$pin {}
105 42
106 impl GpioPin for super::lib::peripherals::$pin {} 43 impl GpioPin for crate::peripherals::$pin {}
107 44
108 impl From<super::lib::peripherals::$pin> for AnyPin { 45 impl From<crate::peripherals::$pin> for AnyPin {
109 // TODO: AJM: any reason we aren't using $pin? 46 // TODO: AJM: any reason we aren't using $pin?
110 fn from(_val: super::lib::peripherals::$pin) -> Self { 47 fn from(_val: crate::peripherals::$pin) -> Self {
111 AnyPin 48 AnyPin
112 } 49 }
113 } 50 }
@@ -143,18 +80,6 @@ use dma::Channel;
143 80
144// --- END DMA --- 81// --- END DMA ---
145 82
146// --- START CLOCK ---
147mod clock {
148 #[derive(Debug, Clone, Copy)]
149 pub enum Clock {
150 FroLf, // Low-Frequency Free-Running Oscillator
151 }
152}
153
154use clock::Clock;
155
156// --- END CLOCK ---
157
158// ============================================================================ 83// ============================================================================
159// MISC 84// MISC
160// ============================================================================ 85// ============================================================================
@@ -182,7 +107,8 @@ pub struct Info {
182 107
183/// Trait for LPUART peripheral instances 108/// Trait for LPUART peripheral instances
184#[allow(private_bounds)] 109#[allow(private_bounds)]
185pub trait Instance: SealedInstance + PeripheralType + 'static + Send { 110pub trait Instance: SealedInstance + PeripheralType + 'static + Send + Gate<MrccPeriphConfig = LpuartConfig> {
111 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance;
186 type Interrupt: interrupt::typelevel::Interrupt; 112 type Interrupt: interrupt::typelevel::Interrupt;
187} 113}
188 114
@@ -190,7 +116,7 @@ macro_rules! impl_instance {
190 ($($n:expr),*) => { 116 ($($n:expr),*) => {
191 $( 117 $(
192 paste!{ 118 paste!{
193 impl SealedInstance for lib::peripherals::[<LPUART $n>] { 119 impl SealedInstance for crate::peripherals::[<LPUART $n>] {
194 fn info() -> Info { 120 fn info() -> Info {
195 Info { 121 Info {
196 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() }, 122 regs: unsafe { &*pac::[<Lpuart $n>]::ptr() },
@@ -208,7 +134,9 @@ macro_rules! impl_instance {
208 } 134 }
209 } 135 }
210 136
211 impl Instance for lib::peripherals::[<LPUART $n>] { 137 impl Instance for crate::peripherals::[<LPUART $n>] {
138 const CLOCK_INSTANCE: crate::clocks::periph_helpers::LpuartInstance
139 = crate::clocks::periph_helpers::LpuartInstance::[<Lpuart $n>];
212 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>]; 140 type Interrupt = crate::interrupt::typelevel::[<LPUART $n>];
213 } 141 }
214 } 142 }
@@ -236,8 +164,7 @@ pub fn disable_transceiver(regs: Regs) {
236} 164}
237 165
238/// Calculate and configure baudrate settings 166/// Calculate and configure baudrate settings
239pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock: Clock) -> Result<()> { 167pub fn configure_baudrate(regs: Regs, baudrate_bps: u32, clock_freq: u32) -> Result<()> {
240 let clock_freq = get_fc_freq(clock)?;
241 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?; 168 let (osr, sbr) = calculate_baudrate(baudrate_bps, clock_freq)?;
242 169
243 // Configure BAUD register 170 // Configure BAUD register
@@ -408,16 +335,6 @@ pub fn calculate_baudrate(baudrate: u32, src_clock_hz: u32) -> Result<(u8, u16)>
408 Ok((osr, sbr)) 335 Ok((osr, sbr))
409} 336}
410 337
411pub fn get_fc_freq(clock: Clock) -> Result<u32> {
412 // This is a placeholder - actual implementation would query the clock system
413 // In real implementation, this would get the LPUART clock frequency
414 match clock {
415 Clock::FroLf => Ok(12_000_000), // Low frequency oscillator
416 #[allow(unreachable_patterns)]
417 _ => Err(Error::InvalidArgument),
418 }
419}
420
421/// Wait for all transmit operations to complete 338/// Wait for all transmit operations to complete
422pub fn wait_for_tx_complete(regs: Regs) { 339pub fn wait_for_tx_complete(regs: Regs) {
423 // Wait for TX FIFO to empty 340 // Wait for TX FIFO to empty
@@ -504,7 +421,7 @@ macro_rules! impl_pin_trait {
504 ($fcn:ident, $mode:ident, $($pin:ident, $alt:ident),*) => { 421 ($fcn:ident, $mode:ident, $($pin:ident, $alt:ident),*) => {
505 paste! { 422 paste! {
506 $( 423 $(
507 impl [<$mode:camel Pin>]<lib::peripherals::$fcn> for lib::peripherals::$pin { 424 impl [<$mode:camel Pin>]<crate::peripherals::$fcn> for crate::peripherals::$pin {
508 fn [<as_ $mode>](&self) { 425 fn [<as_ $mode>](&self) {
509 let _alt = gpio::Alt::$alt; 426 let _alt = gpio::Alt::$alt;
510 // todo!("Configure pin for LPUART function") 427 // todo!("Configure pin for LPUART function")
@@ -553,6 +470,8 @@ pub enum Error {
553 TxFifoFull, 470 TxFifoFull,
554 /// TX Busy 471 /// TX Busy
555 TxBusy, 472 TxBusy,
473 /// Clock Error
474 ClockSetup(ClockError),
556} 475}
557 476
558/// A specialized Result type for LPUART operations 477/// A specialized Result type for LPUART operations
@@ -565,10 +484,14 @@ pub type Result<T> = core::result::Result<T, Error>;
565/// Lpuart config 484/// Lpuart config
566#[derive(Debug, Clone, Copy)] 485#[derive(Debug, Clone, Copy)]
567pub struct Config { 486pub struct Config {
487 /// Power state required for this peripheral
488 pub power: PoweredClock,
489 /// Clock source
490 pub source: LpuartClockSel,
491 /// Clock divisor
492 pub div: Div4,
568 /// Baud rate in bits per second 493 /// Baud rate in bits per second
569 pub baudrate_bps: u32, 494 pub baudrate_bps: u32,
570 /// Clock
571 pub clock: Clock,
572 /// Parity configuration 495 /// Parity configuration
573 pub parity_mode: Option<Parity>, 496 pub parity_mode: Option<Parity>,
574 /// Number of data bits 497 /// Number of data bits
@@ -605,7 +528,6 @@ impl Default for Config {
605 fn default() -> Self { 528 fn default() -> Self {
606 Self { 529 Self {
607 baudrate_bps: 115_200u32, 530 baudrate_bps: 115_200u32,
608 clock: Clock::FroLf,
609 parity_mode: None, 531 parity_mode: None,
610 data_bits_count: DataBits::Data8, 532 data_bits_count: DataBits::Data8,
611 msb_firs: MsbFirst::LsbFirst, 533 msb_firs: MsbFirst::LsbFirst,
@@ -621,6 +543,9 @@ impl Default for Config {
621 enable_tx: false, 543 enable_tx: false,
622 enable_rx: false, 544 enable_rx: false,
623 swap_txd_rxd: false, 545 swap_txd_rxd: false,
546 power: PoweredClock::NormalEnabledDeepSleepDisabled,
547 source: LpuartClockSel::FroLfDiv,
548 div: Div4::no_div(),
624 } 549 }
625 } 550 }
626} 551}
@@ -706,10 +631,19 @@ impl<'a, M: Mode> Lpuart<'a, M> {
706 ) -> Result<()> { 631 ) -> Result<()> {
707 let regs = T::info().regs; 632 let regs = T::info().regs;
708 633
634 // Enable clocks
635 let conf = LpuartConfig {
636 power: config.power,
637 source: config.source,
638 div: config.div,
639 instance: T::CLOCK_INSTANCE,
640 };
641 let clock_freq = unsafe { enable_and_reset::<T>(&conf).map_err(Error::ClockSetup)? };
642
709 // Perform initialization sequence 643 // Perform initialization sequence
710 perform_software_reset(regs); 644 perform_software_reset(regs);
711 disable_transceiver(regs); 645 disable_transceiver(regs);
712 configure_baudrate(regs, config.baudrate_bps, config.clock)?; 646 configure_baudrate(regs, config.baudrate_bps, clock_freq)?;
713 configure_frame_format(regs, &config); 647 configure_frame_format(regs, &config);
714 configure_control_settings(regs, &config); 648 configure_control_settings(regs, &config);
715 configure_fifo(regs, &config); 649 configure_fifo(regs, &config);
@@ -838,6 +772,10 @@ impl<'a> LpuartTx<'a, Blocking> {
838 Ok(()) 772 Ok(())
839 } 773 }
840 774
775 pub fn write_str_blocking(&mut self, buf: &str) {
776 let _ = self.blocking_write(buf.as_bytes());
777 }
778
841 /// Write data to LPUART TX without blocking. 779 /// Write data to LPUART TX without blocking.
842 pub fn write(&mut self, buf: &[u8]) -> Result<()> { 780 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
843 for x in buf { 781 for x in buf {
@@ -967,6 +905,22 @@ impl<'a> Lpuart<'a, Blocking> {
967 self.tx.blocking_write(buf) 905 self.tx.blocking_write(buf)
968 } 906 }
969 907
908 pub fn write_byte(&mut self, byte: u8) {
909 _ = self.tx.write_byte(byte);
910 }
911
912 pub fn read_byte_blocking(&mut self) -> u8 {
913 loop {
914 if let Ok(b) = self.rx.read_byte() {
915 return b;
916 }
917 }
918 }
919
920 pub fn write_str_blocking(&mut self, buf: &str) {
921 self.tx.write_str_blocking(buf);
922 }
923
970 /// Write data to LPUART TX without blocking 924 /// Write data to LPUART TX without blocking
971 pub fn write(&mut self, buf: &[u8]) -> Result<()> { 925 pub fn write(&mut self, buf: &[u8]) -> Result<()> {
972 self.tx.write(buf) 926 self.tx.write(buf)
diff --git a/src/ostimer.rs b/src/ostimer.rs
index 8bc68389a..cd5451b53 100644
--- a/src/ostimer.rs
+++ b/src/ostimer.rs
@@ -29,8 +29,13 @@
29 29
30use core::sync::atomic::{AtomicBool, Ordering}; 30use core::sync::atomic::{AtomicBool, Ordering};
31 31
32use embassy_hal_internal::{Peri, PeripheralType};
33
34use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
35use crate::clocks::{assert_reset, enable_and_reset, is_reset_released, release_reset, Gate, PoweredClock};
32use crate::interrupt::InterruptExt; 36use crate::interrupt::InterruptExt;
33use crate::pac; 37use crate::pac;
38use crate::peripherals::OSTIMER0;
34 39
35// PAC defines the shared RegisterBlock under `ostimer0`. 40// PAC defines the shared RegisterBlock under `ostimer0`.
36type Regs = pac::ostimer0::RegisterBlock; 41type Regs = pac::ostimer0::RegisterBlock;
@@ -197,16 +202,16 @@ impl<'d> Alarm<'d> {
197pub struct Config { 202pub struct Config {
198 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag. 203 /// Initialize MATCH registers to their max values and mask/clear the interrupt flag.
199 pub init_match_max: bool, 204 pub init_match_max: bool,
200 /// OSTIMER clock frequency in Hz (must match the actual hardware clock) 205 pub power: PoweredClock,
201 pub clock_frequency_hz: u64, 206 pub source: OstimerClockSel,
202} 207}
203 208
204impl Default for Config { 209impl Default for Config {
205 fn default() -> Self { 210 fn default() -> Self {
206 Self { 211 Self {
207 init_match_max: true, 212 init_match_max: true,
208 // Default to 1MHz - user should override this with actual frequency 213 power: PoweredClock::NormalEnabledDeepSleepDisabled,
209 clock_frequency_hz: 1_000_000, 214 source: OstimerClockSel::Clk1M,
210 } 215 }
211 } 216 }
212} 217}
@@ -222,8 +227,16 @@ impl<'d, I: Instance> Ostimer<'d, I> {
222 /// Construct OSTIMER handle. 227 /// Construct OSTIMER handle.
223 /// Requires clocks for the instance to be enabled by the board before calling. 228 /// Requires clocks for the instance to be enabled by the board before calling.
224 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation. 229 /// Does not enable NVIC or INTENA; use time_driver::init() for async operation.
225 pub fn new(_inst: impl Instance, cfg: Config, _p: &'d crate::pac::Peripherals) -> Self { 230 pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self {
226 assert!(cfg.clock_frequency_hz > 0, "OSTIMER frequency must be greater than 0"); 231 let clock_freq = unsafe {
232 enable_and_reset::<I>(&OsTimerConfig {
233 power: cfg.power,
234 source: cfg.source,
235 })
236 .expect("Enabling OsTimer clock should not fail")
237 };
238
239 assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0");
227 240
228 if cfg.init_match_max { 241 if cfg.init_match_max {
229 let r: &Regs = unsafe { &*I::ptr() }; 242 let r: &Regs = unsafe { &*I::ptr() };
@@ -233,7 +246,7 @@ impl<'d, I: Instance> Ostimer<'d, I> {
233 246
234 Self { 247 Self {
235 _inst: core::marker::PhantomData, 248 _inst: core::marker::PhantomData,
236 clock_frequency_hz: cfg.clock_frequency_hz, 249 clock_frequency_hz: clock_freq as u64,
237 _phantom: core::marker::PhantomData, 250 _phantom: core::marker::PhantomData,
238 } 251 }
239 } 252 }
@@ -260,7 +273,7 @@ impl<'d, I: Instance> Ostimer<'d, I> {
260 /// # Safety 273 /// # Safety
261 /// This operation will reset the entire OSTIMER peripheral. Any active alarms 274 /// This operation will reset the entire OSTIMER peripheral. Any active alarms
262 /// or time_driver operations will be disrupted. Use with caution. 275 /// or time_driver operations will be disrupted. Use with caution.
263 pub fn reset(&self, peripherals: &crate::pac::Peripherals) { 276 pub fn reset(&self, _peripherals: &crate::pac::Peripherals) {
264 critical_section::with(|_| { 277 critical_section::with(|_| {
265 let r: &Regs = unsafe { &*I::ptr() }; 278 let r: &Regs = unsafe { &*I::ptr() };
266 279
@@ -270,19 +283,17 @@ impl<'d, I: Instance> Ostimer<'d, I> {
270 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); 283 .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit());
271 284
272 unsafe { 285 unsafe {
273 crate::reset::assert::<crate::reset::line::Ostimer0>(peripherals); 286 assert_reset::<OSTIMER0>();
274 }
275 287
276 for _ in 0..RESET_STABILIZE_SPINS { 288 for _ in 0..RESET_STABILIZE_SPINS {
277 cortex_m::asm::nop(); 289 cortex_m::asm::nop();
278 } 290 }
279 291
280 unsafe { 292 release_reset::<OSTIMER0>();
281 crate::reset::release::<crate::reset::line::Ostimer0>(peripherals);
282 }
283 293
284 while !<crate::reset::line::Ostimer0 as crate::reset::ResetLine>::is_released(&peripherals.mrcc0) { 294 while !is_reset_released::<OSTIMER0>() {
285 cortex_m::asm::nop(); 295 cortex_m::asm::nop();
296 }
286 } 297 }
287 298
288 for _ in 0..RESET_STABILIZE_SPINS { 299 for _ in 0..RESET_STABILIZE_SPINS {
@@ -469,14 +480,13 @@ fn now_ticks_read() -> u64 {
469 // Read high then low to minimize incoherent snapshots 480 // Read high then low to minimize incoherent snapshots
470 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64); 481 let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64);
471 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64; 482 let lo = r.evtimerl().read().evtimer_count_value().bits() as u64;
472
473 // Combine and convert from Gray code to binary 483 // Combine and convert from Gray code to binary
474 let gray = lo | (hi << EVTIMER_HI_SHIFT); 484 let gray = lo | (hi << EVTIMER_HI_SHIFT);
475 gray_to_bin(gray) 485 gray_to_bin(gray)
476} 486}
477 487
478// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance 488// Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance
479pub trait Instance { 489pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType {
480 fn ptr() -> *const Regs; 490 fn ptr() -> *const Regs;
481} 491}
482 492
@@ -491,12 +501,12 @@ impl Instance for crate::peripherals::OSTIMER0 {
491} 501}
492 502
493// Also implement Instance for the Peri wrapper type 503// Also implement Instance for the Peri wrapper type
494impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> { 504// impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> {
495 #[inline(always)] 505// #[inline(always)]
496 fn ptr() -> *const Regs { 506// fn ptr() -> *const Regs {
497 pac::Ostimer0::ptr() 507// pac::Ostimer0::ptr()
498 } 508// }
499} 509// }
500 510
501#[inline(always)] 511#[inline(always)]
502fn bin_to_gray(x: u64) -> u64 { 512fn bin_to_gray(x: u64) -> u64 {
@@ -524,7 +534,10 @@ pub mod time_driver {
524 bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, 534 bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME,
525 EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, 535 EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK,
526 }; 536 };
537 use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel};
538 use crate::clocks::{enable_and_reset, PoweredClock};
527 use crate::pac; 539 use crate::pac;
540 use crate::peripherals::OSTIMER0;
528 pub struct Driver; 541 pub struct Driver;
529 static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); 542 static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
530 543
@@ -611,6 +624,14 @@ pub mod time_driver {
611 /// Note: The frequency parameter is currently accepted for API compatibility. 624 /// Note: The frequency parameter is currently accepted for API compatibility.
612 /// The embassy_time_driver macro handles driver registration automatically. 625 /// The embassy_time_driver macro handles driver registration automatically.
613 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { 626 pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) {
627 let _clock_freq = unsafe {
628 enable_and_reset::<OSTIMER0>(&OsTimerConfig {
629 power: PoweredClock::AlwaysEnabled,
630 source: OstimerClockSel::Clk1M,
631 })
632 .expect("Enabling OsTimer clock should not fail")
633 };
634
614 // Mask/clear at peripheral and set default MATCH 635 // Mask/clear at peripheral and set default MATCH
615 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; 636 let r: &Regs = unsafe { &*pac::Ostimer0::ptr() };
616 super::prime_match_registers(r); 637 super::prime_match_registers(r);
diff --git a/src/reset.rs b/src/reset.rs
deleted file mode 100644
index 1c131d1cc..000000000
--- a/src/reset.rs
+++ /dev/null
@@ -1,112 +0,0 @@
1//! Reset control helpers built on PAC field writers.
2use crate::pac;
3
4/// Trait describing a reset line that can be asserted/deasserted.
5pub trait ResetLine {
6 /// Drive the peripheral out of reset.
7 unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock);
8
9 /// Drive the peripheral into reset.
10 unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock);
11
12 /// Check whether the peripheral is currently released.
13 fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool;
14}
15
16/// Release a reset line for the given peripheral set.
17#[inline]
18pub unsafe fn release<R: ResetLine>(peripherals: &pac::Peripherals) {
19 R::release(&peripherals.mrcc0);
20}
21
22/// Assert a reset line for the given peripheral set.
23#[inline]
24pub unsafe fn assert<R: ResetLine>(peripherals: &pac::Peripherals) {
25 R::assert(&peripherals.mrcc0);
26}
27
28/// Pulse a reset line (assert then release) with a short delay.
29#[inline]
30pub unsafe fn pulse<R: ResetLine>(peripherals: &pac::Peripherals) {
31 let mrcc = &peripherals.mrcc0;
32 R::assert(mrcc);
33 cortex_m::asm::nop();
34 cortex_m::asm::nop();
35 R::release(mrcc);
36}
37
38macro_rules! impl_reset_line {
39 ($name:ident, $reg:ident, $field:ident) => {
40 pub struct $name;
41
42 impl ResetLine for $name {
43 #[inline]
44 unsafe fn release(mrcc: &pac::mrcc0::RegisterBlock) {
45 mrcc.$reg().modify(|_, w| w.$field().enabled());
46 }
47
48 #[inline]
49 unsafe fn assert(mrcc: &pac::mrcc0::RegisterBlock) {
50 mrcc.$reg().modify(|_, w| w.$field().disabled());
51 }
52
53 #[inline]
54 fn is_released(mrcc: &pac::mrcc0::RegisterBlock) -> bool {
55 mrcc.$reg().read().$field().is_enabled()
56 }
57 }
58 };
59}
60
61pub mod line {
62 use super::*;
63
64 impl_reset_line!(Port2, mrcc_glb_rst1, port2);
65 impl_reset_line!(Port3, mrcc_glb_rst1, port3);
66 impl_reset_line!(Gpio3, mrcc_glb_rst2, gpio3);
67 impl_reset_line!(Lpuart2, mrcc_glb_rst0, lpuart2);
68 impl_reset_line!(Ostimer0, mrcc_glb_rst1, ostimer0);
69 impl_reset_line!(Port1, mrcc_glb_rst1, port1);
70 impl_reset_line!(Adc1, mrcc_glb_rst1, adc1);
71}
72
73#[inline]
74pub unsafe fn release_reset_port2(peripherals: &pac::Peripherals) {
75 release::<line::Port2>(peripherals);
76}
77
78#[inline]
79pub unsafe fn release_reset_port3(peripherals: &pac::Peripherals) {
80 release::<line::Port3>(peripherals);
81}
82
83#[inline]
84pub unsafe fn release_reset_gpio3(peripherals: &pac::Peripherals) {
85 release::<line::Gpio3>(peripherals);
86}
87
88#[inline]
89pub unsafe fn release_reset_lpuart2(peripherals: &pac::Peripherals) {
90 release::<line::Lpuart2>(peripherals);
91}
92
93#[inline]
94pub unsafe fn release_reset_ostimer0(peripherals: &pac::Peripherals) {
95 release::<line::Ostimer0>(peripherals);
96}
97
98/// Convenience shim retained for existing call sites.
99#[inline]
100pub unsafe fn reset_ostimer0(peripherals: &pac::Peripherals) {
101 pulse::<line::Ostimer0>(peripherals);
102}
103
104#[inline]
105pub unsafe fn release_reset_port1(peripherals: &pac::Peripherals) {
106 release::<line::Port1>(peripherals);
107}
108
109#[inline]
110pub unsafe fn release_reset_adc1(peripherals: &pac::Peripherals) {
111 release::<line::Adc1>(peripherals);
112}
diff --git a/src/rtc.rs b/src/rtc.rs
index facb9cf8c..b750a97ea 100644
--- a/src/rtc.rs
+++ b/src/rtc.rs
@@ -1,6 +1,9 @@
1//! RTC DateTime driver. 1//! RTC DateTime driver.
2use core::sync::atomic::{AtomicBool, Ordering}; 2use core::sync::atomic::{AtomicBool, Ordering};
3 3
4use embassy_hal_internal::{Peri, PeripheralType};
5
6use crate::clocks::with_clocks;
4use crate::pac; 7use crate::pac;
5use crate::pac::rtc0::cr::Um; 8use crate::pac::rtc0::cr::Um;
6 9
@@ -9,7 +12,7 @@ type Regs = pac::rtc0::RegisterBlock;
9static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false); 12static ALARM_TRIGGERED: AtomicBool = AtomicBool::new(false);
10 13
11// Token-based instance pattern like embassy-imxrt 14// Token-based instance pattern like embassy-imxrt
12pub trait Instance { 15pub trait Instance: PeripheralType {
13 fn ptr() -> *const Regs; 16 fn ptr() -> *const Regs;
14} 17}
15 18
@@ -22,14 +25,6 @@ impl Instance for crate::peripherals::RTC0 {
22 } 25 }
23} 26}
24 27
25// Also implement Instance for the Peri wrapper type
26impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::RTC0> {
27 #[inline(always)]
28 fn ptr() -> *const Regs {
29 pac::Rtc0::ptr()
30 }
31}
32
33const DAYS_IN_A_YEAR: u32 = 365; 28const DAYS_IN_A_YEAR: u32 = 365;
34const SECONDS_IN_A_DAY: u32 = 86400; 29const SECONDS_IN_A_DAY: u32 = 86400;
35const SECONDS_IN_A_HOUR: u32 = 3600; 30const SECONDS_IN_A_HOUR: u32 = 3600;
@@ -157,15 +152,24 @@ pub fn get_default_config() -> RtcConfig {
157 } 152 }
158} 153}
159/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy) 154/// Minimal RTC handle for a specific instance I (store the zero-sized token like embassy)
160pub struct Rtc<I: Instance> { 155pub struct Rtc<'a, I: Instance> {
161 _inst: core::marker::PhantomData<I>, 156 _inst: core::marker::PhantomData<&'a mut I>,
162} 157}
163 158
164impl<I: Instance> Rtc<I> { 159impl<'a, I: Instance> Rtc<'a, I> {
165 /// initialize RTC 160 /// initialize RTC
166 pub fn new(_inst: impl Instance, config: RtcConfig) -> Self { 161 pub fn new(_inst: Peri<'a, I>, config: RtcConfig) -> Self {
167 let rtc = unsafe { &*I::ptr() }; 162 let rtc = unsafe { &*I::ptr() };
168 163
164 // The RTC is NOT gated by the MRCC, but we DO need to make sure the 16k clock
165 // on the vsys domain is active
166 let clocks = with_clocks(|c| c.clk_16k_vsys.clone());
167 match clocks {
168 None => panic!("Clocks have not been initialized"),
169 Some(None) => panic!("Clocks initialized, but clk_16k_vsys not active"),
170 Some(Some(_)) => {}
171 }
172
169 /* RTC reset */ 173 /* RTC reset */
170 rtc.cr().modify(|_, w| w.swr().set_bit()); 174 rtc.cr().modify(|_, w| w.swr().set_bit());
171 rtc.cr().modify(|_, w| w.swr().clear_bit()); 175 rtc.cr().modify(|_, w| w.swr().clear_bit());
diff --git a/src/uart.rs b/src/uart.rs
deleted file mode 100644
index 3705959d3..000000000
--- a/src/uart.rs
+++ /dev/null
@@ -1,316 +0,0 @@
1//! Minimal polling UART2 bring-up replicating MCUXpresso hello_world ordering.
2//! WARNING: This is a narrow implementation only for debug console (115200 8N1).
3
4// TODO(AJM): As of 2025-11-13, we need to do a pass to ensure safety docs
5// are complete prior to release.
6#![allow(clippy::missing_safety_doc)]
7
8use core::cell::RefCell;
9
10use cortex_m::interrupt::Mutex;
11use embassy_sync::signal::Signal;
12
13use crate::pac;
14
15// svd2rust defines the shared LPUART RegisterBlock under lpuart0; all instances reuse it.
16type Regs = pac::lpuart0::RegisterBlock;
17
18// Token-based instance pattern like embassy-imxrt
19pub trait Instance {
20 fn ptr() -> *const Regs;
21}
22
23/// Token for LPUART2 provided by embassy-hal-internal peripherals macro.
24pub type Lpuart2 = crate::peripherals::LPUART2;
25impl Instance for crate::peripherals::LPUART2 {
26 #[inline(always)]
27 fn ptr() -> *const Regs {
28 pac::Lpuart2::ptr()
29 }
30}
31
32// Also implement Instance for the Peri wrapper type
33impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::LPUART2> {
34 #[inline(always)]
35 fn ptr() -> *const Regs {
36 pac::Lpuart2::ptr()
37 }
38}
39
40/// UART configuration (explicit src_hz; no hardcoded frequencies)
41#[derive(Copy, Clone)]
42pub struct Config {
43 pub src_hz: u32,
44 pub baud: u32,
45 pub parity: Parity,
46 pub stop_bits: StopBits,
47}
48
49#[derive(Copy, Clone)]
50pub enum Parity {
51 None,
52 Even,
53 Odd,
54}
55#[derive(Copy, Clone)]
56pub enum StopBits {
57 One,
58 Two,
59}
60
61impl Config {
62 pub fn new(src_hz: u32) -> Self {
63 Self {
64 src_hz,
65 baud: 115_200,
66 parity: Parity::None,
67 stop_bits: StopBits::One,
68 }
69 }
70}
71
72/// Compute a valid (OSR, SBR) tuple for given source clock and baud.
73/// Uses a functional fold approach to find the best OSR/SBR combination
74/// with minimal baud rate error.
75fn compute_osr_sbr(src_hz: u32, baud: u32) -> (u8, u16) {
76 let (best_osr, best_sbr, _best_err) = (8u32..=32).fold(
77 (16u8, 4u16, u32::MAX), // (best_osr, best_sbr, best_err)
78 |(best_osr, best_sbr, best_err), osr| {
79 let denom = baud.saturating_mul(osr);
80 if denom == 0 {
81 return (best_osr, best_sbr, best_err);
82 }
83
84 let sbr = (src_hz + denom / 2) / denom; // round
85 if sbr == 0 || sbr > 0x1FFF {
86 return (best_osr, best_sbr, best_err);
87 }
88
89 let actual = src_hz / (osr * sbr);
90 let err = actual.abs_diff(baud);
91
92 // Update best if this is better, or same error but higher OSR
93 if err < best_err || (err == best_err && osr as u8 > best_osr) {
94 (osr as u8, sbr as u16, err)
95 } else {
96 (best_osr, best_sbr, best_err)
97 }
98 },
99 );
100 (best_osr, best_sbr)
101}
102
103/// Minimal UART handle for a specific instance I (store the zero-sized token like embassy)
104pub struct Uart<I: Instance> {
105 _inst: core::marker::PhantomData<I>,
106}
107
108impl<I: Instance> Uart<I> {
109 /// Create and initialize LPUART (reset + config). Clocks and pins must be prepared by the caller.
110 pub fn new(_inst: impl Instance, cfg: Config) -> Self {
111 let l = unsafe { &*I::ptr() };
112 // 1) software reset pulse
113 l.global().write(|w| w.rst().reset());
114 cortex_m::asm::delay(3); // Short delay for reset to take effect
115 l.global().write(|w| w.rst().no_effect());
116 cortex_m::asm::delay(10); // Allow peripheral to stabilize after reset
117 // 2) BAUD
118 let (osr, sbr) = compute_osr_sbr(cfg.src_hz, cfg.baud);
119 l.baud().modify(|_, w| {
120 let w = match cfg.stop_bits {
121 StopBits::One => w.sbns().one(),
122 StopBits::Two => w.sbns().two(),
123 };
124 // OSR field encodes (osr-1); use raw bits to avoid a long match on all variants
125 let raw_osr = osr.saturating_sub(1);
126 unsafe { w.osr().bits(raw_osr).sbr().bits(sbr) }
127 });
128 // 3) CTRL baseline and parity
129 l.ctrl().write(|w| {
130 let w = w.ilt().from_stop().idlecfg().idle_2();
131 let w = match cfg.parity {
132 Parity::None => w.pe().disabled(),
133 Parity::Even => w.pe().enabled().pt().even(),
134 Parity::Odd => w.pe().enabled().pt().odd(),
135 };
136 w.re().enabled().te().enabled().rie().disabled()
137 });
138 // 4) FIFOs and WATER: keep it simple for polling; disable FIFOs and set RX watermark to 0
139 l.fifo().modify(|_, w| {
140 w.txfe()
141 .disabled()
142 .rxfe()
143 .disabled()
144 .txflush()
145 .txfifo_rst()
146 .rxflush()
147 .rxfifo_rst()
148 });
149 l.water()
150 .modify(|_, w| unsafe { w.txwater().bits(0).rxwater().bits(0) });
151 Self {
152 _inst: core::marker::PhantomData,
153 }
154 }
155
156 /// Enable RX interrupts. The caller must ensure an appropriate IRQ handler is installed.
157 pub unsafe fn enable_rx_interrupts(&self) {
158 let l = &*I::ptr();
159 l.ctrl().modify(|_, w| w.rie().enabled());
160 }
161
162 #[inline(never)]
163 pub fn write_byte(&self, b: u8) {
164 let l = unsafe { &*I::ptr() };
165 // Timeout after ~10ms at 12MHz (assuming 115200 baud, should be plenty)
166 const DATA_OFFSET: usize = 0x1C; // DATA register offset inside LPUART block
167 let data_ptr = unsafe { (I::ptr() as *mut u8).add(DATA_OFFSET) };
168 for _ in 0..120000 {
169 if l.water().read().txcount().bits() == 0 {
170 unsafe { core::ptr::write_volatile(data_ptr, b) };
171 return;
172 }
173 }
174 // If timeout, skip the write to avoid hanging
175 }
176
177 #[inline(never)]
178 pub fn write_str_blocking(&self, s: &str) {
179 for &b in s.as_bytes() {
180 if b == b'\n' {
181 self.write_byte(b'\r');
182 }
183 self.write_byte(b);
184 }
185 }
186 pub fn read_byte_blocking(&self) -> u8 {
187 let l = unsafe { &*I::ptr() };
188 while !l.stat().read().rdrf().is_rxdata() {}
189 (l.data().read().bits() & 0xFF) as u8
190 }
191}
192
193// Simple ring buffer for UART RX data
194const RX_BUFFER_SIZE: usize = 256;
195pub struct RingBuffer {
196 buffer: [u8; RX_BUFFER_SIZE],
197 read_idx: usize,
198 write_idx: usize,
199 count: usize,
200}
201
202impl Default for RingBuffer {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208impl RingBuffer {
209 pub const fn new() -> Self {
210 Self {
211 buffer: [0; RX_BUFFER_SIZE],
212 read_idx: 0,
213 write_idx: 0,
214 count: 0,
215 }
216 }
217
218 pub fn push(&mut self, data: u8) -> bool {
219 if self.count >= RX_BUFFER_SIZE {
220 return false; // Buffer full
221 }
222 self.buffer[self.write_idx] = data;
223 self.write_idx = (self.write_idx + 1) % RX_BUFFER_SIZE;
224 self.count += 1;
225 true
226 }
227
228 pub fn pop(&mut self) -> Option<u8> {
229 if self.count == 0 {
230 return None;
231 }
232 let data = self.buffer[self.read_idx];
233 self.read_idx = (self.read_idx + 1) % RX_BUFFER_SIZE;
234 self.count -= 1;
235 Some(data)
236 }
237
238 pub fn is_empty(&self) -> bool {
239 self.count == 0
240 }
241
242 pub fn len(&self) -> usize {
243 self.count
244 }
245}
246
247// Global RX buffer shared between interrupt handler and UART instance
248static RX_BUFFER: Mutex<RefCell<RingBuffer>> = Mutex::new(RefCell::new(RingBuffer::new()));
249static RX_SIGNAL: Signal<embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, ()> = Signal::new();
250
251// Debug counter for interrupt handler calls
252static mut INTERRUPT_COUNT: u32 = 0;
253
254impl<I: Instance> Uart<I> {
255 /// Read a byte asynchronously using interrupts
256 pub async fn read_byte_async(&self) -> u8 {
257 loop {
258 // Check if we have data in the buffer
259 let byte = cortex_m::interrupt::free(|cs| {
260 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
261 buffer.pop()
262 });
263
264 if let Some(byte) = byte {
265 return byte;
266 }
267
268 // Wait for the interrupt signal
269 RX_SIGNAL.wait().await;
270 }
271 }
272
273 /// Check if there's data available in the RX buffer
274 pub fn rx_data_available(&self) -> bool {
275 cortex_m::interrupt::free(|cs| {
276 let buffer = RX_BUFFER.borrow(cs).borrow();
277 !buffer.is_empty()
278 })
279 }
280
281 /// Try to read a byte from RX buffer (non-blocking)
282 pub fn try_read_byte(&self) -> Option<u8> {
283 cortex_m::interrupt::free(|cs| {
284 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
285 buffer.pop()
286 })
287 }
288}
289
290/// Type-level handler for LPUART2 interrupts, compatible with bind_interrupts!.
291pub struct UartInterruptHandler;
292
293impl crate::interrupt::typelevel::Handler<crate::interrupt::typelevel::LPUART2> for UartInterruptHandler {
294 unsafe fn on_interrupt() {
295 INTERRUPT_COUNT += 1;
296
297 let lpuart = &*pac::Lpuart2::ptr();
298
299 // Check if we have RX data
300 if lpuart.stat().read().rdrf().is_rxdata() {
301 // Read the data byte
302 let data = (lpuart.data().read().bits() & 0xFF) as u8;
303
304 // Store in ring buffer
305 cortex_m::interrupt::free(|cs| {
306 let mut buffer = RX_BUFFER.borrow(cs).borrow_mut();
307 if buffer.push(data) {
308 // Data added successfully, signal waiting tasks
309 RX_SIGNAL.signal(());
310 }
311 });
312 }
313 // Always clear any error flags that might cause spurious interrupts
314 let _ = lpuart.stat().read();
315 }
316}