aboutsummaryrefslogtreecommitdiff
path: root/embassy-mcxa/src/clocks/mod.rs
diff options
context:
space:
mode:
authorJames Munns <[email protected]>2025-12-18 19:31:25 +0100
committerJames Munns <[email protected]>2025-12-18 19:31:25 +0100
commit1a3eb7ef28c071e17f93663f7e07d7d6e9f7789e (patch)
tree2952c9d44404f7014f4709d494b21c5fee2eff8e /embassy-mcxa/src/clocks/mod.rs
parentb0cf9b4626e7fa99d1da82a5eb745e9ed14508de (diff)
Cleanups, docs
Diffstat (limited to 'embassy-mcxa/src/clocks/mod.rs')
-rw-r--r--embassy-mcxa/src/clocks/mod.rs207
1 files changed, 95 insertions, 112 deletions
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs
index 866e78bf2..5a2d5ed5c 100644
--- a/embassy-mcxa/src/clocks/mod.rs
+++ b/embassy-mcxa/src/clocks/mod.rs
@@ -439,72 +439,49 @@ pub unsafe fn pulse_reset<G: Gate>() {
439/// 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
440/// 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`].
441impl Clocks { 441impl Clocks {
442 /// Ensure the `fro_lf_div` clock is active and valid at the given power state. 442 fn ensure_clock_active(
443 pub fn ensure_fro_lf_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 443 &self,
444 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 {
445 return Err(ClockError::BadConfig { 449 return Err(ClockError::BadConfig {
446 clock: "fro_lf_div", 450 clock: name,
447 reason: "required but not active", 451 reason: "required but not active",
448 }); 452 });
449 }; 453 };
450 if !clk.power.meets_requirement_of(at_level) { 454 if !clk.power.meets_requirement_of(at_level) {
451 return Err(ClockError::BadConfig { 455 return Err(ClockError::BadConfig {
452 clock: "fro_lf_div", 456 clock: name,
453 reason: "not low power active", 457 reason: "not low power active",
454 }); 458 });
455 } 459 }
456 Ok(clk.frequency) 460 Ok(clk.frequency)
457 } 461 }
458 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
459 /// 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]
460 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> {
461 let Some(clk) = self.fro_hf.as_ref() else { 472 self.ensure_clock_active(&self.fro_hf, "fro_hf", at_level)
462 return Err(ClockError::BadConfig {
463 clock: "fro_hf",
464 reason: "required but not active",
465 });
466 };
467 if !clk.power.meets_requirement_of(at_level) {
468 return Err(ClockError::BadConfig {
469 clock: "fro_hf",
470 reason: "not low power active",
471 });
472 }
473 Ok(clk.frequency)
474 } 473 }
475 474
476 /// 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]
477 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> {
478 let Some(clk) = self.fro_hf_div.as_ref() else { 478 self.ensure_clock_active(&self.fro_hf_div, "fro_hf_div", at_level)
479 return Err(ClockError::BadConfig {
480 clock: "fro_hf_div",
481 reason: "required but not active",
482 });
483 };
484 if !clk.power.meets_requirement_of(at_level) {
485 return Err(ClockError::BadConfig {
486 clock: "fro_hf_div",
487 reason: "not low power active",
488 });
489 }
490 Ok(clk.frequency)
491 } 479 }
492 480
493 /// 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]
494 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> {
495 let Some(clk) = self.clk_in.as_ref() else { 484 self.ensure_clock_active(&self.clk_in, "clk_in", at_level)
496 return Err(ClockError::BadConfig {
497 clock: "clk_in",
498 reason: "required but not active",
499 });
500 };
501 if !clk.power.meets_requirement_of(at_level) {
502 return Err(ClockError::BadConfig {
503 clock: "clk_in",
504 reason: "not low power active",
505 });
506 }
507 Ok(clk.frequency)
508 } 485 }
509 486
510 /// 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.
@@ -534,54 +511,21 @@ impl Clocks {
534 } 511 }
535 512
536 /// 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]
537 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> {
538 let Some(clk) = self.clk_1m.as_ref() else { 516 self.ensure_clock_active(&self.clk_1m, "clk_1m", at_level)
539 return Err(ClockError::BadConfig {
540 clock: "clk_1m",
541 reason: "required but not active",
542 });
543 };
544 if !clk.power.meets_requirement_of(at_level) {
545 return Err(ClockError::BadConfig {
546 clock: "clk_1m",
547 reason: "not low power active",
548 });
549 }
550 Ok(clk.frequency)
551 } 517 }
552 518
553 /// 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.
520 #[inline]
554 pub fn ensure_pll1_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 521 pub fn ensure_pll1_clk_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
555 let Some(clk) = self.pll1_clk.as_ref() else { 522 self.ensure_clock_active(&self.pll1_clk, "pll1_clk", at_level)
556 return Err(ClockError::BadConfig {
557 clock: "spll",
558 reason: "required but not active",
559 });
560 };
561 if !clk.power.meets_requirement_of(at_level) {
562 return Err(ClockError::BadConfig {
563 clock: "spll",
564 reason: "not low power active",
565 });
566 }
567 Ok(clk.frequency)
568 } 523 }
569 524
570 /// 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.
526 #[inline]
571 pub fn ensure_pll1_clk_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> { 527 pub fn ensure_pll1_clk_div_active(&self, at_level: &PoweredClock) -> Result<u32, ClockError> {
572 let Some(clk) = self.pll1_clk_div.as_ref() else { 528 self.ensure_clock_active(&self.pll1_clk_div, "pll1_clk_div", at_level)
573 return Err(ClockError::BadConfig {
574 clock: "spll",
575 reason: "required but not active",
576 });
577 };
578 if !clk.power.meets_requirement_of(at_level) {
579 return Err(ClockError::BadConfig {
580 clock: "spll",
581 reason: "not low power active",
582 });
583 }
584 Ok(clk.frequency)
585 } 529 }
586 530
587 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active 531 /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active
@@ -1029,10 +973,12 @@ impl ClockOperator<'_> {
1029 // | P | Postdivider value | 973 // | P | Postdivider value |
1030 // | Tpon | PLL start-up time | 974 // | Tpon | PLL start-up time |
1031 975
976 // No PLL? Nothing to do!
1032 let Some(cfg) = self.config.spll.as_ref() else { 977 let Some(cfg) = self.config.spll.as_ref() else {
1033 return Ok(()); 978 return Ok(());
1034 }; 979 };
1035 980
981 // match on the source, ensure it is active already
1036 let res = match cfg.source { 982 let res = match cfg.source {
1037 config::SpllSource::Sosc => self 983 config::SpllSource::Sosc => self
1038 .clocks 984 .clocks
@@ -1053,10 +999,12 @@ impl ClockOperator<'_> {
1053 .map(|c| (c, pac::scg0::spllctrl::Source::Sirc)) 999 .map(|c| (c, pac::scg0::spllctrl::Source::Sirc))
1054 .ok_or("sirc not active"), 1000 .ok_or("sirc not active"),
1055 }; 1001 };
1002 // This checks if active
1056 let (clk, variant) = res.map_err(|s| ClockError::BadConfig { 1003 let (clk, variant) = res.map_err(|s| ClockError::BadConfig {
1057 clock: "spll", 1004 clock: "spll",
1058 reason: s, 1005 reason: s,
1059 })?; 1006 })?;
1007 // This checks the correct power reqs
1060 if !clk.power.meets_requirement_of(&cfg.power) { 1008 if !clk.power.meets_requirement_of(&cfg.power) {
1061 return Err(ClockError::BadConfig { 1009 return Err(ClockError::BadConfig {
1062 clock: "spll", 1010 clock: "spll",
@@ -1069,6 +1017,14 @@ impl ClockOperator<'_> {
1069 // > In normal applications, you must calculate the bandwidth manually by using the feedback divider M (ranging from 1 to 216-1), 1017 // > In normal applications, you must calculate the bandwidth manually by using the feedback divider M (ranging from 1 to 216-1),
1070 // > Equation 1, and Equation 2. The PLL is automatically stable in such case. In normal applications, SPLLCTRL[BANDDIRECT] must 1018 // > Equation 1, and Equation 2. The PLL is automatically stable in such case. In normal applications, SPLLCTRL[BANDDIRECT] must
1071 // > be 0; in this case, the bandwidth changes as a function of M. 1019 // > be 0; in this case, the bandwidth changes as a function of M.
1020 if clk.frequency == 0 {
1021 return Err(ClockError::BadConfig {
1022 clock: "spll",
1023 reason: "internal error",
1024 });
1025 }
1026
1027 // These are calculated differently depending on the mode.
1072 let f_in = clk.frequency; 1028 let f_in = clk.frequency;
1073 let bp_pre: bool; 1029 let bp_pre: bool;
1074 let bp_post: bool; 1030 let bp_post: bool;
@@ -1082,13 +1038,44 @@ impl ClockOperator<'_> {
1082 let fout: Option<u32>; 1038 let fout: Option<u32>;
1083 let fcco: Option<u32>; 1039 let fcco: Option<u32>;
1084 1040
1041 let m_check = |m: u16| {
1042 if !(1..=u16::MAX).contains(&m) {
1043 Err(ClockError::BadConfig {
1044 clock: "spll",
1045 reason: "m_mult out of range",
1046 })
1047 } else {
1048 Ok(m)
1049 }
1050 };
1051 let p_check = |p: u8| {
1052 if !(1..=31).contains(&p) {
1053 Err(ClockError::BadConfig {
1054 clock: "spll",
1055 reason: "p_div out of range",
1056 })
1057 } else {
1058 Ok(p)
1059 }
1060 };
1061 let n_check = |n: u8| {
1062 if !(1..=u8::MAX).contains(&n) {
1063 Err(ClockError::BadConfig {
1064 clock: "spll",
1065 reason: "n_div out of range",
1066 })
1067 } else {
1068 Ok(n)
1069 }
1070 };
1071
1085 match cfg.mode { 1072 match cfg.mode {
1086 // Fout = M x Fin 1073 // Fout = M x Fin
1087 config::SpllMode::Mode1a { m_mult } => { 1074 config::SpllMode::Mode1a { m_mult } => {
1088 bp_pre = true; 1075 bp_pre = true;
1089 bp_post = true; 1076 bp_post = true;
1090 bp_post2 = false; 1077 bp_post2 = false;
1091 m = m_mult; 1078 m = m_check(m_mult)?;
1092 p = None; 1079 p = None;
1093 n = None; 1080 n = None;
1094 fcco = f_in.checked_mul(m_mult as u32); 1081 fcco = f_in.checked_mul(m_mult as u32);
@@ -1104,8 +1091,8 @@ impl ClockOperator<'_> {
1104 bp_pre = true; 1091 bp_pre = true;
1105 bp_post = false; 1092 bp_post = false;
1106 bp_post2 = bypass_p2_div; 1093 bp_post2 = bypass_p2_div;
1107 m = m_mult; 1094 m = m_check(m_mult)?;
1108 p = Some(p_div); 1095 p = Some(p_check(p_div)?);
1109 n = None; 1096 n = None;
1110 let mut div = p_div as u32; 1097 let mut div = p_div as u32;
1111 if !bypass_p2_div { 1098 if !bypass_p2_div {
@@ -1119,9 +1106,9 @@ impl ClockOperator<'_> {
1119 bp_pre = false; 1106 bp_pre = false;
1120 bp_post = true; 1107 bp_post = true;
1121 bp_post2 = false; 1108 bp_post2 = false;
1122 m = m_mult; 1109 m = m_check(m_mult)?;
1123 p = None; 1110 p = None;
1124 n = Some(n_div); 1111 n = Some(n_check(n_div)?);
1125 fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32); 1112 fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32);
1126 fout = fcco; 1113 fout = fcco;
1127 } 1114 }
@@ -1136,9 +1123,9 @@ impl ClockOperator<'_> {
1136 bp_pre = false; 1123 bp_pre = false;
1137 bp_post = false; 1124 bp_post = false;
1138 bp_post2 = bypass_p2_div; 1125 bp_post2 = bypass_p2_div;
1139 m = m_mult; 1126 m = m_check(m_mult)?;
1140 p = Some(p_div); 1127 p = Some(p_check(p_div)?);
1141 n = Some(n_div); 1128 n = Some(n_check(n_div)?);
1142 // This can't overflow: u8 x u8 (x 2) always fits in u32 1129 // This can't overflow: u8 x u8 (x 2) always fits in u32
1143 let mut div = (p_div as u32) * (n_div as u32); 1130 let mut div = (p_div as u32) * (n_div as u32);
1144 if !bypass_p2_div { 1131 if !bypass_p2_div {
@@ -1149,16 +1136,21 @@ impl ClockOperator<'_> {
1149 } 1136 }
1150 }; 1137 };
1151 1138
1152 defmt::debug!("f_in: {:?}", f_in); 1139 // Dump all the PLL calcs if needed for debugging
1153 defmt::debug!("bp_pre: {:?}", bp_pre); 1140 #[cfg(feature = "defmt")]
1154 defmt::debug!("bp_post: {:?}", bp_post); 1141 {
1155 defmt::debug!("bp_post2: {:?}", bp_post2); 1142 defmt::debug!("f_in: {:?}", f_in);
1156 defmt::debug!("m: {:?}", m); 1143 defmt::debug!("bp_pre: {:?}", bp_pre);
1157 defmt::debug!("p: {:?}", p); 1144 defmt::debug!("bp_post: {:?}", bp_post);
1158 defmt::debug!("n: {:?}", n); 1145 defmt::debug!("bp_post2: {:?}", bp_post2);
1159 defmt::debug!("fout: {:?}", fout); 1146 defmt::debug!("m: {:?}", m);
1160 defmt::debug!("fcco: {:?}", fcco); 1147 defmt::debug!("p: {:?}", p);
1148 defmt::debug!("n: {:?}", n);
1149 defmt::debug!("fout: {:?}", fout);
1150 defmt::debug!("fcco: {:?}", fcco);
1151 }
1161 1152
1153 // Ensure the Fcco and Fout calcs didn't overflow
1162 let fcco = fcco.ok_or(ClockError::BadConfig { 1154 let fcco = fcco.ok_or(ClockError::BadConfig {
1163 clock: "spll", 1155 clock: "spll",
1164 reason: "fcco invalid1", 1156 reason: "fcco invalid1",
@@ -1167,6 +1159,7 @@ impl ClockOperator<'_> {
1167 clock: "spll", 1159 clock: "spll",
1168 reason: "fout invalid", 1160 reason: "fout invalid",
1169 })?; 1161 })?;
1162
1170 // Fcco: 275MHz to 550MHz 1163 // Fcco: 275MHz to 550MHz
1171 if !(275_000_000..=550_000_000).contains(&fcco) { 1164 if !(275_000_000..=550_000_000).contains(&fcco) {
1172 return Err(ClockError::BadConfig { 1165 return Err(ClockError::BadConfig {
@@ -1174,10 +1167,11 @@ impl ClockOperator<'_> {
1174 reason: "fcco invalid2", 1167 reason: "fcco invalid2",
1175 }); 1168 });
1176 } 1169 }
1177 // Fout: 4.3MHz to 2x Max CPU Frequency
1178 1170
1179 // TODO: Different for different CPUs? 1171 // TODO: Different for different CPUs?
1180 const CPU_MAX_FREQ: u32 = 180_000_000; 1172 const CPU_MAX_FREQ: u32 = 180_000_000;
1173
1174 // Fout: 4.3MHz to 2x Max CPU Frequency
1181 if !(4_300_000..=(2 * CPU_MAX_FREQ)).contains(&fout) { 1175 if !(4_300_000..=(2 * CPU_MAX_FREQ)).contains(&fout) {
1182 return Err(ClockError::BadConfig { 1176 return Err(ClockError::BadConfig {
1183 clock: "spll", 1177 clock: "spll",
@@ -1185,17 +1179,6 @@ impl ClockOperator<'_> {
1185 }); 1179 });
1186 } 1180 }
1187 1181
1188 // TODO:
1189 assert!(clk.frequency != 0);
1190 assert!(m >= 1);
1191 if let Some(p) = p.as_ref() {
1192 assert!(*p >= 1);
1193 assert!(*p <= 31);
1194 }
1195 if let Some(n) = n.as_ref() {
1196 assert!(*n >= 1);
1197 }
1198
1199 // A = floor(m / 4) + 1 1182 // A = floor(m / 4) + 1
1200 let selp_a = (m / 4) + 1; 1183 let selp_a = (m / 4) + 1;
1201 // SELP = A if A < 31 1184 // SELP = A if A < 31