diff options
Diffstat (limited to 'embassy-mcxa')
| -rw-r--r-- | embassy-mcxa/src/clocks/config.rs | 88 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/mod.rs | 207 |
2 files changed, 165 insertions, 130 deletions
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs index 3f1729d00..4beca5f27 100644 --- a/embassy-mcxa/src/clocks/config.rs +++ b/embassy-mcxa/src/clocks/config.rs | |||
| @@ -136,7 +136,7 @@ pub enum SoscMode { | |||
| 136 | ActiveClock, | 136 | ActiveClock, |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | // SOSC/clk_in configuration | 139 | /// SOSC/clk_in configuration |
| 140 | #[derive(Copy, Clone)] | 140 | #[derive(Copy, Clone)] |
| 141 | pub struct SoscConfig { | 141 | pub struct SoscConfig { |
| 142 | /// Mode of the external reference clock | 142 | /// Mode of the external reference clock |
| @@ -149,22 +149,23 @@ pub struct SoscConfig { | |||
| 149 | 149 | ||
| 150 | // SPLL | 150 | // SPLL |
| 151 | 151 | ||
| 152 | // Fin: 32kHz to 150MHz | 152 | /// PLL1/SPLL configuration |
| 153 | // Fcco: 275MHz to 550MHz | ||
| 154 | // Fout: 4.3MHz to 2x Max CPU Frequency | ||
| 155 | |||
| 156 | pub struct SpllConfig { | 153 | pub struct SpllConfig { |
| 154 | /// Input clock source for the PLL1/SPLL | ||
| 157 | pub source: SpllSource, | 155 | pub source: SpllSource, |
| 156 | /// Mode of operation for the PLL1/SPLL | ||
| 158 | pub mode: SpllMode, | 157 | pub mode: SpllMode, |
| 158 | /// Power state of the SPLL | ||
| 159 | pub power: PoweredClock, | 159 | pub power: PoweredClock, |
| 160 | /// Is the "pll1_clk_div" clock enabled? | 160 | /// Is the "pll1_clk_div" clock enabled? |
| 161 | pub pll1_clk_div: Option<Div8>, | 161 | pub pll1_clk_div: Option<Div8>, |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | /// Input clock source for the PLL1/SPLL | ||
| 164 | pub enum SpllSource { | 165 | pub enum SpllSource { |
| 165 | /// External Oscillator (8-50M) | 166 | /// External Oscillator (8-50MHz) |
| 166 | Sosc, | 167 | Sosc, |
| 167 | /// Fast Internal Oscillator (45M) | 168 | /// Fast Internal Oscillator (45MHz) |
| 168 | // NOTE: Figure 69 says "firc_45mhz"/"clk_45m", not "fro_hf_gated", | 169 | // NOTE: Figure 69 says "firc_45mhz"/"clk_45m", not "fro_hf_gated", |
| 169 | // so this is is always 45MHz. | 170 | // so this is is always 45MHz. |
| 170 | Firc, | 171 | Firc, |
| @@ -173,32 +174,83 @@ pub enum SpllSource { | |||
| 173 | // TODO: the reference manual hints that ROSC is possible, | 174 | // TODO: the reference manual hints that ROSC is possible, |
| 174 | // however the minimum input frequency is 32K, but ROSC is 16K. | 175 | // however the minimum input frequency is 32K, but ROSC is 16K. |
| 175 | // Some diagrams show this option, and some diagrams omit it. | 176 | // Some diagrams show this option, and some diagrams omit it. |
| 177 | // SVD shows it as "reserved". | ||
| 176 | // | 178 | // |
| 177 | // /// Realtime Internal Oscillator (16K Osc) | 179 | // /// Realtime Internal Oscillator (16K Osc) |
| 178 | // Rosc, | 180 | // Rosc, |
| 179 | } | 181 | } |
| 180 | 182 | ||
| 181 | /// N: 1..=255 | 183 | /// Mode of operation for the SPLL/PLL1 |
| 182 | /// M: 1..=65535 | 184 | /// |
| 183 | /// P: 1..=31 | 185 | /// NOTE: Currently, only "Mode 1" normal operational modes are implemented, |
| 186 | /// as described in the Reference Manual. | ||
| 187 | #[non_exhaustive] | ||
| 184 | pub enum SpllMode { | 188 | pub enum SpllMode { |
| 185 | /// Fout = M x Fin | 189 | /// Mode 1a does not use the Pre/Post dividers. |
| 186 | Mode1a { m_mult: u16 }, | 190 | /// |
| 187 | /// if !bypass_p2_div: Fout = (M / (2 x P)) x Fin | 191 | /// `Fout = m_mult x SpllSource` |
| 188 | /// if bypass_p2_div: Fout = (M / P ) x Fin | 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 | ||
| 189 | Mode1b { | 212 | Mode1b { |
| 213 | /// PLL Multiplier. `m_mult` must be in the range 1..=65535. | ||
| 190 | m_mult: u16, | 214 | m_mult: u16, |
| 215 | /// Post Divider. `p_div` must be in the range 1..=31. | ||
| 191 | p_div: u8, | 216 | p_div: u8, |
| 217 | /// Bonus post divider | ||
| 192 | bypass_p2_div: bool, | 218 | bypass_p2_div: bool, |
| 193 | }, | 219 | }, |
| 194 | /// Fout = (M / N) x Fin | 220 | |
| 195 | Mode1c { m_mult: u16, n_div: u8 }, | 221 | /// Mode 1c does use the Pre-divider, but does not use the Post-divider |
| 196 | /// if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin | 222 | /// |
| 197 | /// if bypass_p2_div: Fout = (M / ( N x P )) x Fin | 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 | ||
| 198 | Mode1d { | 246 | Mode1d { |
| 247 | /// PLL Multiplier. `m_mult` must be in the range 1..=65535. | ||
| 199 | m_mult: u16, | 248 | m_mult: u16, |
| 249 | /// Pre Divider. `n_div` must be in the range 1..=255. | ||
| 200 | n_div: u8, | 250 | n_div: u8, |
| 251 | /// Post Divider. `p_div` must be in the range 1..=31. | ||
| 201 | p_div: u8, | 252 | p_div: u8, |
| 253 | /// Bonus post divider | ||
| 202 | bypass_p2_div: bool, | 254 | bypass_p2_div: bool, |
| 203 | }, | 255 | }, |
| 204 | } | 256 | } |
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`]. |
| 441 | impl Clocks { | 441 | impl 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 |
