aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-mcxa')
-rw-r--r--embassy-mcxa/src/clocks/config.rs115
-rw-r--r--embassy-mcxa/src/clocks/mod.rs495
2 files changed, 532 insertions, 78 deletions
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs
index 9f97160ff..4beca5f27 100644
--- a/embassy-mcxa/src/clocks/config.rs
+++ b/embassy-mcxa/src/clocks/config.rs
@@ -121,8 +121,12 @@ pub struct ClocksConfig {
121 pub fro16k: Option<Fro16KConfig>, 121 pub fro16k: Option<Fro16KConfig>,
122 /// SOSC, clk_in clock source 122 /// SOSC, clk_in clock source
123 pub sosc: Option<SoscConfig>, 123 pub sosc: Option<SoscConfig>,
124 /// SPLL
125 pub spll: Option<SpllConfig>,
124} 126}
125 127
128// SOSC
129
126/// The mode of the external reference clock 130/// The mode of the external reference clock
127#[derive(Copy, Clone)] 131#[derive(Copy, Clone)]
128pub enum SoscMode { 132pub enum SoscMode {
@@ -132,7 +136,7 @@ pub enum SoscMode {
132 ActiveClock, 136 ActiveClock,
133} 137}
134 138
135// SOSC/clk_in configuration 139/// SOSC/clk_in configuration
136#[derive(Copy, Clone)] 140#[derive(Copy, Clone)]
137pub struct SoscConfig { 141pub struct SoscConfig {
138 /// Mode of the external reference clock 142 /// Mode of the external reference clock
@@ -143,6 +147,114 @@ pub struct SoscConfig {
143 pub power: PoweredClock, 147 pub power: PoweredClock,
144} 148}
145 149
150// SPLL
151
152/// PLL1/SPLL configuration
153pub struct SpllConfig {
154 /// Input clock source for the PLL1/SPLL
155 pub source: SpllSource,
156 /// Mode of operation for the PLL1/SPLL
157 pub mode: SpllMode,
158 /// Power state of the SPLL
159 pub power: PoweredClock,
160 /// Is the "pll1_clk_div" clock enabled?
161 pub pll1_clk_div: Option<Div8>,
162}
163
164/// Input clock source for the PLL1/SPLL
165pub enum SpllSource {
166 /// External Oscillator (8-50MHz)
167 Sosc,
168 /// Fast Internal Oscillator (45MHz)
169 // NOTE: Figure 69 says "firc_45mhz"/"clk_45m", not "fro_hf_gated",
170 // so this is is always 45MHz.
171 Firc,
172 /// S Internal Oscillator (12M)
173 Sirc,
174 // TODO: the reference manual hints that ROSC is possible,
175 // however the minimum input frequency is 32K, but ROSC is 16K.
176 // Some diagrams show this option, and some diagrams omit it.
177 // SVD shows it as "reserved".
178 //
179 // /// Realtime Internal Oscillator (16K Osc)
180 // Rosc,
181}
182
183/// Mode of operation for the SPLL/PLL1
184///
185/// NOTE: Currently, only "Mode 1" normal operational modes are implemented,
186/// as described in the Reference Manual.
187#[non_exhaustive]
188pub enum SpllMode {
189 /// Mode 1a does not use the Pre/Post dividers.
190 ///
191 /// `Fout = m_mult x SpllSource`
192 ///
193 /// Both of the following constraints must be met:
194 ///
195 /// * Fout: 275MHz to 550MHz
196 /// * Fout: 4.3MHz to 2x Max CPU Frequency
197 Mode1a {
198 /// PLL Multiplier. Must be in the range 1..=65535.
199 m_mult: u16,
200 },
201
202 /// Mode 1b does not use the Pre-divider.
203 ///
204 /// * `if !bypass_p2_div: Fout = (M / (2 x P)) x Fin`
205 /// * `if bypass_p2_div: Fout = (M / P ) x Fin`
206 ///
207 /// Both of the following constraints must be met:
208 ///
209 /// * Fcco: 275MHz to 550MHz
210 /// * `Fcco = m_mult x SpllSource`
211 /// * Fout: 4.3MHz to 2x Max CPU Frequency
212 Mode1b {
213 /// PLL Multiplier. `m_mult` must be in the range 1..=65535.
214 m_mult: u16,
215 /// Post Divider. `p_div` must be in the range 1..=31.
216 p_div: u8,
217 /// Bonus post divider
218 bypass_p2_div: bool,
219 },
220
221 /// Mode 1c does use the Pre-divider, but does not use the Post-divider
222 ///
223 /// `Fout = (M / N) x Fin`
224 ///
225 /// Both of the following constraints must be met:
226 ///
227 /// * Fout: 275MHz to 550MHz
228 /// * Fout: 4.3MHz to 2x Max CPU Frequency
229 Mode1c {
230 /// PLL Multiplier. `m_mult` must be in the range 1..=65535.
231 m_mult: u16,
232 /// Pre Divider. `n_div` must be in the range 1..=255.
233 n_div: u8,
234 },
235
236 /// Mode 1b uses both the Pre and Post dividers.
237 ///
238 /// * `if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin`
239 /// * `if bypass_p2_div: Fout = (M / ( N x P )) x Fin`
240 ///
241 /// Both of the following constraints must be met:
242 ///
243 /// * Fcco: 275MHz to 550MHz
244 /// * `Fcco = (m_mult x SpllSource) / (n_div x p_div (x 2))`
245 /// * Fout: 4.3MHz to 2x Max CPU Frequency
246 Mode1d {
247 /// PLL Multiplier. `m_mult` must be in the range 1..=65535.
248 m_mult: u16,
249 /// Pre Divider. `n_div` must be in the range 1..=255.
250 n_div: u8,
251 /// Post Divider. `p_div` must be in the range 1..=31.
252 p_div: u8,
253 /// Bonus post divider
254 bypass_p2_div: bool,
255 },
256}
257
146// FIRC/FRO180M 258// FIRC/FRO180M
147 259
148/// ```text 260/// ```text
@@ -222,6 +334,7 @@ impl Default for ClocksConfig {
222 vdd_core_domain_active: true, 334 vdd_core_domain_active: true,
223 }), 335 }),
224 sosc: None, 336 sosc: None,
337 spll: None,
225 } 338 }
226 } 339 }
227} 340}
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
index b41a1ba46..04559fd04 100644
--- a/embassy-mcxa/src/clocks/mod.rs
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -88,6 +88,7 @@ pub fn init(settings: ClocksConfig) -> Result<(), ClockError> {
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 operator.configure_sosc()?;
91 operator.configure_spll()?;
91 92
92 // For now, just use FIRC as the main/cpu clock, which should already be 93 // For now, just use FIRC as the main/cpu clock, which should already be
93 // the case on reset 94 // the case on reset
@@ -199,6 +200,9 @@ pub struct Clocks {
199 200
200 /// `pll1_clk` is the output of the main system PLL, `pll1`. 201 /// `pll1_clk` is the output of the main system PLL, `pll1`.
201 pub pll1_clk: Option<Clock>, 202 pub pll1_clk: Option<Clock>,
203
204 /// `pll1_clk_div` is a configurable frequency clock, sourced from `pll1_clk`
205 pub pll1_clk_div: Option<Clock>,
202} 206}
203 207
204/// `ClockError` is the main error returned when configuring or checking clock state 208/// `ClockError` is the main error returned when configuring or checking clock state
@@ -435,72 +439,49 @@ pub unsafe fn pulse_reset<G: Gate>() {
435/// selected clocks are active at a suitable level at time of construction. These methods 439/// selected clocks are active at a suitable level at time of construction. These methods
436/// return the frequency of the requested clock, in Hertz, or a [`ClockError`]. 440/// return the frequency of the requested clock, in Hertz, or a [`ClockError`].
437impl Clocks { 441impl Clocks {
438 /// Ensure the `fro_lf_div` clock is active and valid at the given power state. 442 fn ensure_clock_active(
439 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 443 &self,
440 let Some(clk) = self.fro_lf_div.as_ref() else { 444 clock: &Option<Clock>,
445 name: &'static str,
446 at_level: &PoweredClock,
447 ) -> Result<u32, ClockError> {
448 let Some(clk) = clock.as_ref() else {
441 return Err(ClockError::BadConfig { 449 return Err(ClockError::BadConfig {
442 clock: "fro_lf_div", 450 clock: name,
443 reason: "required but not active", 451 reason: "required but not active",
444 }); 452 });
445 }; 453 };
446 if !clk.power.meets_requirement_of(at_level) { 454 if !clk.power.meets_requirement_of(at_level) {
447 return Err(ClockError::BadConfig { 455 return Err(ClockError::BadConfig {
448 clock: "fro_lf_div", 456 clock: name,
449 reason: "not low power active", 457 reason: "not low power active",
450 }); 458 });
451 } 459 }
452 Ok(clk.frequency) 460 Ok(clk.frequency)
453 } 461 }
454 462
463 /// Ensure the `fro_lf_div` clock is active and valid at the given power state.
464 #[inline]
465 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
466 self.ensure_clock_active(&self.fro_lf_div, "fro_lf_div", at_level)
467 }
468
455 /// Ensure the `fro_hf` clock is active and valid at the given power state. 469 /// Ensure the `fro_hf` clock is active and valid at the given power state.
470 #[inline]
456 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 471 pub fn ensure_fro_hf_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
457 let Some(clk) = self.fro_hf.as_ref() else { 472 self.ensure_clock_active(&self.fro_hf, "fro_hf", at_level)
458 return Err(ClockError::BadConfig {
459 clock: "fro_hf",
460 reason: "required but not active",
461 });
462 };
463 if !clk.power.meets_requirement_of(at_level) {
464 return Err(ClockError::BadConfig {
465 clock: "fro_hf",
466 reason: "not low power active",
467 });
468 }
469 Ok(clk.frequency)
470 } 473 }
471 474
472 /// Ensure the `fro_hf_div` clock is active and valid at the given power state. 475 /// Ensure the `fro_hf_div` clock is active and valid at the given power state.
476 #[inline]
473 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 477 pub fn ensure_fro_hf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
474 let Some(clk) = self.fro_hf_div.as_ref() else { 478 self.ensure_clock_active(&self.fro_hf_div, "fro_hf_div", at_level)
475 return Err(ClockError::BadConfig {
476 clock: "fro_hf_div",
477 reason: "required but not active",
478 });
479 };
480 if !clk.power.meets_requirement_of(at_level) {
481 return Err(ClockError::BadConfig {
482 clock: "fro_hf_div",
483 reason: "not low power active",
484 });
485 }
486 Ok(clk.frequency)
487 } 479 }
488 480
489 /// Ensure the `clk_in` clock is active and valid at the given power state. 481 /// Ensure the `clk_in` clock is active and valid at the given power state.
482 #[inline]
490 pub fn ensure_clk_in_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 483 pub fn ensure_clk_in_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
491 let Some(clk) = self.clk_in.as_ref() else { 484 self.ensure_clock_active(&self.clk_in, "clk_in", at_level)
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)
504 } 485 }
505 486
506 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state. 487 /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state.
@@ -530,30 +511,21 @@ impl Clocks {
530 } 511 }
531 512
532 /// Ensure the `clk_1m` clock is active and valid at the given power state. 513 /// Ensure the `clk_1m` clock is active and valid at the given power state.
514 #[inline]
533 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 515 pub fn ensure_clk_1m_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
534 let Some(clk) = self.clk_1m.as_ref() else { 516 self.ensure_clock_active(&self.clk_1m, "clk_1m", at_level)
535 return Err(ClockError::BadConfig {
536 clock: "clk_1m",
537 reason: "required but not active",
538 });
539 };
540 if !clk.power.meets_requirement_of(at_level) {
541 return Err(ClockError::BadConfig {
542 clock: "clk_1m",
543 reason: "not low power active",
544 });
545 }
546 Ok(clk.frequency)
547 } 517 }
548 518
549 /// Ensure the `pll1_clk` clock is active and valid at the given power state. 519 /// Ensure the `pll1_clk` clock is active and valid at the given power state.
550 pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { 520 #[inline]
551 Err(ClockError::NotImplemented { clock: "pll1_clk" }) 521 pub fn ensure_pll1_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
522 self.ensure_clock_active(&self.pll1_clk, "pll1_clk", at_level)
552 } 523 }
553 524
554 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state. 525 /// Ensure the `pll1_clk_div` clock is active and valid at the given power state.
555 pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result<u32, ClockError> { 526 #[inline]
556 Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) 527 pub fn ensure_pll1_clk_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
528 self.ensure_clock_active(&self.pll1_clk_div, "pll1_clk_div", at_level)
557 } 529 }
558 530
559 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active 531 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active
@@ -825,7 +797,7 @@ impl ClockOperator<'_> {
825 Ok(()) 797 Ok(())
826 } 798 }
827 799
828 /// Configure the FRO16K/clk_16k clock family 800 /// Configure the ROSC/FRO16K/clk_16k clock family
829 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> { 801 fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> {
830 let Some(fro16k) = self.config.fro16k.as_ref() else { 802 let Some(fro16k) = self.config.fro16k.as_ref() else {
831 return Ok(()); 803 return Ok(());
@@ -866,21 +838,30 @@ impl ClockOperator<'_> {
866 Ok(()) 838 Ok(())
867 } 839 }
868 840
841 fn ensure_ldo_active(&mut self) {
842 // TODO: Config for the LDO? For now, just enable
843 // using the default settings:
844 // LDOBYPASS: 0/not bypassed
845 // VOUT_SEL: 0b100: 1.1v
846 // LDOEN: 0/Disabled
847 let already_enabled = {
848 let ldocsr = self.scg0.ldocsr().read();
849 ldocsr.ldoen().is_enabled() && ldocsr.vout_ok().is_enabled()
850 };
851 if !already_enabled {
852 self.scg0.ldocsr().modify(|_r, w| w.ldoen().enabled());
853 while self.scg0.ldocsr().read().vout_ok().is_disabled() {}
854 }
855 }
856
869 /// Configure the SOSC/clk_in oscillator 857 /// Configure the SOSC/clk_in oscillator
870 fn configure_sosc(&mut self) -> Result<(), ClockError> { 858 fn configure_sosc(&mut self) -> Result<(), ClockError> {
871 let Some(parts) = self.config.sosc.as_ref() else { 859 let Some(parts) = self.config.sosc.as_ref() else {
872 return Ok(()); 860 return Ok(());
873 }; 861 };
874 862
875 let scg0 = unsafe { pac::Scg0::steal() }; 863 // Enable (and wait for) LDO to be active
876 864 self.ensure_ldo_active();
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 865
885 // TODO: something something pins? This seems to work when the pins are 866 // 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. 867 // not enabled, even if GPIO hasn't been initialized at all yet.
@@ -920,14 +901,14 @@ impl ClockOperator<'_> {
920 }; 901 };
921 902
922 // Set source/erefs and range 903 // Set source/erefs and range
923 scg0.sosccfg().modify(|_r, w| { 904 self.scg0.sosccfg().modify(|_r, w| {
924 w.erefs().variant(eref); 905 w.erefs().variant(eref);
925 w.range().variant(range); 906 w.range().variant(range);
926 w 907 w
927 }); 908 });
928 909
929 // Disable lock 910 // Disable lock
930 scg0.sosccsr().modify(|_r, w| w.lk().clear_bit()); 911 self.scg0.sosccsr().modify(|_r, w| w.lk().clear_bit());
931 912
932 // TODO: We could enable the SOSC clock monitor. There are some things to 913 // TODO: We could enable the SOSC clock monitor. There are some things to
933 // figure out first: 914 // figure out first:
@@ -938,7 +919,7 @@ impl ClockOperator<'_> {
938 // * We need to decide if we need an interrupt or a reset if the monitor trips 919 // * We need to decide if we need an interrupt or a reset if the monitor trips
939 920
940 // Apply remaining config 921 // Apply remaining config
941 scg0.sosccsr().modify(|_r, w| { 922 self.scg0.sosccsr().modify(|_r, w| {
942 // For now, just disable the monitor. See above. 923 // For now, just disable the monitor. See above.
943 w.sosccm().disabled(); 924 w.sosccm().disabled();
944 925
@@ -957,8 +938,8 @@ impl ClockOperator<'_> {
957 }); 938 });
958 939
959 // Wait for SOSC to be valid, check for errors 940 // Wait for SOSC to be valid, check for errors
960 while !scg0.sosccsr().read().soscvld().bit_is_set() {} 941 while !self.scg0.sosccsr().read().soscvld().bit_is_set() {}
961 if scg0.sosccsr().read().soscerr().is_enabled_and_error() { 942 if self.scg0.sosccsr().read().soscerr().is_enabled_and_error() {
962 return Err(ClockError::BadConfig { 943 return Err(ClockError::BadConfig {
963 clock: "clk_in", 944 clock: "clk_in",
964 reason: "soscerr is set", 945 reason: "soscerr is set",
@@ -966,7 +947,7 @@ impl ClockOperator<'_> {
966 } 947 }
967 948
968 // Re-lock the sosc 949 // Re-lock the sosc
969 scg0.sosccsr().modify(|_r, w| w.lk().set_bit()); 950 self.scg0.sosccsr().modify(|_r, w| w.lk().set_bit());
970 951
971 self.clocks.clk_in = Some(Clock { 952 self.clocks.clk_in = Some(Clock {
972 frequency: freq, 953 frequency: freq,
@@ -975,6 +956,366 @@ impl ClockOperator<'_> {
975 956
976 Ok(()) 957 Ok(())
977 } 958 }
959
960 fn configure_spll(&mut self) -> Result<(), ClockError> {
961 // # Vocab
962 //
963 // | Name | Meaning |
964 // | :--- | :--- |
965 // | Fin | Frequency of clkin |
966 // | clkout | Output clock of the PLL |
967 // | Fout | Frequency of clkout (depends on mode) |
968 // | clkref | PLL Reference clock, the input clock to the PFD |
969 // | Fref | Frequency of clkref, Fref = Fin / N |
970 // | Fcco | Frequency of the output clock of the CCO, Fcco = M * Fref |
971 // | N | Predivider value |
972 // | M | Feedback divider value |
973 // | P | Postdivider value |
974 // | Tpon | PLL start-up time |
975
976 // No PLL? Nothing to do!
977 let Some(cfg) = self.config.spll.as_ref() else {
978 return Ok(());
979 };
980
981 // Ensure the LDO is active
982 self.ensure_ldo_active();
983
984 // match on the source, ensure it is active already
985 let res = match cfg.source {
986 config::SpllSource::Sosc => self
987 .clocks
988 .clk_in
989 .as_ref()
990 .map(|c| (c, pac::scg0::spllctrl::Source::Sosc))
991 .ok_or("sosc not active"),
992 config::SpllSource::Firc => self
993 .clocks
994 .clk_45m
995 .as_ref()
996 .map(|c| (c, pac::scg0::spllctrl::Source::Firc))
997 .ok_or("firc not active"),
998 config::SpllSource::Sirc => self
999 .clocks
1000 .fro_12m
1001 .as_ref()
1002 .map(|c| (c, pac::scg0::spllctrl::Source::Sirc))
1003 .ok_or("sirc not active"),
1004 };
1005 // This checks if active
1006 let (clk, variant) = res.map_err(|s| ClockError::BadConfig {
1007 clock: "spll",
1008 reason: s,
1009 })?;
1010 // This checks the correct power reqs
1011 if !clk.power.meets_requirement_of(&cfg.power) {
1012 return Err(ClockError::BadConfig {
1013 clock: "spll",
1014 reason: "needs low power source",
1015 });
1016 }
1017
1018 // Bandwidth calc
1019 //
1020 // > In normal applications, you must calculate the bandwidth manually by using the feedback divider M (ranging from 1 to 216-1),
1021 // > Equation 1, and Equation 2. The PLL is automatically stable in such case. In normal applications, SPLLCTRL[BANDDIRECT] must
1022 // > be 0; in this case, the bandwidth changes as a function of M.
1023 if clk.frequency == 0 {
1024 return Err(ClockError::BadConfig {
1025 clock: "spll",
1026 reason: "internal error",
1027 });
1028 }
1029
1030 // These are calculated differently depending on the mode.
1031 let f_in = clk.frequency;
1032 let bp_pre: bool;
1033 let bp_post: bool;
1034 let bp_post2: bool;
1035 let m: u16;
1036 let p: Option<u8>;
1037 let n: Option<u8>;
1038
1039 // Calculate both Fout and Fcco so we can ensure they don't overflow
1040 // and are in range
1041 let fout: Option<u32>;
1042 let fcco: Option<u32>;
1043
1044 let m_check = |m: u16| {
1045 if !(1..=u16::MAX).contains(&m) {
1046 Err(ClockError::BadConfig {
1047 clock: "spll",
1048 reason: "m_mult out of range",
1049 })
1050 } else {
1051 Ok(m)
1052 }
1053 };
1054 let p_check = |p: u8| {
1055 if !(1..=31).contains(&p) {
1056 Err(ClockError::BadConfig {
1057 clock: "spll",
1058 reason: "p_div out of range",
1059 })
1060 } else {
1061 Ok(p)
1062 }
1063 };
1064 let n_check = |n: u8| {
1065 if !(1..=u8::MAX).contains(&n) {
1066 Err(ClockError::BadConfig {
1067 clock: "spll",
1068 reason: "n_div out of range",
1069 })
1070 } else {
1071 Ok(n)
1072 }
1073 };
1074
1075 match cfg.mode {
1076 // Fout = M x Fin
1077 config::SpllMode::Mode1a { m_mult } => {
1078 bp_pre = true;
1079 bp_post = true;
1080 bp_post2 = false;
1081 m = m_check(m_mult)?;
1082 p = None;
1083 n = None;
1084 fcco = f_in.checked_mul(m_mult as u32);
1085 fout = fcco;
1086 }
1087 // if !bypass_p2_div: Fout = (M / (2 x P)) x Fin
1088 // if bypass_p2_div: Fout = (M / P ) x Fin
1089 config::SpllMode::Mode1b {
1090 m_mult,
1091 p_div,
1092 bypass_p2_div,
1093 } => {
1094 bp_pre = true;
1095 bp_post = false;
1096 bp_post2 = bypass_p2_div;
1097 m = m_check(m_mult)?;
1098 p = Some(p_check(p_div)?);
1099 n = None;
1100 let mut div = p_div as u32;
1101 if !bypass_p2_div {
1102 div *= 2;
1103 }
1104 fcco = f_in.checked_mul(m_mult as u32);
1105 fout = (f_in / div).checked_mul(m_mult as u32);
1106 }
1107 // Fout = (M / N) x Fin
1108 config::SpllMode::Mode1c { m_mult, n_div } => {
1109 bp_pre = false;
1110 bp_post = true;
1111 bp_post2 = false;
1112 m = m_check(m_mult)?;
1113 p = None;
1114 n = Some(n_check(n_div)?);
1115 fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32);
1116 fout = fcco;
1117 }
1118 // if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin
1119 // if bypass_p2_div: Fout = (M / ( N x P )) x Fin
1120 config::SpllMode::Mode1d {
1121 m_mult,
1122 n_div,
1123 p_div,
1124 bypass_p2_div,
1125 } => {
1126 bp_pre = false;
1127 bp_post = false;
1128 bp_post2 = bypass_p2_div;
1129 m = m_check(m_mult)?;
1130 p = Some(p_check(p_div)?);
1131 n = Some(n_check(n_div)?);
1132 // This can't overflow: u8 x u8 (x 2) always fits in u32
1133 let mut div = (p_div as u32) * (n_div as u32);
1134 if !bypass_p2_div {
1135 div *= 2;
1136 }
1137 fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32);
1138 fout = (f_in / div).checked_mul(m_mult as u32);
1139 }
1140 };
1141
1142 // Dump all the PLL calcs if needed for debugging
1143 #[cfg(feature = "defmt")]
1144 {
1145 defmt::debug!("f_in: {:?}", f_in);
1146 defmt::debug!("bp_pre: {:?}", bp_pre);
1147 defmt::debug!("bp_post: {:?}", bp_post);
1148 defmt::debug!("bp_post2: {:?}", bp_post2);
1149 defmt::debug!("m: {:?}", m);
1150 defmt::debug!("p: {:?}", p);
1151 defmt::debug!("n: {:?}", n);
1152 defmt::debug!("fout: {:?}", fout);
1153 defmt::debug!("fcco: {:?}", fcco);
1154 }
1155
1156 // Ensure the Fcco and Fout calcs didn't overflow
1157 let fcco = fcco.ok_or(ClockError::BadConfig {
1158 clock: "spll",
1159 reason: "fcco invalid1",
1160 })?;
1161 let fout = fout.ok_or(ClockError::BadConfig {
1162 clock: "spll",
1163 reason: "fout invalid",
1164 })?;
1165
1166 // Fcco: 275MHz to 550MHz
1167 if !(275_000_000..=550_000_000).contains(&fcco) {
1168 return Err(ClockError::BadConfig {
1169 clock: "spll",
1170 reason: "fcco invalid2",
1171 });
1172 }
1173
1174 // TODO: Different for different CPUs?
1175 const CPU_MAX_FREQ: u32 = 180_000_000;
1176
1177 // Fout: 4.3MHz to 2x Max CPU Frequency
1178 if !(4_300_000..=(2 * CPU_MAX_FREQ)).contains(&fout) {
1179 return Err(ClockError::BadConfig {
1180 clock: "spll",
1181 reason: "fout invalid",
1182 });
1183 }
1184
1185 // A = floor(m / 4) + 1
1186 let selp_a = (m / 4) + 1;
1187 // SELP = A if A < 31
1188 // = 31 if A >= 31
1189 let selp = selp_a.min(31);
1190
1191 // A = 1 if M >= 8000
1192 // = floor(8000 / M) if 8000 > M >= 122
1193 // = 2 x floor(M / 4) / 3 if 122 > M >= 1
1194 let seli_a = if m >= 8000 {
1195 1
1196 } else if m >= 122 {
1197 8000 / m
1198 } else {
1199 (2 * (m / 4)) / 3
1200 };
1201 // SELI = A if A < 63
1202 // = 63 if A >= 63
1203 let seli = seli_a.min(63);
1204 // SELR must be 0.
1205 let selr = 0;
1206
1207 self.scg0.spllctrl().modify(|_r, w| {
1208 w.source().variant(variant);
1209 unsafe {
1210 w.selp().bits(selp as u8);
1211 w.seli().bits(seli as u8);
1212 w.selr().bits(selr);
1213 }
1214 w
1215 });
1216
1217 if let Some(n) = n {
1218 self.scg0.spllndiv().modify(|_r, w| unsafe { w.ndiv().bits(n) });
1219 }
1220 if let Some(p) = p {
1221 self.scg0.spllpdiv().modify(|_r, w| unsafe { w.pdiv().bits(p) });
1222 }
1223 self.scg0.spllmdiv().modify(|_r, w| unsafe { w.mdiv().bits(m) });
1224
1225 self.scg0.spllctrl().modify(|_r, w| {
1226 w.bypassprediv().bit(bp_pre);
1227 w.bypasspostdiv().bit(bp_post);
1228 w.bypasspostdiv2().bit(bp_post2);
1229
1230 // TODO: support FRM?
1231 w.frm().disabled();
1232
1233 w
1234 });
1235
1236 // Unlock
1237 self.scg0.spllcsr().modify(|_r, w| w.lk().write_enabled());
1238
1239 // TODO: Support clock monitors?
1240 // self.scg0.spllcsr().modify(|_r, w| w.spllcm().?);
1241
1242 self.scg0.trim_lock().write(|w| unsafe {
1243 w.trim_lock_key().bits(0x5a5a);
1244 w.trim_unlock().not_locked()
1245 });
1246
1247 // SPLLLOCK_CNFG: The lock time programmed in this register must be
1248 // equal to meet the PLL 500μs lock time plus the 300 refclk count startup.
1249 //
1250 // LOCK_TIME = 500μs/T ref + 300, F ref = F in /N (input frequency divided by pre-divider ratio).
1251 //
1252 // 500us is 1/2000th of a second, therefore Fref / 2000 is the number of cycles in 500us.
1253 let f_ref = if let Some(n) = n { f_in / (n as u32) } else { f_in };
1254 let lock_time = f_ref.div_ceil(2000) + 300;
1255 self.scg0
1256 .splllock_cnfg()
1257 .write(|w| unsafe { w.lock_time().bits(lock_time) });
1258
1259 // TODO: Support Spread spectrum?
1260
1261 self.scg0.spllcsr().modify(|_r, w| {
1262 w.spllclken().enabled();
1263 w.spllpwren().enabled();
1264 w.spllsten().bit(matches!(cfg.power, PoweredClock::AlwaysEnabled));
1265 w
1266 });
1267
1268 // Wait for SPLL to set up
1269 loop {
1270 let csr = self.scg0.spllcsr().read();
1271 if csr.spll_lock().is_enabled_and_valid() {
1272 if csr.spllerr().is_enabled_and_error() {
1273 return Err(ClockError::BadConfig {
1274 clock: "spll",
1275 reason: "spllerr is set",
1276 });
1277 }
1278 break;
1279 }
1280 }
1281
1282 // Re-lock SPLL CSR
1283 self.scg0.spllcsr().modify(|_r, w| w.lk().write_disabled());
1284
1285 // Store clock state
1286 self.clocks.pll1_clk = Some(Clock {
1287 frequency: fout,
1288 power: cfg.power,
1289 });
1290
1291 // Do we enable the `pll1_clk_div` output?
1292 if let Some(d) = cfg.pll1_clk_div.as_ref() {
1293 // Halt and reset the div; then set our desired div.
1294 self.syscon.pll1clkdiv().write(|w| {
1295 w.halt().halt();
1296 w.reset().asserted();
1297 unsafe { w.div().bits(d.into_bits()) };
1298 w
1299 });
1300 // Then unhalt it, and reset it
1301 self.syscon.pll1clkdiv().write(|w| {
1302 w.halt().run();
1303 w.reset().released();
1304 w
1305 });
1306
1307 // Wait for clock to stabilize
1308 while self.syscon.pll1clkdiv().read().unstab().is_ongoing() {}
1309
1310 // Store off the clock info
1311 self.clocks.pll1_clk_div = Some(Clock {
1312 frequency: fout / d.into_divisor(),
1313 power: cfg.power,
1314 });
1315 }
1316
1317 Ok(())
1318 }
978} 1319}
979 1320
980// 1321//