diff options
| -rw-r--r-- | embassy-stm32/src/rcc/g0.rs | 285 |
1 files changed, 280 insertions, 5 deletions
diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index ad5a661d6..be0497290 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw}; | 1 | use crate::pac::flash::vals::Latency; |
| 2 | use crate::pac::{PWR, RCC}; | 2 | use crate::pac::rcc::vals::{self, Hpre, Hsidiv, Ppre, Sw}; |
| 3 | use crate::pac::{FLASH, PWR, RCC}; | ||
| 3 | use crate::rcc::{set_freqs, Clocks}; | 4 | use crate::rcc::{set_freqs, Clocks}; |
| 4 | use crate::time::Hertz; | 5 | use crate::time::Hertz; |
| 5 | use crate::time::U32Ext; | 6 | use crate::time::U32Ext; |
| @@ -15,6 +16,7 @@ pub const LSI_FREQ: u32 = 32_000; | |||
| 15 | pub enum ClockSrc { | 16 | pub enum ClockSrc { |
| 16 | HSE(Hertz), | 17 | HSE(Hertz), |
| 17 | HSI16(HSI16Prescaler), | 18 | HSI16(HSI16Prescaler), |
| 19 | PLL(PllConfig), | ||
| 18 | LSI, | 20 | LSI, |
| 19 | } | 21 | } |
| 20 | 22 | ||
| @@ -45,6 +47,132 @@ impl Into<Hsidiv> for HSI16Prescaler { | |||
| 45 | } | 47 | } |
| 46 | } | 48 | } |
| 47 | 49 | ||
| 50 | /// The PLL configuration. | ||
| 51 | /// | ||
| 52 | /// * `VCOCLK = source / m * n` | ||
| 53 | /// * `PLLRCLK = VCOCLK / r` | ||
| 54 | /// * `PLLQCLK = VCOCLK / q` | ||
| 55 | /// * `PLLPCLK = VCOCLK / p` | ||
| 56 | #[derive(Clone, Copy)] | ||
| 57 | pub struct PllConfig { | ||
| 58 | /// The source from which the PLL receives a clock signal | ||
| 59 | pub source: PllSrc, | ||
| 60 | /// The initial divisor of that clock signal | ||
| 61 | pub m: Pllm, | ||
| 62 | /// The PLL VCO multiplier, which must be in the range `8..=86`. | ||
| 63 | pub n: u8, | ||
| 64 | /// The final divisor for `PLLRCLK` output which drives the system clock | ||
| 65 | pub r: Pllr, | ||
| 66 | |||
| 67 | /// The divisor for the `PLLQCLK` output, if desired | ||
| 68 | pub q: Option<Pllr>, | ||
| 69 | |||
| 70 | /// The divisor for the `PLLPCLK` output, if desired | ||
| 71 | pub p: Option<Pllr>, | ||
| 72 | } | ||
| 73 | |||
| 74 | impl Default for PllConfig { | ||
| 75 | #[inline] | ||
| 76 | fn default() -> PllConfig { | ||
| 77 | // HSI16 / 1 * 8 / 2 = 64 MHz | ||
| 78 | PllConfig { | ||
| 79 | source: PllSrc::HSI16, | ||
| 80 | m: Pllm::Div1, | ||
| 81 | n: 8, | ||
| 82 | r: Pllr::Div2, | ||
| 83 | q: None, | ||
| 84 | p: None, | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | #[derive(Clone, Copy, Eq, PartialEq)] | ||
| 90 | pub enum PllSrc { | ||
| 91 | HSI16, | ||
| 92 | HSE(Hertz), | ||
| 93 | } | ||
| 94 | |||
| 95 | #[derive(Clone, Copy)] | ||
| 96 | pub enum Pllm { | ||
| 97 | Div1, | ||
| 98 | Div2, | ||
| 99 | Div3, | ||
| 100 | Div4, | ||
| 101 | Div5, | ||
| 102 | Div6, | ||
| 103 | Div7, | ||
| 104 | Div8, | ||
| 105 | } | ||
| 106 | |||
| 107 | impl From<Pllm> for u8 { | ||
| 108 | fn from(v: Pllm) -> Self { | ||
| 109 | match v { | ||
| 110 | Pllm::Div1 => 0b000, | ||
| 111 | Pllm::Div2 => 0b001, | ||
| 112 | Pllm::Div3 => 0b010, | ||
| 113 | Pllm::Div4 => 0b011, | ||
| 114 | Pllm::Div5 => 0b100, | ||
| 115 | Pllm::Div6 => 0b101, | ||
| 116 | Pllm::Div7 => 0b110, | ||
| 117 | Pllm::Div8 => 0b111, | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | impl From<Pllm> for u32 { | ||
| 123 | fn from(v: Pllm) -> Self { | ||
| 124 | match v { | ||
| 125 | Pllm::Div1 => 1, | ||
| 126 | Pllm::Div2 => 2, | ||
| 127 | Pllm::Div3 => 3, | ||
| 128 | Pllm::Div4 => 4, | ||
| 129 | Pllm::Div5 => 5, | ||
| 130 | Pllm::Div6 => 6, | ||
| 131 | Pllm::Div7 => 7, | ||
| 132 | Pllm::Div8 => 8, | ||
| 133 | } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | #[derive(Clone, Copy)] | ||
| 138 | pub enum Pllr { | ||
| 139 | Div2, | ||
| 140 | Div3, | ||
| 141 | Div4, | ||
| 142 | Div5, | ||
| 143 | Div6, | ||
| 144 | Div7, | ||
| 145 | Div8, | ||
| 146 | } | ||
| 147 | |||
| 148 | impl From<Pllr> for u8 { | ||
| 149 | fn from(v: Pllr) -> Self { | ||
| 150 | match v { | ||
| 151 | Pllr::Div2 => 0b000, | ||
| 152 | Pllr::Div3 => 0b001, | ||
| 153 | Pllr::Div4 => 0b010, | ||
| 154 | Pllr::Div5 => 0b011, | ||
| 155 | Pllr::Div6 => 0b101, | ||
| 156 | Pllr::Div7 => 0b110, | ||
| 157 | Pllr::Div8 => 0b111, | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | impl From<Pllr> for u32 { | ||
| 163 | fn from(v: Pllr) -> Self { | ||
| 164 | match v { | ||
| 165 | Pllr::Div2 => 2, | ||
| 166 | Pllr::Div3 => 3, | ||
| 167 | Pllr::Div4 => 4, | ||
| 168 | Pllr::Div5 => 5, | ||
| 169 | Pllr::Div6 => 6, | ||
| 170 | Pllr::Div7 => 7, | ||
| 171 | Pllr::Div8 => 8, | ||
| 172 | } | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 48 | /// AHB prescaler | 176 | /// AHB prescaler |
| 49 | #[derive(Clone, Copy, PartialEq)] | 177 | #[derive(Clone, Copy, PartialEq)] |
| 50 | pub enum AHBPrescaler { | 178 | pub enum AHBPrescaler { |
| @@ -117,6 +245,95 @@ impl Default for Config { | |||
| 117 | } | 245 | } |
| 118 | } | 246 | } |
| 119 | 247 | ||
| 248 | impl PllConfig { | ||
| 249 | pub(crate) unsafe fn init(self) -> u32 { | ||
| 250 | assert!(self.n >= 8 && self.n <= 86); | ||
| 251 | let (src, input_freq) = match self.source { | ||
| 252 | PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ), | ||
| 253 | PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq.0), | ||
| 254 | }; | ||
| 255 | |||
| 256 | let m_freq = input_freq / u32::from(self.m); | ||
| 257 | // RM0454 § 5.4.4: | ||
| 258 | // > Caution: The software must set these bits so that the PLL input frequency after the | ||
| 259 | // > /M divider is between 2.66 and 16 MHz. | ||
| 260 | debug_assert!(m_freq >= 2_660_000 && m_freq <= 16_000_000); | ||
| 261 | |||
| 262 | let n_freq = m_freq * self.n as u32; | ||
| 263 | // RM0454 § 5.4.4: | ||
| 264 | // > Caution: The software must set these bits so that the VCO output frequency is between | ||
| 265 | // > 64 and 344 MHz. | ||
| 266 | debug_assert!(n_freq >= 64_000_000 && n_freq <= 344_000_000); | ||
| 267 | |||
| 268 | let r_freq = n_freq / u32::from(self.r); | ||
| 269 | // RM0454 § 5.4.4: | ||
| 270 | // > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock. | ||
| 271 | debug_assert!(r_freq <= 64_000_000); | ||
| 272 | |||
| 273 | // RM0454 § 5.2.3: | ||
| 274 | // > To modify the PLL configuration, proceed as follows: | ||
| 275 | // > 1. Disable the PLL by setting PLLON to 0 in Clock control register (RCC_CR). | ||
| 276 | RCC.cr().modify(|w| w.set_pllon(false)); | ||
| 277 | |||
| 278 | // > 2. Wait until PLLRDY is cleared. The PLL is now fully stopped. | ||
| 279 | while RCC.cr().read().pllrdy() {} | ||
| 280 | |||
| 281 | // > 3. Change the desired parameter. | ||
| 282 | // Enable whichever clock source we're using, and wait for it to become ready | ||
| 283 | match self.source { | ||
| 284 | PllSrc::HSI16 => { | ||
| 285 | RCC.cr().write(|w| w.set_hsion(true)); | ||
| 286 | while !RCC.cr().read().hsirdy() {} | ||
| 287 | } | ||
| 288 | PllSrc::HSE(_) => { | ||
| 289 | RCC.cr().write(|w| w.set_hseon(true)); | ||
| 290 | while !RCC.cr().read().hserdy() {} | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | // Configure PLLSYSCFGR | ||
| 295 | RCC.pllsyscfgr().modify(|w| { | ||
| 296 | w.set_pllr(u8::from(self.r)); | ||
| 297 | w.set_pllren(false); | ||
| 298 | |||
| 299 | if let Some(q) = self.q { | ||
| 300 | w.set_pllq(u8::from(q)); | ||
| 301 | } | ||
| 302 | w.set_pllqen(false); | ||
| 303 | |||
| 304 | if let Some(p) = self.p { | ||
| 305 | w.set_pllp(u8::from(p)); | ||
| 306 | } | ||
| 307 | w.set_pllpen(false); | ||
| 308 | |||
| 309 | w.set_plln(self.n); | ||
| 310 | |||
| 311 | w.set_pllm(self.m as u8); | ||
| 312 | |||
| 313 | w.set_pllsrc(src) | ||
| 314 | }); | ||
| 315 | |||
| 316 | // > 4. Enable the PLL again by setting PLLON to 1. | ||
| 317 | RCC.cr().modify(|w| w.set_pllon(true)); | ||
| 318 | |||
| 319 | // Wait for the PLL to become ready | ||
| 320 | while !RCC.cr().read().pllrdy() {} | ||
| 321 | |||
| 322 | // > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL | ||
| 323 | // > configuration register (RCC_PLLCFGR). | ||
| 324 | RCC.pllsyscfgr().modify(|w| { | ||
| 325 | // We'll use R for system clock, so enable that unconditionally | ||
| 326 | w.set_pllren(true); | ||
| 327 | |||
| 328 | // We may also use Q or P | ||
| 329 | w.set_pllqen(self.q.is_some()); | ||
| 330 | w.set_pllpen(self.p.is_some()); | ||
| 331 | }); | ||
| 332 | |||
| 333 | r_freq | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 120 | pub(crate) unsafe fn init(config: Config) { | 337 | pub(crate) unsafe fn init(config: Config) { |
| 121 | let (sys_clk, sw) = match config.mux { | 338 | let (sys_clk, sw) = match config.mux { |
| 122 | ClockSrc::HSI16(div) => { | 339 | ClockSrc::HSI16(div) => { |
| @@ -137,6 +354,10 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 137 | 354 | ||
| 138 | (freq.0, Sw::HSE) | 355 | (freq.0, Sw::HSE) |
| 139 | } | 356 | } |
| 357 | ClockSrc::PLL(pll) => { | ||
| 358 | let freq = pll.init(); | ||
| 359 | (freq, Sw::PLLRCLK) | ||
| 360 | } | ||
| 140 | ClockSrc::LSI => { | 361 | ClockSrc::LSI => { |
| 141 | // Enable LSI | 362 | // Enable LSI |
| 142 | RCC.csr().write(|w| w.set_lsion(true)); | 363 | RCC.csr().write(|w| w.set_lsion(true)); |
| @@ -145,12 +366,66 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 145 | } | 366 | } |
| 146 | }; | 367 | }; |
| 147 | 368 | ||
| 369 | // Determine the flash latency implied by the target clock speed | ||
| 370 | // RM0454 § 3.3.4: | ||
| 371 | let target_flash_latency = if sys_clk <= 24_000_000 { | ||
| 372 | Latency::WS0 | ||
| 373 | } else if sys_clk <= 48_000_000 { | ||
| 374 | Latency::WS1 | ||
| 375 | } else { | ||
| 376 | Latency::WS2 | ||
| 377 | }; | ||
| 378 | |||
| 379 | // Increase the number of cycles we wait for flash if the new value is higher | ||
| 380 | // There's no harm in waiting a little too much before the clock change, but we'll | ||
| 381 | // crash immediately if we don't wait enough after the clock change | ||
| 382 | let mut set_flash_latency_after = false; | ||
| 383 | FLASH.acr().modify(|w| { | ||
| 384 | // Is the current flash latency less than what we need at the new SYSCLK? | ||
| 385 | if w.latency().0 <= target_flash_latency.0 { | ||
| 386 | // We must increase the number of wait states now | ||
| 387 | w.set_latency(target_flash_latency) | ||
| 388 | } else { | ||
| 389 | // We may decrease the number of wait states later | ||
| 390 | set_flash_latency_after = true; | ||
| 391 | } | ||
| 392 | |||
| 393 | // RM0454 § 3.3.5: | ||
| 394 | // > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register | ||
| 395 | // > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the | ||
| 396 | // > Flash memory. | ||
| 397 | // | ||
| 398 | // Enable flash prefetching if we have at least one wait state, and disable it otherwise. | ||
| 399 | w.set_prften(target_flash_latency.0 > 0); | ||
| 400 | }); | ||
| 401 | |||
| 402 | if !set_flash_latency_after { | ||
| 403 | // Spin until the effective flash latency is compatible with the clock change | ||
| 404 | while FLASH.acr().read().latency().0 < target_flash_latency.0 {} | ||
| 405 | } | ||
| 406 | |||
| 407 | // Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once | ||
| 408 | let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into()); | ||
| 148 | RCC.cfgr().modify(|w| { | 409 | RCC.cfgr().modify(|w| { |
| 149 | w.set_sw(sw.into()); | 410 | w.set_sw(sw); |
| 150 | w.set_hpre(config.ahb_pre.into()); | 411 | w.set_hpre(hpre); |
| 151 | w.set_ppre(config.apb_pre.into()); | 412 | w.set_ppre(ppre); |
| 152 | }); | 413 | }); |
| 153 | 414 | ||
| 415 | if set_flash_latency_after { | ||
| 416 | // We can make the flash require fewer wait states | ||
| 417 | // Spin until the SYSCLK changes have taken effect | ||
| 418 | loop { | ||
| 419 | let cfgr = RCC.cfgr().read(); | ||
| 420 | if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre { | ||
| 421 | break; | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | // Set the flash latency to require fewer wait states | ||
| 426 | FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); | ||
| 427 | } | ||
| 428 | |||
| 154 | let ahb_div = match config.ahb_pre { | 429 | let ahb_div = match config.ahb_pre { |
| 155 | AHBPrescaler::NotDivided => 1, | 430 | AHBPrescaler::NotDivided => 1, |
| 156 | AHBPrescaler::Div2 => 2, | 431 | AHBPrescaler::Div2 => 2, |
