From 9f889d015ef5d373343ad6c769824e28be780e53 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 14 Nov 2025 15:35:20 +0100 Subject: Implement SPConfHelper for OSTimer and ADC --- src/board.rs | 14 ---- src/clocks/mod.rs | 71 ++++++++++++++---- src/clocks/periph_helpers.rs | 171 ++++++++++++++++++++++++++++++++++++++++++- src/lpuart/mod.rs | 12 +-- 4 files changed, 229 insertions(+), 39 deletions(-) delete mode 100644 src/board.rs 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 @@ -use crate::{clocks, pac, pins}; - -/// Initialize clocks and pin muxing for UART2 debug console. -pub unsafe fn init_uart2(p: &pac::Peripherals) { - clocks::ensure_frolf_running(p); - clocks::enable_uart2_port2(p); - pins::configure_uart2_pins_port2(); - clocks::select_uart2_clock(p); -} - -/// Initialize clocks for the LED GPIO/PORT used by the blink example. -pub unsafe fn init_led(p: &pac::Peripherals) { - clocks::enable_led_port(p); -} diff --git a/src/clocks/mod.rs b/src/clocks/mod.rs index 63a8fb64d..db41db628 100644 --- a/src/clocks/mod.rs +++ b/src/clocks/mod.rs @@ -141,16 +141,23 @@ impl SPConfHelper for UnimplementedConfig { } } +pub struct NoConfig; +impl SPConfHelper for NoConfig { + fn post_enable_config(&self, _clocks: &Clocks) -> Result { + Ok(0) + } +} + pub mod gate { - use super::{periph_helpers::LpuartConfig, *}; + use super::{periph_helpers::{AdcConfig, LpuartConfig, OsTimerConfig}, *}; - impl_cc_gate!(PORT1, mrcc_glb_cc1, port1, UnimplementedConfig); - impl_cc_gate!(PORT2, mrcc_glb_cc1, port2, UnimplementedConfig); - impl_cc_gate!(PORT3, mrcc_glb_cc1, port3, UnimplementedConfig); - impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, ostimer0, UnimplementedConfig); + impl_cc_gate!(PORT1, mrcc_glb_cc1, port1, NoConfig); + impl_cc_gate!(PORT2, mrcc_glb_cc1, port2, NoConfig); + impl_cc_gate!(PORT3, mrcc_glb_cc1, port3, NoConfig); + impl_cc_gate!(OSTIMER0, mrcc_glb_cc1, ostimer0, OsTimerConfig); impl_cc_gate!(LPUART2, mrcc_glb_cc0, lpuart2, LpuartConfig); - impl_cc_gate!(GPIO3, mrcc_glb_cc2, gpio3, UnimplementedConfig); - impl_cc_gate!(ADC1, mrcc_glb_cc1, adc1, UnimplementedConfig); + impl_cc_gate!(GPIO3, mrcc_glb_cc2, gpio3, NoConfig); + impl_cc_gate!(ADC1, mrcc_glb_cc1, adc1, AdcConfig); } // /// Convenience helper enabling the PORT2 and LPUART2 gates required for the debug UART. @@ -418,7 +425,6 @@ pub struct Clocks { pub fro_lf_div: Option, // // End FRO12M stuff - pub clk_16k_vsys: Option, pub clk_16k_vdd_core: Option, pub main_clk: Option, @@ -691,7 +697,10 @@ impl ClockOperator<'_> { // Lock the control register self.vbat0.frolcka().modify(|_, w| w.lock().set_bit()); - let Fro16KConfig { vsys_domain_active, vdd_core_domain_active } = fro16k; + let Fro16KConfig { + vsys_domain_active, + vdd_core_domain_active, + } = fro16k; // Enable clock outputs to both VSYS and VDD_CORE domains // Bit 0: clk_16k0 to VSYS domain @@ -713,9 +722,7 @@ impl ClockOperator<'_> { power: PoweredClock::AlwaysEnabled, }); } - self.vbat0.froclke().modify(|_r, w| { - unsafe { w.clke().bits(bits) } - }); + self.vbat0.froclke().modify(|_r, w| unsafe { w.clke().bits(bits) }); Ok(()) } @@ -783,6 +790,22 @@ impl Clocks { Ok(clk.frequency) } + pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result { + let Some(clk) = self.fro_hf.as_ref() else { + return Err(ClockError::BadConfig { + clock: "fro_hf", + reason: "required but not active", + }); + }; + if !clk.power.meets_requirement_of(at_level) { + return Err(ClockError::BadConfig { + clock: "fro_hf", + 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 { @@ -803,8 +826,28 @@ impl Clocks { 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_16k_vsys_active(&self, _at_level: &PoweredClock) -> Result { + // NOTE: clk_16k is always active in low power mode + Ok(self + .clk_16k_vsys + .as_ref() + .ok_or(ClockError::BadConfig { + clock: "clk_16k_vsys", + reason: "required but not active", + })? + .frequency) + } + + pub fn ensure_clk_16k_vdd_core_active(&self, _at_level: &PoweredClock) -> Result { + // NOTE: clk_16k is always active in low power mode + Ok(self + .clk_16k_vdd_core + .as_ref() + .ok_or(ClockError::BadConfig { + clock: "clk_16k_vdd_core", + reason: "required but not active", + })? + .frequency) } pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result { diff --git a/src/clocks/periph_helpers.rs b/src/clocks/periph_helpers.rs index 4d186acf8..de767ef87 100644 --- a/src/clocks/periph_helpers.rs +++ b/src/clocks/periph_helpers.rs @@ -1,4 +1,4 @@ -use super::{ClockError, Clocks, Div8, PoweredClock}; +use super::{ClockError, Clocks, PoweredClock}; use crate::pac; pub trait SPConfHelper { @@ -7,6 +7,59 @@ pub trait SPConfHelper { // config types +/// This type represents a divider in the range 1..=16. +/// +/// At a hardware level, this is an 8-bit register from 0..=15, +/// which adds one. +/// +/// While the *clock* domain seems to use 8-bit dividers, the *peripheral* domain +/// seems to use 4 bit dividers! +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Div4(pub(super) u8); + +impl Div4 { + /// Store a "raw" divisor value that will divide the source by + /// `(n + 1)`, e.g. `Div4::from_raw(0)` will divide the source + /// by 1, and `Div4::from_raw(15)` will divide the source by + /// 16. + pub const fn from_raw(n: u8) -> Option { + if n > 0b1111 { + None + } else { + Some(Self(n)) + } + } + + /// Store a specific divisor value that will divide the source + /// by `n`. e.g. `Div4::from_divisor(1)` will divide the source + /// by 1, and `Div4::from_divisor(16)` will divide the source + /// by 16. + /// + /// Will return `None` if `n` is not in the range `1..=16`. + /// Consider [`Self::from_raw`] for an infallible version. + pub const fn from_divisor(n: u8) -> Option { + let Some(n) = n.checked_sub(1) else { + return None; + }; + if n > 0b1111 { + return None; + } + Some(Self(n)) + } + + /// Convert into "raw" bits form + #[inline(always)] + pub const fn into_bits(self) -> u8 { + self.0 + } + + /// Convert into "divisor" form, as a u32 for convenient frequency math + #[inline(always)] + pub const fn into_divisor(self) -> u32 { + self.0 as u32 + 1 + } +} + #[derive(Debug, Clone, Copy)] pub enum LpuartClockSel { /// FRO12M/FRO_LF/SIRC clock source, passed through divider @@ -43,12 +96,41 @@ pub struct LpuartConfig { /// Clock source pub source: LpuartClockSel, /// Clock divisor - pub div: Div8, + pub div: Div4, /// Which instance is this? // NOTE: should not be user settable pub(crate) instance: LpuartInstance, } +pub enum OstimerClockSel { + /// 16k clock, sourced from FRO16K (Vdd Core) + Clk16kVddCore, + /// 1 MHz Clock sourced from FRO12M + Clk1M, + /// Disabled + None, +} + +pub struct OsTimerConfig { + pub power: PoweredClock, + pub source: OstimerClockSel, +} + +pub enum AdcClockSel { + FroLfDiv, + FroHf, + ClkIn, + Clk1M, + Pll1ClkDiv, + None, +} + +pub struct AdcConfig { + pub power: PoweredClock, + pub source: AdcClockSel, + pub div: Div4, +} + // impls impl SPConfHelper for LpuartConfig { @@ -80,7 +162,7 @@ impl SPConfHelper for LpuartConfig { (freq, Mux::ClkrootFunc3) } LpuartClockSel::Clk16K => { - let freq = clocks.ensure_clk_16k_active(&self.power)?; + let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; (freq, Mux::ClkrootFunc4) } LpuartClockSel::Clk1M => { @@ -124,3 +206,86 @@ impl SPConfHelper for LpuartConfig { Ok(freq / self.div.into_divisor()) } } + +impl SPConfHelper for OsTimerConfig { + fn post_enable_config(&self, clocks: &Clocks) -> Result { + let mrcc0 = unsafe { pac::Mrcc0::steal() }; + Ok(match self.source { + OstimerClockSel::Clk16kVddCore => { + let freq = clocks.ensure_clk_16k_vdd_core_active(&self.power)?; + mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_16k()); + freq + } + OstimerClockSel::Clk1M => { + let freq = clocks.ensure_clk_1m_active(&self.power)?; + mrcc0.mrcc_ostimer0_clksel().write(|w| w.mux().clkroot_1m()); + freq + } + OstimerClockSel::None => { + mrcc0.mrcc_ostimer0_clksel().write(|w| unsafe { w.mux().bits(0b11) }); + 0 + } + }) + } +} + +impl SPConfHelper for AdcConfig { + fn post_enable_config(&self, clocks: &Clocks) -> Result { + use mcxa_pac::mrcc0::mrcc_adc_clksel::Mux; + let mrcc0 = unsafe { pac::Mrcc0::steal() }; + let (freq, variant) = match self.source { + AdcClockSel::FroLfDiv => { + let freq = clocks.ensure_fro_lf_div_active(&self.power)?; + (freq, Mux::ClkrootFunc0) + } + AdcClockSel::FroHf => { + let freq = clocks.ensure_fro_hf_active(&self.power)?; + (freq, Mux::ClkrootFunc1) + } + AdcClockSel::ClkIn => { + let freq = clocks.ensure_clk_in_active(&self.power)?; + (freq, Mux::ClkrootFunc3) + } + AdcClockSel::Clk1M => { + let freq = clocks.ensure_clk_1m_active(&self.power)?; + (freq, Mux::ClkrootFunc5) + } + AdcClockSel::Pll1ClkDiv => { + let freq = clocks.ensure_pll1_clk_div_active(&self.power)?; + (freq, Mux::ClkrootFunc6) + } + AdcClockSel::None => { + mrcc0.mrcc_adc_clksel().write(|w| unsafe { + // no ClkrootFunc7, just write manually for now + w.mux().bits(0b111) + }); + mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { + w.reset().on(); + w.halt().on(); + w + }); + return Ok(0); + } + }; + + // set clksel + mrcc0.mrcc_adc_clksel().modify(|_r, w| w.mux().variant(variant)); + + // Set up clkdiv + mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { + w.halt().on(); + w.reset().on(); + w + }); + mrcc0.mrcc_adc_clkdiv().modify(|_r, w| { + w.halt().off(); + w.reset().off(); + unsafe { w.div().bits(self.div.into_bits()) }; + w + }); + + while mrcc0.mrcc_adc_clkdiv().read().unstab().is_on() {} + + Ok(freq / self.div.into_divisor()) + } +} diff --git a/src/lpuart/mod.rs b/src/lpuart/mod.rs index 41ec951ec..35b4db24c 100644 --- a/src/lpuart/mod.rs +++ b/src/lpuart/mod.rs @@ -4,7 +4,7 @@ use embassy_hal_internal::{Peri, PeripheralType}; use paste::paste; use crate::clocks::periph_helpers::{LpuartClockSel, LpuartConfig}; -use crate::clocks::{enable_and_reset, ClockError, Div8, Gate, PoweredClock}; +use crate::clocks::{enable_and_reset, ClockError, periph_helpers::Div4, Gate, PoweredClock}; use crate::pac::lpuart0::baud::Sbns as StopBits; use crate::pac::lpuart0::ctrl::{Idlecfg as IdleConfig, Ilt as IdleType, Pt as Parity, M as DataBits}; use crate::pac::lpuart0::modir::{Txctsc as TxCtsConfig, Txctssrc as TxCtsSource}; @@ -20,8 +20,6 @@ pub mod buffered; // Stub implementation for LIB (Peripherals), GPIO, DMA and CLOCK until stable API // Pin and Clock initialization is currently done at the examples level. - - // --- START GPIO --- mod gpio { @@ -491,7 +489,7 @@ pub struct Config { /// Clock source pub source: LpuartClockSel, /// Clock divisor - pub div: Div8, + pub div: Div4, /// Baud rate in bits per second pub baudrate_bps: u32, /// Parity configuration @@ -547,7 +545,7 @@ impl Default for Config { swap_txd_rxd: false, power: PoweredClock::NormalEnabledDeepSleepDisabled, source: LpuartClockSel::FroLfDiv, - div: const { Div8::from_divisor(1).unwrap() }, + div: const { Div4::from_divisor(1).unwrap() }, } } } @@ -640,9 +638,7 @@ impl<'a, M: Mode> Lpuart<'a, M> { div: config.div, instance: T::CLOCK_INSTANCE, }; - let clock_freq = unsafe { - enable_and_reset::(&conf).map_err(Error::ClockSetup)? - }; + let clock_freq = unsafe { enable_and_reset::(&conf).map_err(Error::ClockSetup)? }; // Perform initialization sequence perform_software_reset(regs); -- cgit