aboutsummaryrefslogtreecommitdiff
path: root/src/clocks
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-11-12 18:54:27 +0100
committerJames Munns <[email protected]>2025-11-12 18:54:27 +0100
commit2f08491f1a09df79bf1d41e26217d21b0bd7bdae (patch)
treec68cbb51f49aac5f8001eca81f38750ec10d9d14 /src/clocks
parenta2e63ff090d240f8dea0d448477990a89db114be (diff)
Start adding peripheral helpers
Diffstat (limited to 'src/clocks')
-rw-r--r--src/clocks/mod.rs69
-rw-r--r--src/clocks/periph_helpers.rs125
2 files changed, 187 insertions, 7 deletions
diff --git a/src/clocks/mod.rs b/src/clocks/mod.rs
index c86ed1cd5..ad48a9cf2 100644
--- a/src/clocks/mod.rs
+++ b/src/clocks/mod.rs
@@ -6,10 +6,7 @@ use mcxa_pac::scg0::{
6}; 6};
7 7
8use crate::pac; 8use crate::pac;
9 9pub mod periph_helpers;
10pub trait SPConfHelper {
11 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>;
12}
13 10
14// /// Trait describing an AHB clock gate that can be toggled through MRCC. 11// /// Trait describing an AHB clock gate that can be toggled through MRCC.
15// pub trait Gate { 12// pub trait Gate {
@@ -198,10 +195,22 @@ pub struct Clock {
198 195
199#[derive(Debug, Clone, Copy)] 196#[derive(Debug, Clone, Copy)]
200pub enum PoweredClock { 197pub enum PoweredClock {
201 HighPowerOnly, 198 NormalEnabledDeepSleepDisabled,
202 AlwaysEnabled, 199 AlwaysEnabled,
203} 200}
204 201
202impl PoweredClock {
203 /// Does THIS clock meet the power requirements of the OTHER clock?
204 pub fn meets_requirement_of(&self, other: &Self) -> bool {
205 match (self, other) {
206 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false,
207 (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
208 (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true,
209 (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true,
210 }
211 }
212}
213
205/// ```text 214/// ```text
206/// ┌─────────────────────────────────────────────────────────┐ 215/// ┌─────────────────────────────────────────────────────────┐
207/// │ │ 216/// │ │
@@ -341,6 +350,7 @@ static CLOCKS: critical_section::Mutex<Option<Clocks>> = critical_section::Mutex
341pub enum ClockError { 350pub enum ClockError {
342 AlreadyInitialized, 351 AlreadyInitialized,
343 BadConfig { clock: &'static str, reason: &'static str }, 352 BadConfig { clock: &'static str, reason: &'static str },
353 NotImplemented { clock: &'static str },
344} 354}
345 355
346struct ClockOperator<'a> { 356struct ClockOperator<'a> {
@@ -417,7 +427,7 @@ impl ClockOperator<'_> {
417 427
418 // When is the FRO enabled? 428 // When is the FRO enabled?
419 let pow_set = match power { 429 let pow_set = match power {
420 PoweredClock::HighPowerOnly => Fircsten::DisabledInStopModes, 430 PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes,
421 PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes, 431 PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes,
422 }; 432 };
423 433
@@ -505,7 +515,7 @@ impl ClockOperator<'_> {
505 }); 515 });
506 516
507 let deep = match power { 517 let deep = match power {
508 PoweredClock::HighPowerOnly => Sircsten::Disabled, 518 PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled,
509 PoweredClock::AlwaysEnabled => Sircsten::Enabled, 519 PoweredClock::AlwaysEnabled => Sircsten::Enabled,
510 }; 520 };
511 let pclk = if *fro_12m_enabled { 521 let pclk = if *fro_12m_enabled {
@@ -615,3 +625,48 @@ pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
615 Some(f(c)) 625 Some(f(c))
616 }) 626 })
617} 627}
628
629impl Clocks {
630 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
631 let Some(clk) = self.fro_lf_div.as_ref() else {
632 return Err(ClockError::BadConfig { clock: "fro_lf_div", reason: "required but not active" });
633 };
634 if !clk.power.meets_requirement_of(at_level) {
635 return Err(ClockError::BadConfig { clock: "fro_lf_div", reason: "not low power active" });
636 }
637 Ok(clk.frequency)
638 }
639
640 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
641 let Some(clk) = self.fro_hf_div.as_ref() else {
642 return Err(ClockError::BadConfig { clock: "fro_hf_div", reason: "required but not active" });
643 };
644 if !clk.power.meets_requirement_of(at_level) {
645 return Err(ClockError::BadConfig { clock: "fro_hf_div", reason: "not low power active" });
646 }
647 Ok(clk.frequency)
648 }
649
650 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
651 Err(ClockError::NotImplemented { clock: "clk_in" })
652 }
653
654 pub fn ensure_clk_16k_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
655 Err(ClockError::NotImplemented { clock: "clk_16k" })
656 }
657
658 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
659 let Some(clk) = self.clk_1m.as_ref() else {
660 return Err(ClockError::BadConfig { clock: "clk_1m", reason: "required but not active" });
661 };
662 if !clk.power.meets_requirement_of(at_level) {
663 return Err(ClockError::BadConfig { clock: "clk_1m", reason: "not low power active" });
664 }
665 Ok(clk.frequency)
666 }
667
668 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> {
669 Err(ClockError::NotImplemented { clock: "pll1_clk_div" })
670 }
671
672}
diff --git a/src/clocks/periph_helpers.rs b/src/clocks/periph_helpers.rs
new file mode 100644
index 000000000..c4d4e9e3b
--- /dev/null
+++ b/src/clocks/periph_helpers.rs
@@ -0,0 +1,125 @@
1use super::{ClockError, Clocks, Div8, PoweredClock};
2use crate::pac;
3
4pub trait SPConfHelper {
5 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>;
6}
7
8// config types
9
10pub enum LpuartClockSel {
11 /// FRO12M/FRO_LF/SIRC clock source, passed through divider
12 /// "fro_lf_div"
13 FroLfDiv,
14 /// FRO180M/FRO_HF/FIRC clock source, passed through divider
15 /// "fro_hf_div"
16 FroHfDiv,
17 /// SOSC/XTAL/EXTAL clock source
18 ClkIn,
19 /// FRO16K/clk_16k source
20 Clk16K,
21 /// clk_1m/FRO_LF divided by 12
22 Clk1M,
23 /// Output of PLL1, passed through clock divider,
24 /// "pll1_clk_div", maybe "pll1_lf_div"?
25 Pll1ClkDiv,
26 /// Disabled
27 None,
28}
29
30pub enum LpuartInstance {
31 Lpuart0,
32 Lpuart1,
33 Lpuart2,
34 Lpuart3,
35 Lpuart4,
36 Lpuart5,
37}
38
39pub struct LpuartConfig {
40 /// Power state required for this peripheral
41 pub power: PoweredClock,
42 /// Clock source
43 pub source: LpuartClockSel,
44 /// Clock divisor
45 pub div: Div8,
46 /// Which instance is this?
47 // NOTE: should not be user settable
48 instance: LpuartInstance,
49}
50
51// impls
52
53impl SPConfHelper for LpuartConfig {
54 fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError> {
55 // check that source is suitable
56 let mrcc0 = unsafe { pac::Mrcc0::steal() };
57 use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux;
58
59 let (clkdiv, clksel) = match self.instance {
60 LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()),
61 LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()),
62 LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()),
63 LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()),
64 LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()),
65 LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()),
66 };
67
68 let (freq, variant) = match self.source {
69 LpuartClockSel::FroLfDiv => {
70 let freq = clocks.ensure_fro_lf_div_active(&self.power)?;
71 (freq, Mux::ClkrootFunc0)
72 }
73 LpuartClockSel::FroHfDiv => {
74 let freq = clocks.ensure_fro_hf_div_active(&self.power)?;
75 (freq, Mux::ClkrootFunc2)
76 }
77 LpuartClockSel::ClkIn => {
78 let freq = clocks.ensure_clk_in_active(&self.power)?;
79 (freq, Mux::ClkrootFunc3)
80 }
81 LpuartClockSel::Clk16K => {
82 let freq = clocks.ensure_clk_16k_active(&self.power)?;
83 (freq, Mux::ClkrootFunc4)
84 }
85 LpuartClockSel::Clk1M => {
86 let freq = clocks.ensure_clk_1m_active(&self.power)?;
87 (freq, Mux::ClkrootFunc5)
88 }
89 LpuartClockSel::Pll1ClkDiv => {
90 let freq = clocks.ensure_pll1_clk_div_active(&self.power)?;
91 (freq, Mux::ClkrootFunc6)
92 }
93 LpuartClockSel::None => unsafe {
94 // no ClkrootFunc7, just write manually for now
95 clksel.write(|w| w.bits(0b111));
96 clkdiv.modify(|_r, w| {
97 w.reset().on();
98 w.halt().on();
99 w
100 });
101 return Ok(0);
102 },
103 };
104
105 // set clksel
106 clksel.modify(|_r, w| w.mux().variant(variant));
107
108 // Set up clkdiv
109 clkdiv.modify(|_r, w| {
110 w.halt().on();
111 w.reset().on();
112 w
113 });
114 clkdiv.modify(|_r, w| {
115 w.halt().off();
116 w.reset().off();
117 unsafe { w.div().bits(self.div.into_bits()) };
118 w
119 });
120
121 while clkdiv.read().unstab().is_on() {}
122
123 Ok(freq / self.div.into_divisor())
124 }
125}