diff options
Diffstat (limited to 'embassy-mcxa/src/clocks/mod.rs')
| -rw-r--r-- | embassy-mcxa/src/clocks/mod.rs | 495 |
1 files changed, 418 insertions, 77 deletions
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`]. |
| 437 | impl Clocks { | 441 | impl 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 | // |
