| 1 | pub use crate::pac::pwr::vals::Vos as VoltageScale; |
| 2 | #[cfg (all(peri_usb_otg_hs))] |
| 3 | pub use crate::pac::rcc::vals::Otghssel; |
| 4 | pub use crate::pac::rcc::vals::{ |
| 5 | Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, |
| 6 | Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, |
| 7 | }; |
| 8 | use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; |
| 9 | #[cfg (all(peri_usb_otg_hs))] |
| 10 | pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; |
| 11 | use crate::pac::{FLASH, PWR, RCC}; |
| 12 | use crate::rcc::LSI_FREQ; |
| 13 | use crate::time::Hertz; |
| 14 | |
| 15 | /// HSI speed |
| 16 | pub const HSI_FREQ: Hertz = Hertz(16_000_000); |
| 17 | |
| 18 | #[derive (Clone, Copy, Eq, PartialEq)] |
| 19 | pub enum HseMode { |
| 20 | /// crystal/ceramic oscillator (HSEBYP=0) |
| 21 | Oscillator, |
| 22 | /// external analog clock (low swing) (HSEBYP=1, HSEEXT=0) |
| 23 | Bypass, |
| 24 | /// external digital clock (full swing) (HSEBYP=1, HSEEXT=1) |
| 25 | BypassDigital, |
| 26 | } |
| 27 | |
| 28 | #[derive (Clone, Copy, Eq, PartialEq)] |
| 29 | pub struct Hse { |
| 30 | /// HSE frequency. |
| 31 | pub freq: Hertz, |
| 32 | /// HSE mode. |
| 33 | pub mode: HseMode, |
| 34 | } |
| 35 | |
| 36 | #[derive (Clone, Copy)] |
| 37 | pub struct Pll { |
| 38 | /// The clock source for the PLL. |
| 39 | pub source: PllSource, |
| 40 | /// The PLL pre-divider. |
| 41 | /// |
| 42 | /// The clock speed of the `source` divided by `m` must be between 4 and 16 MHz. |
| 43 | pub prediv: PllPreDiv, |
| 44 | /// The PLL multiplier. |
| 45 | /// |
| 46 | /// The multiplied clock – `source` divided by `m` times `n` – must be between 128 and 544 |
| 47 | /// MHz. The upper limit may be lower depending on the `Config { voltage_range }`. |
| 48 | pub mul: PllMul, |
| 49 | /// The divider for the P output. |
| 50 | /// |
| 51 | /// The P output is one of several options |
| 52 | /// that can be used to feed the SAI/MDF/ADF Clock mux's. |
| 53 | pub divp: Option<PllDiv>, |
| 54 | /// The divider for the Q output. |
| 55 | /// |
| 56 | /// The Q ouput is one of severals options that can be used to feed the 48MHz clocks |
| 57 | /// and the OCTOSPI clock. It may also be used on the MDF/ADF clock mux's. |
| 58 | pub divq: Option<PllDiv>, |
| 59 | /// The divider for the R output. |
| 60 | /// |
| 61 | /// When used to drive the system clock, `source` divided by `m` times `n` divided by `r` |
| 62 | /// must not exceed 160 MHz. System clocks above 55 MHz require a non-default |
| 63 | /// `Config { voltage_range }`. |
| 64 | pub divr: Option<PllDiv>, |
| 65 | } |
| 66 | |
| 67 | #[derive (Clone, Copy)] |
| 68 | pub struct Config { |
| 69 | // base clock sources |
| 70 | pub msis: Option<MSIRange>, |
| 71 | pub msik: Option<MSIRange>, |
| 72 | pub hsi: bool, |
| 73 | pub hse: Option<Hse>, |
| 74 | pub hsi48: Option<super::Hsi48Config>, |
| 75 | |
| 76 | // pll |
| 77 | pub pll1: Option<Pll>, |
| 78 | pub pll2: Option<Pll>, |
| 79 | pub pll3: Option<Pll>, |
| 80 | |
| 81 | // sysclk, buses. |
| 82 | pub sys: Sysclk, |
| 83 | pub ahb_pre: AHBPrescaler, |
| 84 | pub apb1_pre: APBPrescaler, |
| 85 | pub apb2_pre: APBPrescaler, |
| 86 | pub apb3_pre: APBPrescaler, |
| 87 | |
| 88 | /// The voltage range influences the maximum clock frequencies for different parts of the |
| 89 | /// device. In particular, system clocks exceeding 110 MHz require `RANGE1`, and system clocks |
| 90 | /// exceeding 55 MHz require at least `RANGE2`. |
| 91 | /// |
| 92 | /// See RM0456 § 10.5.4 for a general overview and § 11.4.10 for clock source frequency limits. |
| 93 | pub voltage_range: VoltageScale, |
| 94 | pub ls: super::LsConfig, |
| 95 | |
| 96 | /// Per-peripheral kernel clock selection muxes |
| 97 | pub mux: super::mux::ClockMux, |
| 98 | } |
| 99 | |
| 100 | impl Default for Config { |
| 101 | fn default() -> Self { |
| 102 | Self { |
| 103 | msis: Some(Msirange::RANGE_4MHZ), |
| 104 | msik: Some(Msirange::RANGE_4MHZ), |
| 105 | hse: None, |
| 106 | hsi: false, |
| 107 | hsi48: Some(Default::default()), |
| 108 | pll1: None, |
| 109 | pll2: None, |
| 110 | pll3: None, |
| 111 | sys: Sysclk::MSIS, |
| 112 | ahb_pre: AHBPrescaler::DIV1, |
| 113 | apb1_pre: APBPrescaler::DIV1, |
| 114 | apb2_pre: APBPrescaler::DIV1, |
| 115 | apb3_pre: APBPrescaler::DIV1, |
| 116 | voltage_range: VoltageScale::RANGE1, |
| 117 | ls: Default::default(), |
| 118 | mux: Default::default(), |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | pub(crate) unsafe fn init(config: Config) { |
| 124 | // Set the requested power mode |
| 125 | PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); |
| 126 | while !PWR.vosr().read().vosrdy() {} |
| 127 | |
| 128 | let msis = config.msis.map(|range| { |
| 129 | // Check MSI output per RM0456 § 11.4.10 |
| 130 | match config.voltage_range { |
| 131 | VoltageScale::RANGE4 => { |
| 132 | assert!(msirange_to_hertz(range).0 <= 24_000_000); |
| 133 | } |
| 134 | _ => {} |
| 135 | } |
| 136 | |
| 137 | // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range |
| 138 | loop { |
| 139 | let cr = RCC.cr().read(); |
| 140 | if cr.msison() == false || cr.msisrdy() == true { |
| 141 | break; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | RCC.icscr1().modify(|w| { |
| 146 | w.set_msisrange(range); |
| 147 | w.set_msirgsel(Msirgsel::ICSCR1); |
| 148 | }); |
| 149 | RCC.cr().write(|w| { |
| 150 | w.set_msipllen(false); |
| 151 | w.set_msison(true); |
| 152 | }); |
| 153 | while !RCC.cr().read().msisrdy() {} |
| 154 | msirange_to_hertz(range) |
| 155 | }); |
| 156 | |
| 157 | let msik = config.msik.map(|range| { |
| 158 | // Check MSI output per RM0456 § 11.4.10 |
| 159 | match config.voltage_range { |
| 160 | VoltageScale::RANGE4 => { |
| 161 | assert!(msirange_to_hertz(range).0 <= 24_000_000); |
| 162 | } |
| 163 | _ => {} |
| 164 | } |
| 165 | |
| 166 | // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range |
| 167 | loop { |
| 168 | let cr = RCC.cr().read(); |
| 169 | if cr.msikon() == false || cr.msikrdy() == true { |
| 170 | break; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | RCC.icscr1().modify(|w| { |
| 175 | w.set_msikrange(range); |
| 176 | w.set_msirgsel(Msirgsel::ICSCR1); |
| 177 | }); |
| 178 | RCC.cr().write(|w| { |
| 179 | w.set_msikon(true); |
| 180 | }); |
| 181 | while !RCC.cr().read().msikrdy() {} |
| 182 | msirange_to_hertz(range) |
| 183 | }); |
| 184 | |
| 185 | let hsi = config.hsi.then(|| { |
| 186 | RCC.cr().write(|w| w.set_hsion(true)); |
| 187 | while !RCC.cr().read().hsirdy() {} |
| 188 | |
| 189 | HSI_FREQ |
| 190 | }); |
| 191 | |
| 192 | let hse = config.hse.map(|hse| { |
| 193 | // Check frequency limits per RM456 § 11.4.10 |
| 194 | match config.voltage_range { |
| 195 | VoltageScale::RANGE1 | VoltageScale::RANGE2 | VoltageScale::RANGE3 => { |
| 196 | assert!(hse.freq.0 <= 50_000_000); |
| 197 | } |
| 198 | VoltageScale::RANGE4 => { |
| 199 | assert!(hse.freq.0 <= 25_000_000); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | // Enable HSE, and wait for it to stabilize |
| 204 | RCC.cr().write(|w| { |
| 205 | w.set_hseon(true); |
| 206 | w.set_hsebyp(hse.mode != HseMode::Oscillator); |
| 207 | w.set_hseext(match hse.mode { |
| 208 | HseMode::Oscillator | HseMode::Bypass => Hseext::ANALOG, |
| 209 | HseMode::BypassDigital => Hseext::DIGITAL, |
| 210 | }); |
| 211 | }); |
| 212 | while !RCC.cr().read().hserdy() {} |
| 213 | |
| 214 | hse.freq |
| 215 | }); |
| 216 | |
| 217 | let hsi48 = config.hsi48.map(super::init_hsi48); |
| 218 | |
| 219 | let pll_input = PllInput { hse, hsi, msi: msis }; |
| 220 | let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); |
| 221 | let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); |
| 222 | let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range); |
| 223 | |
| 224 | let sys_clk = match config.sys { |
| 225 | Sysclk::HSE => hse.unwrap(), |
| 226 | Sysclk::HSI => hsi.unwrap(), |
| 227 | Sysclk::MSIS => msis.unwrap(), |
| 228 | Sysclk::PLL1_R => pll1.r.unwrap(), |
| 229 | }; |
| 230 | |
| 231 | // Do we need the EPOD booster to reach the target clock speed per § 10.5.4? |
| 232 | if sys_clk >= Hertz::mhz(55) { |
| 233 | // Enable the booster |
| 234 | PWR.vosr().modify(|w| w.set_boosten(true)); |
| 235 | while !PWR.vosr().read().boostrdy() {} |
| 236 | } |
| 237 | |
| 238 | // The clock source is ready |
| 239 | // Calculate and set the flash wait states |
| 240 | let wait_states = match config.voltage_range { |
| 241 | // VOS 1 range VCORE 1.26V - 1.40V |
| 242 | VoltageScale::RANGE1 => match sys_clk.0 { |
| 243 | ..=32_000_000 => 0, |
| 244 | ..=64_000_000 => 1, |
| 245 | ..=96_000_000 => 2, |
| 246 | ..=128_000_000 => 3, |
| 247 | _ => 4, |
| 248 | }, |
| 249 | // VOS 2 range VCORE 1.15V - 1.26V |
| 250 | VoltageScale::RANGE2 => match sys_clk.0 { |
| 251 | ..=30_000_000 => 0, |
| 252 | ..=60_000_000 => 1, |
| 253 | ..=90_000_000 => 2, |
| 254 | _ => 3, |
| 255 | }, |
| 256 | // VOS 3 range VCORE 1.05V - 1.15V |
| 257 | VoltageScale::RANGE3 => match sys_clk.0 { |
| 258 | ..=24_000_000 => 0, |
| 259 | ..=48_000_000 => 1, |
| 260 | _ => 2, |
| 261 | }, |
| 262 | // VOS 4 range VCORE 0.95V - 1.05V |
| 263 | VoltageScale::RANGE4 => match sys_clk.0 { |
| 264 | ..=12_000_000 => 0, |
| 265 | _ => 1, |
| 266 | }, |
| 267 | }; |
| 268 | FLASH.acr().modify(|w| { |
| 269 | w.set_latency(wait_states); |
| 270 | }); |
| 271 | |
| 272 | // Switch the system clock source |
| 273 | RCC.cfgr1().modify(|w| w.set_sw(config.sys)); |
| 274 | while RCC.cfgr1().read().sws() != config.sys {} |
| 275 | |
| 276 | // Configure the bus prescalers |
| 277 | RCC.cfgr2().modify(|w| { |
| 278 | w.set_hpre(config.ahb_pre); |
| 279 | w.set_ppre1(config.apb1_pre); |
| 280 | w.set_ppre2(config.apb2_pre); |
| 281 | }); |
| 282 | RCC.cfgr3().modify(|w| { |
| 283 | w.set_ppre3(config.apb3_pre); |
| 284 | }); |
| 285 | |
| 286 | let hclk = sys_clk / config.ahb_pre; |
| 287 | |
| 288 | let hclk_max = match config.voltage_range { |
| 289 | VoltageScale::RANGE1 => Hertz::mhz(160), |
| 290 | VoltageScale::RANGE2 => Hertz::mhz(110), |
| 291 | VoltageScale::RANGE3 => Hertz::mhz(55), |
| 292 | VoltageScale::RANGE4 => Hertz::mhz(25), |
| 293 | }; |
| 294 | assert!(hclk <= hclk_max); |
| 295 | |
| 296 | let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); |
| 297 | let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); |
| 298 | let (pclk3, _) = super::util::calc_pclk(hclk, config.apb3_pre); |
| 299 | |
| 300 | let rtc = config.ls.init(); |
| 301 | |
| 302 | #[cfg (all(stm32u5, peri_usb_otg_hs))] |
| 303 | let usb_refck = match config.mux.otghssel { |
| 304 | Otghssel::HSE => hse, |
| 305 | Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), |
| 306 | Otghssel::PLL1_P => pll1.p, |
| 307 | Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8), |
| 308 | }; |
| 309 | #[cfg (all(stm32u5, peri_usb_otg_hs))] |
| 310 | let usb_refck_sel = match usb_refck { |
| 311 | Some(clk_val) => match clk_val { |
| 312 | Hertz(16_000_000) => Usbrefcksel::MHZ16, |
| 313 | Hertz(19_200_000) => Usbrefcksel::MHZ19_2, |
| 314 | Hertz(20_000_000) => Usbrefcksel::MHZ20, |
| 315 | Hertz(24_000_000) => Usbrefcksel::MHZ24, |
| 316 | Hertz(26_000_000) => Usbrefcksel::MHZ26, |
| 317 | Hertz(32_000_000) => Usbrefcksel::MHZ32, |
| 318 | _ => panic!("cannot select OTG_HS reference clock with source frequency of {} Hz, must be one of 16, 19.2, 20, 24, 26, 32 MHz" , clk_val), |
| 319 | }, |
| 320 | None => Usbrefcksel::MHZ24, |
| 321 | }; |
| 322 | #[cfg (all(stm32u5, peri_usb_otg_hs))] |
| 323 | SYSCFG.otghsphycr().modify(|w| { |
| 324 | w.set_clksel(usb_refck_sel); |
| 325 | }); |
| 326 | |
| 327 | let lse = config.ls.lse.map(|l| l.frequency); |
| 328 | let lsi = config.ls.lsi.then_some(LSI_FREQ); |
| 329 | |
| 330 | config.mux.init(); |
| 331 | |
| 332 | set_clocks!( |
| 333 | sys: Some(sys_clk), |
| 334 | hclk1: Some(hclk), |
| 335 | hclk2: Some(hclk), |
| 336 | hclk3: Some(hclk), |
| 337 | pclk1: Some(pclk1), |
| 338 | pclk2: Some(pclk2), |
| 339 | pclk3: Some(pclk3), |
| 340 | pclk1_tim: Some(pclk1_tim), |
| 341 | pclk2_tim: Some(pclk2_tim), |
| 342 | msik: msik, |
| 343 | hsi48: hsi48, |
| 344 | rtc: rtc, |
| 345 | lse: lse, |
| 346 | lsi: lsi, |
| 347 | hse: hse, |
| 348 | hse_div_2: hse.map(|clk| clk / 2u32), |
| 349 | hsi: hsi, |
| 350 | pll1_p: pll1.p, |
| 351 | pll1_p_div_2: pll1.p.map(|clk| clk / 2u32), |
| 352 | pll1_q: pll1.q, |
| 353 | pll1_r: pll1.r, |
| 354 | pll2_p: pll2.p, |
| 355 | pll2_q: pll2.q, |
| 356 | pll2_r: pll2.r, |
| 357 | pll3_p: pll3.p, |
| 358 | pll3_q: pll3.q, |
| 359 | pll3_r: pll3.r, |
| 360 | |
| 361 | #[cfg (dsihost)] |
| 362 | dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers |
| 363 | |
| 364 | // TODO |
| 365 | audioclk: None, |
| 366 | hsi48_div_2: None, |
| 367 | shsi: None, |
| 368 | shsi_div_2: None, |
| 369 | ); |
| 370 | } |
| 371 | |
| 372 | fn msirange_to_hertz(range: Msirange) -> Hertz { |
| 373 | match range { |
| 374 | Msirange::RANGE_48MHZ => Hertz(48_000_000), |
| 375 | Msirange::RANGE_24MHZ => Hertz(24_000_000), |
| 376 | Msirange::RANGE_16MHZ => Hertz(16_000_000), |
| 377 | Msirange::RANGE_12MHZ => Hertz(12_000_000), |
| 378 | Msirange::RANGE_4MHZ => Hertz(4_000_000), |
| 379 | Msirange::RANGE_2MHZ => Hertz(2_000_000), |
| 380 | Msirange::RANGE_1_33MHZ => Hertz(1_330_000), |
| 381 | Msirange::RANGE_1MHZ => Hertz(1_000_000), |
| 382 | Msirange::RANGE_3_072MHZ => Hertz(3_072_000), |
| 383 | Msirange::RANGE_1_536MHZ => Hertz(1_536_000), |
| 384 | Msirange::RANGE_1_024MHZ => Hertz(1_024_000), |
| 385 | Msirange::RANGE_768KHZ => Hertz(768_000), |
| 386 | Msirange::RANGE_400KHZ => Hertz(400_000), |
| 387 | Msirange::RANGE_200KHZ => Hertz(200_000), |
| 388 | Msirange::RANGE_133KHZ => Hertz(133_000), |
| 389 | Msirange::RANGE_100KHZ => Hertz(100_000), |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | pub(super) struct PllInput { |
| 394 | pub hsi: Option<Hertz>, |
| 395 | pub hse: Option<Hertz>, |
| 396 | pub msi: Option<Hertz>, |
| 397 | } |
| 398 | |
| 399 | #[allow (unused)] |
| 400 | #[derive (Default)] |
| 401 | pub(super) struct PllOutput { |
| 402 | pub p: Option<Hertz>, |
| 403 | pub q: Option<Hertz>, |
| 404 | pub r: Option<Hertz>, |
| 405 | } |
| 406 | |
| 407 | #[derive (PartialEq, Eq, Clone, Copy)] |
| 408 | enum PllInstance { |
| 409 | Pll1 = 0, |
| 410 | Pll2 = 1, |
| 411 | Pll3 = 2, |
| 412 | } |
| 413 | |
| 414 | fn pll_enable(instance: PllInstance, enabled: bool) { |
| 415 | RCC.cr().modify(|w: &mut Cr| w.set_pllon(n:instance as _, val:enabled)); |
| 416 | while RCC.cr().read().pllrdy(instance as _) != enabled {} |
| 417 | } |
| 418 | |
| 419 | fn init_pll(instance: PllInstance, config: Option<Pll>, input: &PllInput, voltage_range: VoltageScale) -> PllOutput { |
| 420 | // Disable PLL |
| 421 | pll_enable(instance, false); |
| 422 | |
| 423 | let Some(pll) = config else { return PllOutput::default() }; |
| 424 | |
| 425 | let src_freq = match pll.source { |
| 426 | PllSource::DISABLE => panic!("must not select PLL source as DISABLE" ), |
| 427 | PllSource::HSE => unwrap!(input.hse), |
| 428 | PllSource::HSI => unwrap!(input.hsi), |
| 429 | PllSource::MSIS => unwrap!(input.msi), |
| 430 | }; |
| 431 | |
| 432 | // Calculate the reference clock, which is the source divided by m |
| 433 | let ref_freq = src_freq / pll.prediv; |
| 434 | // Check limits per RM0456 § 11.4.6 |
| 435 | assert!(Hertz::mhz(4) <= ref_freq && ref_freq <= Hertz::mhz(16)); |
| 436 | |
| 437 | // Check PLL clocks per RM0456 § 11.4.10 |
| 438 | let (vco_min, vco_max, out_max) = match voltage_range { |
| 439 | VoltageScale::RANGE1 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(208)), |
| 440 | VoltageScale::RANGE2 => (Hertz::mhz(128), Hertz::mhz(544), Hertz::mhz(110)), |
| 441 | VoltageScale::RANGE3 => (Hertz::mhz(128), Hertz::mhz(330), Hertz::mhz(55)), |
| 442 | VoltageScale::RANGE4 => panic!("PLL is unavailable in voltage range 4" ), |
| 443 | }; |
| 444 | |
| 445 | // Calculate the PLL VCO clock |
| 446 | let vco_freq = ref_freq * pll.mul; |
| 447 | assert!(vco_freq >= vco_min && vco_freq <= vco_max); |
| 448 | |
| 449 | // Calculate output clocks. |
| 450 | let p = pll.divp.map(|div| vco_freq / div); |
| 451 | let q = pll.divq.map(|div| vco_freq / div); |
| 452 | let r = pll.divr.map(|div| vco_freq / div); |
| 453 | for freq in [p, q, r] { |
| 454 | if let Some(freq) = freq { |
| 455 | assert!(freq <= out_max); |
| 456 | } |
| 457 | } |
| 458 | |
| 459 | let divr = match instance { |
| 460 | PllInstance::Pll1 => RCC.pll1divr(), |
| 461 | PllInstance::Pll2 => RCC.pll2divr(), |
| 462 | PllInstance::Pll3 => RCC.pll3divr(), |
| 463 | }; |
| 464 | divr.write(|w| { |
| 465 | w.set_plln(pll.mul); |
| 466 | w.set_pllp(pll.divp.unwrap_or(PllDiv::DIV1)); |
| 467 | w.set_pllq(pll.divq.unwrap_or(PllDiv::DIV1)); |
| 468 | w.set_pllr(pll.divr.unwrap_or(PllDiv::DIV1)); |
| 469 | }); |
| 470 | |
| 471 | let input_range = match ref_freq.0 { |
| 472 | ..=8_000_000 => Pllrge::FREQ_4TO8MHZ, |
| 473 | _ => Pllrge::FREQ_8TO16MHZ, |
| 474 | }; |
| 475 | |
| 476 | macro_rules! write_fields { |
| 477 | ($w:ident) => { |
| 478 | $w.set_pllpen(pll.divp.is_some()); |
| 479 | $w.set_pllqen(pll.divq.is_some()); |
| 480 | $w.set_pllren(pll.divr.is_some()); |
| 481 | $w.set_pllm(pll.prediv); |
| 482 | $w.set_pllsrc(pll.source); |
| 483 | $w.set_pllrge(input_range); |
| 484 | }; |
| 485 | } |
| 486 | |
| 487 | match instance { |
| 488 | PllInstance::Pll1 => RCC.pll1cfgr().write(|w| { |
| 489 | // § 10.5.4: if we're targeting >= 55 MHz, we must configure PLL1MBOOST to a prescaler |
| 490 | // value that results in an output between 4 and 16 MHz for the PWR EPOD boost |
| 491 | if r.unwrap() >= Hertz::mhz(55) { |
| 492 | // source_clk can be up to 50 MHz, so there's just a few cases: |
| 493 | let mboost = match src_freq.0 { |
| 494 | ..=16_000_000 => Pllmboost::DIV1, // Bypass, giving EPOD 4-16 MHz |
| 495 | ..=32_000_000 => Pllmboost::DIV2, // Divide by 2, giving EPOD 8-16 MHz |
| 496 | _ => Pllmboost::DIV4, // Divide by 4, giving EPOD 8-12.5 MHz |
| 497 | }; |
| 498 | w.set_pllmboost(mboost); |
| 499 | } |
| 500 | write_fields!(w); |
| 501 | }), |
| 502 | PllInstance::Pll2 => RCC.pll2cfgr().write(|w| { |
| 503 | write_fields!(w); |
| 504 | }), |
| 505 | PllInstance::Pll3 => RCC.pll3cfgr().write(|w| { |
| 506 | write_fields!(w); |
| 507 | }), |
| 508 | } |
| 509 | |
| 510 | // Enable PLL |
| 511 | pll_enable(instance, true); |
| 512 | |
| 513 | PllOutput { p, q, r } |
| 514 | } |
| 515 | |