From 2f08491f1a09df79bf1d41e26217d21b0bd7bdae Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 12 Nov 2025 18:54:27 +0100 Subject: Start adding peripheral helpers --- Cargo.toml | 2 +- src/clocks/mod.rs | 69 +++++++++++++++++++++--- src/clocks/periph_helpers.rs | 125 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 src/clocks/periph_helpers.rs diff --git a/Cargo.toml b/Cargo.toml index 8ec547494..afcb2d3c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ embassy-time = "0.5.0" embassy-time-driver = "0.2.1" embedded-io = "0.6" heapless = "0.8" -mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt"], rev = "f0281344c605ab24c38979553b41a1655c50625c", version = "0.1.0" } +mcxa-pac = { git = "https://github.com/OpenDevicePartnership/mcxa-pac", features = ["rt"], rev = "f25b7aed0b8ad8e610012046e09340926cbd51a2", version = "0.1.0" } paste = "1.0.15" embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ 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::{ }; use crate::pac; - -pub trait SPConfHelper { - fn post_enable_config(&self, clocks: &Clocks) -> Result; -} +pub mod periph_helpers; // /// Trait describing an AHB clock gate that can be toggled through MRCC. // pub trait Gate { @@ -198,10 +195,22 @@ pub struct Clock { #[derive(Debug, Clone, Copy)] pub enum PoweredClock { - HighPowerOnly, + NormalEnabledDeepSleepDisabled, AlwaysEnabled, } +impl PoweredClock { + /// Does THIS clock meet the power requirements of the OTHER clock? + pub fn meets_requirement_of(&self, other: &Self) -> bool { + match (self, other) { + (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::AlwaysEnabled) => false, + (PoweredClock::NormalEnabledDeepSleepDisabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, + (PoweredClock::AlwaysEnabled, PoweredClock::NormalEnabledDeepSleepDisabled) => true, + (PoweredClock::AlwaysEnabled, PoweredClock::AlwaysEnabled) => true, + } + } +} + /// ```text /// ┌─────────────────────────────────────────────────────────┐ /// │ │ @@ -341,6 +350,7 @@ static CLOCKS: critical_section::Mutex> = critical_section::Mutex pub enum ClockError { AlreadyInitialized, BadConfig { clock: &'static str, reason: &'static str }, + NotImplemented { clock: &'static str }, } struct ClockOperator<'a> { @@ -417,7 +427,7 @@ impl ClockOperator<'_> { // When is the FRO enabled? let pow_set = match power { - PoweredClock::HighPowerOnly => Fircsten::DisabledInStopModes, + PoweredClock::NormalEnabledDeepSleepDisabled => Fircsten::DisabledInStopModes, PoweredClock::AlwaysEnabled => Fircsten::EnabledInStopModes, }; @@ -505,7 +515,7 @@ impl ClockOperator<'_> { }); let deep = match power { - PoweredClock::HighPowerOnly => Sircsten::Disabled, + PoweredClock::NormalEnabledDeepSleepDisabled => Sircsten::Disabled, PoweredClock::AlwaysEnabled => Sircsten::Enabled, }; let pclk = if *fro_12m_enabled { @@ -615,3 +625,48 @@ pub fn with_clocks R>(f: F) -> Option { Some(f(c)) }) } + +impl Clocks { + pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result { + let Some(clk) = self.fro_lf_div.as_ref() else { + return Err(ClockError::BadConfig { clock: "fro_lf_div", reason: "required but not active" }); + }; + if !clk.power.meets_requirement_of(at_level) { + return Err(ClockError::BadConfig { clock: "fro_lf_div", reason: "not low power active" }); + } + Ok(clk.frequency) + } + + pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result { + let Some(clk) = self.fro_hf_div.as_ref() else { + return Err(ClockError::BadConfig { clock: "fro_hf_div", reason: "required but not active" }); + }; + if !clk.power.meets_requirement_of(at_level) { + return Err(ClockError::BadConfig { clock: "fro_hf_div", reason: "not low power active" }); + } + Ok(clk.frequency) + } + + pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result { + Err(ClockError::NotImplemented { clock: "clk_in" }) + } + + pub fn ensure_clk_16k_active(&self, _at_level: &PoweredClock) -> Result { + Err(ClockError::NotImplemented { clock: "clk_16k" }) + } + + pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result { + let Some(clk) = self.clk_1m.as_ref() else { + return Err(ClockError::BadConfig { clock: "clk_1m", reason: "required but not active" }); + }; + if !clk.power.meets_requirement_of(at_level) { + return Err(ClockError::BadConfig { clock: "clk_1m", reason: "not low power active" }); + } + Ok(clk.frequency) + } + + pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result { + Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) + } + +} 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 @@ +use super::{ClockError, Clocks, Div8, PoweredClock}; +use crate::pac; + +pub trait SPConfHelper { + fn post_enable_config(&self, clocks: &Clocks) -> Result; +} + +// config types + +pub enum LpuartClockSel { + /// FRO12M/FRO_LF/SIRC clock source, passed through divider + /// "fro_lf_div" + FroLfDiv, + /// FRO180M/FRO_HF/FIRC clock source, passed through divider + /// "fro_hf_div" + FroHfDiv, + /// SOSC/XTAL/EXTAL clock source + ClkIn, + /// FRO16K/clk_16k source + Clk16K, + /// clk_1m/FRO_LF divided by 12 + Clk1M, + /// Output of PLL1, passed through clock divider, + /// "pll1_clk_div", maybe "pll1_lf_div"? + Pll1ClkDiv, + /// Disabled + None, +} + +pub enum LpuartInstance { + Lpuart0, + Lpuart1, + Lpuart2, + Lpuart3, + Lpuart4, + Lpuart5, +} + +pub struct LpuartConfig { + /// Power state required for this peripheral + pub power: PoweredClock, + /// Clock source + pub source: LpuartClockSel, + /// Clock divisor + pub div: Div8, + /// Which instance is this? + // NOTE: should not be user settable + instance: LpuartInstance, +} + +// impls + +impl SPConfHelper for LpuartConfig { + fn post_enable_config(&self, clocks: &Clocks) -> Result { + // check that source is suitable + let mrcc0 = unsafe { pac::Mrcc0::steal() }; + use mcxa_pac::mrcc0::mrcc_lpuart0_clksel::Mux; + + let (clkdiv, clksel) = match self.instance { + LpuartInstance::Lpuart0 => (mrcc0.mrcc_lpuart0_clkdiv(), mrcc0.mrcc_lpuart0_clksel()), + LpuartInstance::Lpuart1 => (mrcc0.mrcc_lpuart1_clkdiv(), mrcc0.mrcc_lpuart1_clksel()), + LpuartInstance::Lpuart2 => (mrcc0.mrcc_lpuart2_clkdiv(), mrcc0.mrcc_lpuart2_clksel()), + LpuartInstance::Lpuart3 => (mrcc0.mrcc_lpuart3_clkdiv(), mrcc0.mrcc_lpuart3_clksel()), + LpuartInstance::Lpuart4 => (mrcc0.mrcc_lpuart4_clkdiv(), mrcc0.mrcc_lpuart4_clksel()), + LpuartInstance::Lpuart5 => (mrcc0.mrcc_lpuart5_clkdiv(), mrcc0.mrcc_lpuart5_clksel()), + }; + + let (freq, variant) = match self.source { + LpuartClockSel::FroLfDiv => { + let freq = clocks.ensure_fro_lf_div_active(&self.power)?; + (freq, Mux::ClkrootFunc0) + } + LpuartClockSel::FroHfDiv => { + let freq = clocks.ensure_fro_hf_div_active(&self.power)?; + (freq, Mux::ClkrootFunc2) + } + LpuartClockSel::ClkIn => { + let freq = clocks.ensure_clk_in_active(&self.power)?; + (freq, Mux::ClkrootFunc3) + } + LpuartClockSel::Clk16K => { + let freq = clocks.ensure_clk_16k_active(&self.power)?; + (freq, Mux::ClkrootFunc4) + } + LpuartClockSel::Clk1M => { + let freq = clocks.ensure_clk_1m_active(&self.power)?; + (freq, Mux::ClkrootFunc5) + } + LpuartClockSel::Pll1ClkDiv => { + let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; + (freq, Mux::ClkrootFunc6) + } + LpuartClockSel::None => unsafe { + // no ClkrootFunc7, just write manually for now + clksel.write(|w| w.bits(0b111)); + clkdiv.modify(|_r, w| { + w.reset().on(); + w.halt().on(); + w + }); + return Ok(0); + }, + }; + + // set clksel + clksel.modify(|_r, w| w.mux().variant(variant)); + + // Set up clkdiv + clkdiv.modify(|_r, w| { + w.halt().on(); + w.reset().on(); + w + }); + clkdiv.modify(|_r, w| { + w.halt().off(); + w.reset().off(); + unsafe { w.div().bits(self.div.into_bits()) }; + w + }); + + while clkdiv.read().unstab().is_on() {} + + Ok(freq / self.div.into_divisor()) + } +} -- cgit