aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/clocks/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa/src/clocks/mod.rs')
-rw-r--r--embassy-mcxa/src/clocks/mod.rs128
1 files changed, 126 insertions, 2 deletions
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
index 667d79536..ceb1e2a50 100644
--- a/embassy-mcxa/src/clocks/mod.rs
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -87,6 +87,7 @@ pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
87 operator.configure_firc_clocks()?; 87 operator.configure_firc_clocks()?;
88 operator.configure_sirc_clocks()?; 88 operator.configure_sirc_clocks()?;
89 operator.configure_fro16k_clocks()?; 89 operator.configure_fro16k_clocks()?;
90 operator.configure_sosc()?;
90 91
91 // For now, just use FIRC as the main/cpu clock, which should already be 92 // For now, just use FIRC as the main/cpu clock, which should already be
92 // the case on reset 93 // the case on reset
@@ -136,6 +137,7 @@ pub fn with_clocks<R: 'static, F: FnOnce(&Clocks) -> R>(f: F) -> Option<R> {
136#[non_exhaustive] 137#[non_exhaustive]
137pub struct Clocks { 138pub struct Clocks {
138 /// The `clk_in` is a clock provided by an external oscillator 139 /// The `clk_in` is a clock provided by an external oscillator
140 /// AKA SOSC
139 pub clk_in: Option<Clock>, 141 pub clk_in: Option<Clock>,
140 142
141 // FRO180M stuff 143 // FRO180M stuff
@@ -485,8 +487,20 @@ impl Clocks {
485 } 487 }
486 488
487 /// Ensure the `clk_in` clock is active and valid at the given power state. 489 /// Ensure the `clk_in` clock is active and valid at the given power state.
488 pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { 490 pub fn ensure_clk_in_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
489 Err(ClockError::NotImplemented { clock: "clk_in" }) 491 let Some(clk) = self.clk_in.as_ref() else {
492 return Err(ClockError::BadConfig {
493 clock: "clk_in",
494 reason: "required but not active",
495 });
496 };
497 if !clk.power.meets_requirement_of(at_level) {
498 return Err(ClockError::BadConfig {
499 clock: "clk_in",
500 reason: "not low power active",
501 });
502 }
503 Ok(clk.frequency)
490 } 504 }
491 505
492 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state. 506 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
@@ -851,6 +865,116 @@ impl ClockOperator<'_> {
851 865
852 Ok(()) 866 Ok(())
853 } 867 }
868
869 /// Configure the SOSC/clk_in oscillator
870 fn configure_sosc(&mut self) -> Result<(), ClockError> {
871 let Some(parts) = self.config.sosc.as_ref() else {
872 return Ok(());
873 };
874
875 let scg0 = unsafe { pac::Scg0::steal() };
876
877 // TODO: Config for the LDO? For now, if we have Sosc, just enable
878 // using the default settings:
879 // LDOBYPASS: 0/not bypassed
880 // VOUT_SEL: 0b100: 1.1v
881 // LDOEN: 0/Disabled
882 scg0.ldocsr().modify(|_r, w| w.ldoen().enabled());
883 while scg0.ldocsr().read().vout_ok().is_disabled() {}
884
885 // TODO: something something pins? This seems to work when the pins are
886 // not enabled, even if GPIO hasn't been initialized at all yet.
887 let eref = match parts.mode {
888 config::SoscMode::CrystalOscillator => pac::scg0::sosccfg::Erefs::Internal,
889 config::SoscMode::ActiveClock => pac::scg0::sosccfg::Erefs::External,
890 };
891 let freq = parts.frequency;
892
893 // TODO: Fix PAC names here
894 //
895 // #[doc = "0: Frequency range select of 8-16 MHz."]
896 // Freq16to20mhz = 0,
897 // #[doc = "1: Frequency range select of 16-25 MHz."]
898 // LowFreq = 1,
899 // #[doc = "2: Frequency range select of 25-40 MHz."]
900 // MediumFreq = 2,
901 // #[doc = "3: Frequency range select of 40-50 MHz."]
902 // HighFreq = 3,
903 let range = match freq {
904 0..8_000_000 => {
905 return Err(ClockError::BadConfig {
906 clock: "clk_in",
907 reason: "freq too low",
908 });
909 }
910 8_000_000..16_000_000 => pac::scg0::sosccfg::Range::Freq16to20mhz,
911 16_000_000..25_000_000 => pac::scg0::sosccfg::Range::LowFreq,
912 25_000_000..40_000_000 => pac::scg0::sosccfg::Range::MediumFreq,
913 40_000_000..50_000_001 => pac::scg0::sosccfg::Range::HighFreq,
914 50_000_001.. => {
915 return Err(ClockError::BadConfig {
916 clock: "clk_in",
917 reason: "freq too high",
918 });
919 }
920 };
921
922 // Set source/erefs and range
923 scg0.sosccfg().modify(|_r, w| {
924 w.erefs().variant(eref);
925 w.range().variant(range);
926 w
927 });
928
929 // Disable lock
930 scg0.sosccsr().modify(|_r, w| w.lk().clear_bit());
931
932 // TODO: We could enable the SOSC clock monitor. There are some things to
933 // figure out first:
934 //
935 // * This requires SIRC to be enabled, not sure which branch. Maybe fro12m_root?
936 // * If SOSC needs to work in deep sleep, AND the monitor is enabled:
937 // * SIRC also need needs to be low power
938 // * We need to decide if we need an interrupt or a reset if the monitor trips
939
940 // Apply remaining config
941 scg0.sosccsr().modify(|_r, w| {
942 // For now, just disable the monitor. See above.
943 w.sosccm().disabled();
944
945 // Set deep sleep mode
946 match parts.power {
947 PoweredClock::NormalEnabledDeepSleepDisabled => {
948 w.soscsten().clear_bit();
949 }
950 PoweredClock::AlwaysEnabled => {
951 w.soscsten().set_bit();
952 }
953 }
954
955 // Enable SOSC
956 w.soscen().enabled()
957 });
958
959 // Wait for SOSC to be valid, check for errors
960 while !scg0.sosccsr().read().soscvld().bit_is_set() {}
961 if scg0.sosccsr().read().soscerr().is_enabled_and_error() {
962 return Err(ClockError::BadConfig {
963 clock: "clk_in",
964 reason: "soscerr is set",
965 });
966 }
967
968 // Re-lock the sosc
969 scg0.sosccsr().modify(|_r, w| w.lk().set_bit());
970
971 self.clocks.clk_in = Some(Clock {
972 frequency: freq,
973 power: parts.power,
974 });
975
976 Ok(())
977 }
854} 978}
855 979
856// 980//