diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/clocks/mod.rs | 69 | ||||
| -rw-r--r-- | src/clocks/periph_helpers.rs | 125 |
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 | ||
| 8 | use crate::pac; | 8 | use crate::pac; |
| 9 | 9 | pub mod periph_helpers; | |
| 10 | pub 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)] |
| 200 | pub enum PoweredClock { | 197 | pub enum PoweredClock { |
| 201 | HighPowerOnly, | 198 | NormalEnabledDeepSleepDisabled, |
| 202 | AlwaysEnabled, | 199 | AlwaysEnabled, |
| 203 | } | 200 | } |
| 204 | 201 | ||
| 202 | impl 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 | |||
| 341 | pub enum ClockError { | 350 | pub 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 | ||
| 346 | struct ClockOperator<'a> { | 356 | struct 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 | |||
| 629 | impl 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 @@ | |||
| 1 | use super::{ClockError, Clocks, Div8, PoweredClock}; | ||
| 2 | use crate::pac; | ||
| 3 | |||
| 4 | pub trait SPConfHelper { | ||
| 5 | fn post_enable_config(&self, clocks: &Clocks) -> Result<u32, ClockError>; | ||
| 6 | } | ||
| 7 | |||
| 8 | // config types | ||
| 9 | |||
| 10 | pub 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 | |||
| 30 | pub enum LpuartInstance { | ||
| 31 | Lpuart0, | ||
| 32 | Lpuart1, | ||
| 33 | Lpuart2, | ||
| 34 | Lpuart3, | ||
| 35 | Lpuart4, | ||
| 36 | Lpuart5, | ||
| 37 | } | ||
| 38 | |||
| 39 | pub 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 | |||
| 53 | impl 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 | } | ||
