diff options
Diffstat (limited to 'embassy-mcxa/src')
| -rw-r--r-- | embassy-mcxa/src/clocks/config.rs | 66 | ||||
| -rw-r--r-- | embassy-mcxa/src/clocks/mod.rs | 318 |
2 files changed, 368 insertions, 16 deletions
diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs index 9f97160ff..0f362ad0a 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)] |
| 128 | pub enum SoscMode { | 132 | pub enum SoscMode { |
| @@ -143,6 +147,67 @@ pub struct SoscConfig { | |||
| 143 | pub power: PoweredClock, | 147 | pub power: PoweredClock, |
| 144 | } | 148 | } |
| 145 | 149 | ||
| 150 | // SPLL | ||
| 151 | |||
| 152 | // Fin: 32kHz to 150MHz | ||
| 153 | // Fcco: 275MHz to 550MHz | ||
| 154 | // Fout: 4.3MHz to 2x Max CPU Frequency | ||
| 155 | |||
| 156 | pub struct SpllConfig { | ||
| 157 | pub source: SpllSource, | ||
| 158 | pub mode: SpllMode, | ||
| 159 | pub power: PoweredClock, | ||
| 160 | } | ||
| 161 | |||
| 162 | pub enum SpllSource { | ||
| 163 | /// External Oscillator (8-50M) | ||
| 164 | Sosc, | ||
| 165 | /// Fast Internal Oscillator (45M) | ||
| 166 | // NOTE: Figure 69 says "firc_45mhz"/"clk_45m", not "fro_hf_gated", | ||
| 167 | // so this is is always 45MHz. | ||
| 168 | Firc, | ||
| 169 | /// S Internal Oscillator (12M) | ||
| 170 | Sirc, | ||
| 171 | |||
| 172 | // TODO: the reference manual hints that ROSC is possible, | ||
| 173 | // however the minimum input frequency is 32K, but ROSC is 16K. | ||
| 174 | // Some diagrams show this option, and some diagrams omit it. | ||
| 175 | // | ||
| 176 | // /// Realtime Internal Oscillator (16K Osc) | ||
| 177 | // Rosc, | ||
| 178 | } | ||
| 179 | |||
| 180 | /// N: 1..=255 | ||
| 181 | /// M: 1..=65535 | ||
| 182 | /// P: 1..=31 | ||
| 183 | pub enum SpllMode { | ||
| 184 | /// Fout = M x Fin | ||
| 185 | Mode1a { | ||
| 186 | m_mult: u16, | ||
| 187 | }, | ||
| 188 | /// if !bypass_p2_div: Fout = (M / (2 x P)) x Fin | ||
| 189 | /// if bypass_p2_div: Fout = (M / P ) x Fin | ||
| 190 | Mode1b { | ||
| 191 | m_mult: u16, | ||
| 192 | p_div: u8, | ||
| 193 | bypass_p2_div: bool, | ||
| 194 | }, | ||
| 195 | /// Fout = (M / N) x Fin | ||
| 196 | Mode1c { | ||
| 197 | m_mult: u16, | ||
| 198 | n_div: u8, | ||
| 199 | }, | ||
| 200 | /// if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin | ||
| 201 | /// if bypass_p2_div: Fout = (M / ( N x P )) x Fin | ||
| 202 | Mode1d { | ||
| 203 | m_mult: u16, | ||
| 204 | n_div: u8, | ||
| 205 | p_div: u8, | ||
| 206 | bypass_p2_div: bool, | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | |||
| 146 | // FIRC/FRO180M | 211 | // FIRC/FRO180M |
| 147 | 212 | ||
| 148 | /// ```text | 213 | /// ```text |
| @@ -222,6 +287,7 @@ impl Default for ClocksConfig { | |||
| 222 | vdd_core_domain_active: true, | 287 | vdd_core_domain_active: true, |
| 223 | }), | 288 | }), |
| 224 | sosc: None, | 289 | sosc: None, |
| 290 | spll: None, | ||
| 225 | } | 291 | } |
| 226 | } | 292 | } |
| 227 | } | 293 | } |
diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index b41a1ba46..98f7075eb 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 |
| @@ -825,7 +826,7 @@ impl ClockOperator<'_> { | |||
| 825 | Ok(()) | 826 | Ok(()) |
| 826 | } | 827 | } |
| 827 | 828 | ||
| 828 | /// Configure the FRO16K/clk_16k clock family | 829 | /// Configure the ROSC/FRO16K/clk_16k clock family |
| 829 | fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> { | 830 | fn configure_fro16k_clocks(&mut self) -> Result<(), ClockError> { |
| 830 | let Some(fro16k) = self.config.fro16k.as_ref() else { | 831 | let Some(fro16k) = self.config.fro16k.as_ref() else { |
| 831 | return Ok(()); | 832 | return Ok(()); |
| @@ -866,21 +867,30 @@ impl ClockOperator<'_> { | |||
| 866 | Ok(()) | 867 | Ok(()) |
| 867 | } | 868 | } |
| 868 | 869 | ||
| 870 | fn ensure_ldo_active(&mut self) { | ||
| 871 | // TODO: Config for the LDO? For now, just enable | ||
| 872 | // using the default settings: | ||
| 873 | // LDOBYPASS: 0/not bypassed | ||
| 874 | // VOUT_SEL: 0b100: 1.1v | ||
| 875 | // LDOEN: 0/Disabled | ||
| 876 | let already_enabled = { | ||
| 877 | let ldocsr = self.scg0.ldocsr().read(); | ||
| 878 | ldocsr.ldoen().is_enabled() && ldocsr.vout_ok().is_enabled() | ||
| 879 | }; | ||
| 880 | if !already_enabled { | ||
| 881 | self.scg0.ldocsr().modify(|_r, w| w.ldoen().enabled()); | ||
| 882 | while self.scg0.ldocsr().read().vout_ok().is_disabled() {} | ||
| 883 | } | ||
| 884 | } | ||
| 885 | |||
| 869 | /// Configure the SOSC/clk_in oscillator | 886 | /// Configure the SOSC/clk_in oscillator |
| 870 | fn configure_sosc(&mut self) -> Result<(), ClockError> { | 887 | fn configure_sosc(&mut self) -> Result<(), ClockError> { |
| 871 | let Some(parts) = self.config.sosc.as_ref() else { | 888 | let Some(parts) = self.config.sosc.as_ref() else { |
| 872 | return Ok(()); | 889 | return Ok(()); |
| 873 | }; | 890 | }; |
| 874 | 891 | ||
| 875 | let scg0 = unsafe { pac::Scg0::steal() }; | 892 | // Enable (and wait for) LDO to be active |
| 876 | 893 | 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 | 894 | ||
| 885 | // TODO: something something pins? This seems to work when the pins are | 895 | // 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. | 896 | // not enabled, even if GPIO hasn't been initialized at all yet. |
| @@ -920,14 +930,14 @@ impl ClockOperator<'_> { | |||
| 920 | }; | 930 | }; |
| 921 | 931 | ||
| 922 | // Set source/erefs and range | 932 | // Set source/erefs and range |
| 923 | scg0.sosccfg().modify(|_r, w| { | 933 | self.scg0.sosccfg().modify(|_r, w| { |
| 924 | w.erefs().variant(eref); | 934 | w.erefs().variant(eref); |
| 925 | w.range().variant(range); | 935 | w.range().variant(range); |
| 926 | w | 936 | w |
| 927 | }); | 937 | }); |
| 928 | 938 | ||
| 929 | // Disable lock | 939 | // Disable lock |
| 930 | scg0.sosccsr().modify(|_r, w| w.lk().clear_bit()); | 940 | self.scg0.sosccsr().modify(|_r, w| w.lk().clear_bit()); |
| 931 | 941 | ||
| 932 | // TODO: We could enable the SOSC clock monitor. There are some things to | 942 | // TODO: We could enable the SOSC clock monitor. There are some things to |
| 933 | // figure out first: | 943 | // figure out first: |
| @@ -938,7 +948,7 @@ impl ClockOperator<'_> { | |||
| 938 | // * We need to decide if we need an interrupt or a reset if the monitor trips | 948 | // * We need to decide if we need an interrupt or a reset if the monitor trips |
| 939 | 949 | ||
| 940 | // Apply remaining config | 950 | // Apply remaining config |
| 941 | scg0.sosccsr().modify(|_r, w| { | 951 | self.scg0.sosccsr().modify(|_r, w| { |
| 942 | // For now, just disable the monitor. See above. | 952 | // For now, just disable the monitor. See above. |
| 943 | w.sosccm().disabled(); | 953 | w.sosccm().disabled(); |
| 944 | 954 | ||
| @@ -957,8 +967,8 @@ impl ClockOperator<'_> { | |||
| 957 | }); | 967 | }); |
| 958 | 968 | ||
| 959 | // Wait for SOSC to be valid, check for errors | 969 | // Wait for SOSC to be valid, check for errors |
| 960 | while !scg0.sosccsr().read().soscvld().bit_is_set() {} | 970 | while !self.scg0.sosccsr().read().soscvld().bit_is_set() {} |
| 961 | if scg0.sosccsr().read().soscerr().is_enabled_and_error() { | 971 | if self.scg0.sosccsr().read().soscerr().is_enabled_and_error() { |
| 962 | return Err(ClockError::BadConfig { | 972 | return Err(ClockError::BadConfig { |
| 963 | clock: "clk_in", | 973 | clock: "clk_in", |
| 964 | reason: "soscerr is set", | 974 | reason: "soscerr is set", |
| @@ -966,7 +976,7 @@ impl ClockOperator<'_> { | |||
| 966 | } | 976 | } |
| 967 | 977 | ||
| 968 | // Re-lock the sosc | 978 | // Re-lock the sosc |
| 969 | scg0.sosccsr().modify(|_r, w| w.lk().set_bit()); | 979 | self.scg0.sosccsr().modify(|_r, w| w.lk().set_bit()); |
| 970 | 980 | ||
| 971 | self.clocks.clk_in = Some(Clock { | 981 | self.clocks.clk_in = Some(Clock { |
| 972 | frequency: freq, | 982 | frequency: freq, |
| @@ -975,6 +985,282 @@ impl ClockOperator<'_> { | |||
| 975 | 985 | ||
| 976 | Ok(()) | 986 | Ok(()) |
| 977 | } | 987 | } |
| 988 | |||
| 989 | fn configure_spll(&mut self) -> Result<(), ClockError> { | ||
| 990 | // # Vocab | ||
| 991 | // | ||
| 992 | // | Name | Meaning | | ||
| 993 | // | :--- | :--- | | ||
| 994 | // | Fin | Frequency of clkin | | ||
| 995 | // | clkout | Output clock of the PLL | | ||
| 996 | // | Fout | Frequency of clkout (depends on mode) | | ||
| 997 | // | clkref | PLL Reference clock, the input clock to the PFD | | ||
| 998 | // | Fref | Frequency of clkref, Fref = Fin / N | | ||
| 999 | // | Fcco | Frequency of the output clock of the CCO, Fcco = M * Fref | | ||
| 1000 | // | N | Predivider value | | ||
| 1001 | // | M | Feedback divider value | | ||
| 1002 | // | P | Postdivider value | | ||
| 1003 | // | Tpon | PLL start-up time | | ||
| 1004 | |||
| 1005 | let Some(cfg) = self.config.spll.as_ref() else { | ||
| 1006 | return Ok(()); | ||
| 1007 | }; | ||
| 1008 | |||
| 1009 | let res = match cfg.source { | ||
| 1010 | config::SpllSource::Sosc => self | ||
| 1011 | .clocks | ||
| 1012 | .clk_in | ||
| 1013 | .as_ref() | ||
| 1014 | .map(|c| (c, pac::scg0::spllctrl::Source::Sosc)) | ||
| 1015 | .ok_or("sosc not active"), | ||
| 1016 | config::SpllSource::Firc => self | ||
| 1017 | .clocks | ||
| 1018 | .clk_45m | ||
| 1019 | .as_ref() | ||
| 1020 | .map(|c| (c, pac::scg0::spllctrl::Source::Firc)) | ||
| 1021 | .ok_or("firc not active"), | ||
| 1022 | config::SpllSource::Sirc => self | ||
| 1023 | .clocks | ||
| 1024 | .fro_12m | ||
| 1025 | .as_ref() | ||
| 1026 | .map(|c| (c, pac::scg0::spllctrl::Source::Sirc)) | ||
| 1027 | .ok_or("sirc not active"), | ||
| 1028 | }; | ||
| 1029 | let (clk, variant) = res.map_err(|s| ClockError::BadConfig { | ||
| 1030 | clock: "spll", | ||
| 1031 | reason: s, | ||
| 1032 | })?; | ||
| 1033 | if !clk.power.meets_requirement_of(&cfg.power) { | ||
| 1034 | return Err(ClockError::BadConfig { clock: "spll", reason: "needs low power source" }); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | // Bandwidth calc | ||
| 1038 | // | ||
| 1039 | // > In normal applications, you must calculate the bandwidth manually by using the feedback divider M (ranging from 1 to 216-1), | ||
| 1040 | // > Equation 1, and Equation 2. The PLL is automatically stable in such case. In normal applications, SPLLCTRL[BANDDIRECT] must | ||
| 1041 | // > be 0; in this case, the bandwidth changes as a function of M. | ||
| 1042 | let f_in = clk.frequency; | ||
| 1043 | let bp_pre: bool; | ||
| 1044 | let bp_post: bool; | ||
| 1045 | let bp_post2: bool; | ||
| 1046 | let m: u16; | ||
| 1047 | let p: Option<u8>; | ||
| 1048 | let n: Option<u8>; | ||
| 1049 | |||
| 1050 | // Calculate both Fout and Fcco so we can ensure they don't overflow | ||
| 1051 | // and are in range | ||
| 1052 | let fout: Option<u32>; | ||
| 1053 | let fcco: Option<u32>; | ||
| 1054 | |||
| 1055 | match cfg.mode { | ||
| 1056 | // Fout = M x Fin | ||
| 1057 | config::SpllMode::Mode1a { m_mult } => { | ||
| 1058 | bp_pre = true; | ||
| 1059 | bp_post = true; | ||
| 1060 | bp_post2 = false; | ||
| 1061 | m = m_mult; | ||
| 1062 | p = None; | ||
| 1063 | n = None; | ||
| 1064 | fcco = f_in.checked_mul(m_mult as u32); | ||
| 1065 | fout = fcco; | ||
| 1066 | } | ||
| 1067 | // if !bypass_p2_div: Fout = (M / (2 x P)) x Fin | ||
| 1068 | // if bypass_p2_div: Fout = (M / P ) x Fin | ||
| 1069 | config::SpllMode::Mode1b { | ||
| 1070 | m_mult, | ||
| 1071 | p_div, | ||
| 1072 | bypass_p2_div, | ||
| 1073 | } => { | ||
| 1074 | bp_pre = true; | ||
| 1075 | bp_post = false; | ||
| 1076 | bp_post2 = bypass_p2_div; | ||
| 1077 | m = m_mult; | ||
| 1078 | p = Some(p_div); | ||
| 1079 | n = None; | ||
| 1080 | let mut div = p_div as u32; | ||
| 1081 | if !bypass_p2_div { | ||
| 1082 | div *= 2; | ||
| 1083 | } | ||
| 1084 | fcco = f_in.checked_mul(m_mult as u32); | ||
| 1085 | fout = (f_in / div).checked_mul(m_mult as u32); | ||
| 1086 | } | ||
| 1087 | // Fout = (M / N) x Fin | ||
| 1088 | config::SpllMode::Mode1c { m_mult, n_div } => { | ||
| 1089 | bp_pre = false; | ||
| 1090 | bp_post = true; | ||
| 1091 | bp_post2 = false; | ||
| 1092 | m = m_mult; | ||
| 1093 | p = None; | ||
| 1094 | n = Some(n_div); | ||
| 1095 | fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32); | ||
| 1096 | fout = fcco; | ||
| 1097 | } | ||
| 1098 | // if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin | ||
| 1099 | // if bypass_p2_div: Fout = (M / ( N x P )) x Fin | ||
| 1100 | config::SpllMode::Mode1d { | ||
| 1101 | m_mult, | ||
| 1102 | n_div, | ||
| 1103 | p_div, | ||
| 1104 | bypass_p2_div, | ||
| 1105 | } => { | ||
| 1106 | bp_pre = false; | ||
| 1107 | bp_post = false; | ||
| 1108 | bp_post2 = bypass_p2_div; | ||
| 1109 | m = m_mult; | ||
| 1110 | p = Some(p_div); | ||
| 1111 | n = Some(n_div); | ||
| 1112 | // This can't overflow: u8 x u8 (x 2) always fits in u32 | ||
| 1113 | let mut div = (p_div as u32) * (n_div as u32); | ||
| 1114 | if !bypass_p2_div { | ||
| 1115 | div *= 2; | ||
| 1116 | } | ||
| 1117 | fcco = (f_in / (n_div as u32)).checked_mul(m_mult as u32); | ||
| 1118 | fout = (f_in / div).checked_mul(m_mult as u32); | ||
| 1119 | } | ||
| 1120 | }; | ||
| 1121 | let fcco = fcco.ok_or(ClockError::BadConfig { | ||
| 1122 | clock: "spll", | ||
| 1123 | reason: "fcco invalid", | ||
| 1124 | })?; | ||
| 1125 | let fout = fout.ok_or(ClockError::BadConfig { | ||
| 1126 | clock: "spll", | ||
| 1127 | reason: "fout invalid", | ||
| 1128 | })?; | ||
| 1129 | // Fcco: 275MHz to 550MHz | ||
| 1130 | if !(275_000_000..=550_000_000).contains(&fcco) { | ||
| 1131 | return Err(ClockError::BadConfig { | ||
| 1132 | clock: "spll", | ||
| 1133 | reason: "fcco invalid", | ||
| 1134 | }); | ||
| 1135 | } | ||
| 1136 | // Fout: 4.3MHz to 2x Max CPU Frequency | ||
| 1137 | |||
| 1138 | // TODO: Different for different CPUs? | ||
| 1139 | const CPU_MAX_FREQ: u32 = 180_000_000; | ||
| 1140 | if !(4_300_000..=(2 * CPU_MAX_FREQ)).contains(&fout) { | ||
| 1141 | return Err(ClockError::BadConfig { | ||
| 1142 | clock: "spll", | ||
| 1143 | reason: "fout invalid", | ||
| 1144 | }); | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | // TODO: | ||
| 1148 | assert!(clk.frequency != 0); | ||
| 1149 | assert!(m >= 1); | ||
| 1150 | if let Some(p) = p.as_ref() { | ||
| 1151 | assert!(*p >= 1); | ||
| 1152 | } | ||
| 1153 | if let Some(n) = n.as_ref() { | ||
| 1154 | assert!(*n >= 1); | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | // A = floor(m / 4) + 1 | ||
| 1158 | let selp_a = (m / 4) + 1; | ||
| 1159 | // SELP = A if A < 31 | ||
| 1160 | // = 31 if A >= 31 | ||
| 1161 | let selp = selp_a.min(31); | ||
| 1162 | |||
| 1163 | // A = 1 if M >= 8000 | ||
| 1164 | // = floor(8000 / M) if 8000 > M >= 122 | ||
| 1165 | // = 2 x floor(M / 4) / 3 if 122 > M >= 1 | ||
| 1166 | let seli_a = if m >= 8000 { | ||
| 1167 | 1 | ||
| 1168 | } else if m >= 122 { | ||
| 1169 | 8000 / m | ||
| 1170 | } else { | ||
| 1171 | (2 * (m / 4)) / 3 | ||
| 1172 | }; | ||
| 1173 | // SELI = A if A < 63 | ||
| 1174 | // = 63 if A >= 63 | ||
| 1175 | let seli = seli_a.min(63); | ||
| 1176 | // SELR must be 0. | ||
| 1177 | let selr = 0; | ||
| 1178 | |||
| 1179 | self.scg0.spllctrl().modify(|_r, w| { | ||
| 1180 | w.source().variant(variant); | ||
| 1181 | unsafe { | ||
| 1182 | w.selp().bits(selp as u8); | ||
| 1183 | w.seli().bits(seli as u8); | ||
| 1184 | w.selr().bits(selr); | ||
| 1185 | } | ||
| 1186 | w | ||
| 1187 | }); | ||
| 1188 | |||
| 1189 | if let Some(n) = n { | ||
| 1190 | self.scg0.spllndiv().modify(|_r, w| unsafe { w.ndiv().bits(n) }); | ||
| 1191 | } | ||
| 1192 | if let Some(p) = p { | ||
| 1193 | self.scg0.spllpdiv().modify(|_r, w| unsafe { w.pdiv().bits(p) }); | ||
| 1194 | } | ||
| 1195 | self.scg0.spllmdiv().modify(|_r, w| unsafe { w.mdiv().bits(m) }); | ||
| 1196 | |||
| 1197 | self.scg0.spllctrl().modify(|_r, w| { | ||
| 1198 | w.bypassprediv().bit(bp_pre); | ||
| 1199 | w.bypasspostdiv().bit(bp_post); | ||
| 1200 | w.bypasspostdiv2().bit(bp_post2); | ||
| 1201 | |||
| 1202 | // TODO: support FRM? | ||
| 1203 | w.frm().disabled(); | ||
| 1204 | |||
| 1205 | w | ||
| 1206 | }); | ||
| 1207 | |||
| 1208 | // Unlock | ||
| 1209 | self.scg0.spllcsr().modify(|_r, w| w.lk().write_enabled()); | ||
| 1210 | |||
| 1211 | // TODO: Support clock monitors? | ||
| 1212 | // self.scg0.spllcsr().modify(|_r, w| w.spllcm().?); | ||
| 1213 | |||
| 1214 | self.scg0.trim_lock().write(|w| unsafe { | ||
| 1215 | w.trim_lock_key().bits(0x5a5a); | ||
| 1216 | w.trim_unlock().not_locked() | ||
| 1217 | }); | ||
| 1218 | |||
| 1219 | // SPLLLOCK_CNFG: The lock time programmed in this register must be | ||
| 1220 | // equal to meet the PLL 500μs lock time plus the 300 refclk count startup. | ||
| 1221 | // | ||
| 1222 | // LOCK_TIME = 500μs/T ref + 300, F ref = F in /N (input frequency divided by pre-divider ratio). | ||
| 1223 | // | ||
| 1224 | // 500us is 1/2000th of a second, therefore Fref / 2000 is the number of cycles in 500us. | ||
| 1225 | let f_ref = if let Some(n) = n { f_in / (n as u32) } else { f_in }; | ||
| 1226 | let lock_time = f_ref.div_ceil(2000) + 300; | ||
| 1227 | self.scg0 | ||
| 1228 | .splllock_cnfg() | ||
| 1229 | .write(|w| unsafe { w.lock_time().bits(lock_time) }); | ||
| 1230 | |||
| 1231 | // TODO: Support Spread spectrum? | ||
| 1232 | |||
| 1233 | self.scg0.spllcsr().modify(|_r, w| { | ||
| 1234 | w.spllclken().enabled(); | ||
| 1235 | w.spllpwren().enabled(); | ||
| 1236 | w.spllsten().bit(matches!(cfg.power, PoweredClock::AlwaysEnabled)); | ||
| 1237 | w | ||
| 1238 | }); | ||
| 1239 | |||
| 1240 | // Wait for SPLL to set up | ||
| 1241 | loop { | ||
| 1242 | let csr = self.scg0.spllcsr().read(); | ||
| 1243 | if csr.spll_lock().is_enabled_and_valid() { | ||
| 1244 | if csr.spllerr().is_enabled_and_error() { | ||
| 1245 | return Err(ClockError::BadConfig { | ||
| 1246 | clock: "spll", | ||
| 1247 | reason: "spllerr is set", | ||
| 1248 | }); | ||
| 1249 | } | ||
| 1250 | break; | ||
| 1251 | } | ||
| 1252 | } | ||
| 1253 | |||
| 1254 | // Re-lock SPLL CSR | ||
| 1255 | self.scg0.spllcsr().modify(|_r, w| w.lk().write_disabled()); | ||
| 1256 | |||
| 1257 | self.clocks.pll1_clk = Some(Clock { | ||
| 1258 | frequency: fout, | ||
| 1259 | power: cfg.power, | ||
| 1260 | }); | ||
| 1261 | |||
| 1262 | Ok(()) | ||
| 1263 | } | ||
| 978 | } | 1264 | } |
| 979 | 1265 | ||
| 980 | // | 1266 | // |
