From 0cfe1dc9df9966ed36021869e9bf247aedfabc27 Mon Sep 17 00:00:00 2001
From: Joonas Javanainen <joonas.javanainen@gmail.com>
Date: Sat, 9 Apr 2022 17:40:04 +0300
Subject: [PATCH 1/4] Move HSE config out of main clock mux

This makes the configuration more flexible and closer to the underlying
configuration register structure. For example, we could use HSI for the
system clock, but use HSE to output a clock with MCO.
---
 embassy-stm32/src/rcc/f2.rs | 32 ++++++++++++++++++++++++--------
 1 file changed, 24 insertions(+), 8 deletions(-)

diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs
index bece046f8..e44613926 100644
--- a/embassy-stm32/src/rcc/f2.rs
+++ b/embassy-stm32/src/rcc/f2.rs
@@ -9,10 +9,16 @@ use crate::time::Hertz;
 /// HSI speed
 pub const HSI: Hertz = Hertz(16_000_000);
 
+#[derive(Clone, Copy)]
+pub struct HSEConfig {
+    pub frequency: Hertz,
+    pub source: HSESrc,
+}
+
 /// System clock mux source
 #[derive(Clone, Copy)]
 pub enum ClockSrc {
-    HSE(Hertz, HSESrc),
+    HSE,
     HSI,
 }
 
@@ -206,6 +212,7 @@ impl VoltageRange {
 
 /// Clocks configuration
 pub struct Config {
+    pub hse: Option<HSEConfig>,
     pub mux: ClockSrc,
     pub voltage: VoltageRange,
     pub ahb_pre: AHBPrescaler,
@@ -217,6 +224,7 @@ impl Default for Config {
     #[inline]
     fn default() -> Config {
         Config {
+            hse: None,
             voltage: VoltageRange::Min1V8,
             mux: ClockSrc::HSI,
             ahb_pre: AHBPrescaler::NotDivided,
@@ -238,18 +246,26 @@ unsafe fn enable_hse(source: HSESrc) {
     while !RCC.cr().read().hserdy() {}
 }
 
+#[inline]
+unsafe fn enable_hsi() {
+    RCC.cr().write(|w| w.set_hsion(true));
+    while !RCC.cr().read().hsirdy() {}
+}
+
 pub(crate) unsafe fn init(config: Config) {
+    if let Some(hse_config) = config.hse {
+        enable_hse(hse_config.source);
+    }
     let (sys_clk, sw) = match config.mux {
         ClockSrc::HSI => {
-            // Enable HSI
-            RCC.cr().write(|w| w.set_hsion(true));
-            while !RCC.cr().read().hsirdy() {}
-
+            enable_hsi();
             (HSI, Sw::HSI)
         }
-        ClockSrc::HSE(freq, source) => {
-            enable_hse(source);
-            (freq, Sw::HSE)
+        ClockSrc::HSE => {
+            let hse_config = config
+                .hse
+                .expect("HSE must be configured to be used as system clock");
+            (hse_config.frequency, Sw::HSE)
         }
     };
     // RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL

From 07ad52162ba05cec358c35375f1f5e91d0b398e9 Mon Sep 17 00:00:00 2001
From: Joonas Javanainen <joonas.javanainen@gmail.com>
Date: Tue, 26 Apr 2022 20:33:57 +0300
Subject: [PATCH 2/4] Add PLL config support for F2

---
 embassy-stm32/src/rcc/f2.rs  | 246 ++++++++++++++++++++++++++++++++---
 embassy-stm32/src/rcc/mod.rs |   2 +-
 2 files changed, 227 insertions(+), 21 deletions(-)

diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs
index e44613926..7074d7c3a 100644
--- a/embassy-stm32/src/rcc/f2.rs
+++ b/embassy-stm32/src/rcc/f2.rs
@@ -1,7 +1,8 @@
-use core::ops::Div;
+use core::convert::TryFrom;
+use core::ops::{Div, Mul};
 
 use crate::pac::flash::vals::Latency;
-use crate::pac::rcc::vals::{Hpre, Ppre, Sw};
+use crate::pac::rcc::vals::{Hpre, Pllp, Pllsrc, Ppre, Sw};
 use crate::pac::{FLASH, RCC};
 use crate::rcc::{set_freqs, Clocks};
 use crate::time::Hertz;
@@ -20,6 +21,7 @@ pub struct HSEConfig {
 pub enum ClockSrc {
     HSE,
     HSI,
+    PLL,
 }
 
 /// HSE clock source
@@ -31,6 +33,170 @@ pub enum HSESrc {
     Bypass,
 }
 
+#[derive(Clone, Copy)]
+pub struct PLLConfig {
+    pub pre_div: PLLPreDiv,
+    pub mul: PLLMul,
+    pub main_div: PLLMainDiv,
+    pub pll48_div: PLL48Div,
+}
+
+impl Default for PLLConfig {
+    fn default() -> Self {
+        PLLConfig {
+            pre_div: PLLPreDiv(16),
+            mul: PLLMul(192),
+            main_div: PLLMainDiv::Div2,
+            pll48_div: PLL48Div(4),
+        }
+    }
+}
+
+impl PLLConfig {
+    pub fn clocks(&self, src_freq: Hertz) -> PLLClocks {
+        let in_freq = src_freq / self.pre_div;
+        let vco_freq = src_freq * self.mul / self.pre_div;
+        let main_freq = vco_freq / self.main_div;
+        let pll48_freq = vco_freq / self.pll48_div;
+        PLLClocks {
+            in_freq,
+            vco_freq,
+            main_freq,
+            pll48_freq,
+        }
+    }
+}
+
+/// Clock source for both main PLL and PLLI2S
+#[derive(Clone, Copy, PartialEq)]
+pub enum PLLSrc {
+    HSE,
+    HSI,
+}
+
+impl Into<Pllsrc> for PLLSrc {
+    fn into(self) -> Pllsrc {
+        match self {
+            PLLSrc::HSE => Pllsrc::HSE,
+            PLLSrc::HSI => Pllsrc::HSI,
+        }
+    }
+}
+
+/// Division factor for both main PLL and PLLI2S
+#[derive(Clone, Copy, PartialEq)]
+#[repr(transparent)]
+pub struct PLLPreDiv(u8);
+
+impl TryFrom<u8> for PLLPreDiv {
+    type Error = &'static str;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            2..=63 => Ok(PLLPreDiv(value)),
+            _ => Err("PLLPreDiv must be within range 2..=63"),
+        }
+    }
+}
+
+impl Div<PLLPreDiv> for Hertz {
+    type Output = Hertz;
+
+    fn div(self, rhs: PLLPreDiv) -> Self::Output {
+        Hertz(self.0 / u32::from(rhs.0))
+    }
+}
+
+/// Multiplication factor for main PLL
+#[derive(Clone, Copy, PartialEq)]
+#[repr(transparent)]
+pub struct PLLMul(u16);
+
+impl Mul<PLLMul> for Hertz {
+    type Output = Hertz;
+
+    fn mul(self, rhs: PLLMul) -> Self::Output {
+        Hertz(self.0 * u32::from(rhs.0))
+    }
+}
+
+impl TryFrom<u16> for PLLMul {
+    type Error = &'static str;
+
+    fn try_from(value: u16) -> Result<Self, Self::Error> {
+        match value {
+            192..=432 => Ok(PLLMul(value)),
+            _ => Err("PLLMul must be within range 192..=432"),
+        }
+    }
+}
+
+/// PLL division factor for the main system clock
+#[derive(Clone, Copy, PartialEq)]
+pub enum PLLMainDiv {
+    Div2,
+    Div4,
+    Div6,
+    Div8,
+}
+
+impl Into<Pllp> for PLLMainDiv {
+    fn into(self) -> Pllp {
+        match self {
+            PLLMainDiv::Div2 => Pllp::DIV2,
+            PLLMainDiv::Div4 => Pllp::DIV4,
+            PLLMainDiv::Div6 => Pllp::DIV8,
+            PLLMainDiv::Div8 => Pllp::DIV8,
+        }
+    }
+}
+
+impl Div<PLLMainDiv> for Hertz {
+    type Output = Hertz;
+
+    fn div(self, rhs: PLLMainDiv) -> Self::Output {
+        let divisor = match rhs {
+            PLLMainDiv::Div2 => 2,
+            PLLMainDiv::Div4 => 4,
+            PLLMainDiv::Div6 => 6,
+            PLLMainDiv::Div8 => 8,
+        };
+        Hertz(self.0 / divisor)
+    }
+}
+
+/// PLL division factor for USB OTG FS / SDIO / RNG
+#[derive(Clone, Copy, PartialEq)]
+#[repr(transparent)]
+pub struct PLL48Div(u8);
+
+impl Div<PLL48Div> for Hertz {
+    type Output = Hertz;
+
+    fn div(self, rhs: PLL48Div) -> Self::Output {
+        Hertz(self.0 / u32::from(rhs.0))
+    }
+}
+
+impl TryFrom<u8> for PLL48Div {
+    type Error = &'static str;
+
+    fn try_from(value: u8) -> Result<Self, Self::Error> {
+        match value {
+            2..=15 => Ok(PLL48Div(value)),
+            _ => Err("PLL48Div must be within range 2..=15"),
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq)]
+pub struct PLLClocks {
+    pub in_freq: Hertz,
+    pub vco_freq: Hertz,
+    pub main_freq: Hertz,
+    pub pll48_freq: Hertz,
+}
+
 /// AHB prescaler
 #[derive(Clone, Copy, PartialEq)]
 pub enum AHBPrescaler {
@@ -213,6 +379,9 @@ impl VoltageRange {
 /// Clocks configuration
 pub struct Config {
     pub hse: Option<HSEConfig>,
+    pub hsi: bool,
+    pub pll_mux: PLLSrc,
+    pub pll: PLLConfig,
     pub mux: ClockSrc,
     pub voltage: VoltageRange,
     pub ahb_pre: AHBPrescaler,
@@ -225,6 +394,9 @@ impl Default for Config {
     fn default() -> Config {
         Config {
             hse: None,
+            hsi: true,
+            pll_mux: PLLSrc::HSI,
+            pll: PLLConfig::default(),
             voltage: VoltageRange::Min1V8,
             mux: ClockSrc::HSI,
             ahb_pre: AHBPrescaler::NotDivided,
@@ -234,31 +406,53 @@ impl Default for Config {
     }
 }
 
-#[inline]
-unsafe fn enable_hse(source: HSESrc) {
-    RCC.cr().write(|w| {
-        w.set_hsebyp(match source {
-            HSESrc::Bypass => true,
-            HSESrc::Crystal => false,
-        });
-        w.set_hseon(true)
-    });
-    while !RCC.cr().read().hserdy() {}
-}
-
-#[inline]
-unsafe fn enable_hsi() {
+pub(crate) unsafe fn init(config: Config) {
+    // Make sure HSI is enabled
     RCC.cr().write(|w| w.set_hsion(true));
     while !RCC.cr().read().hsirdy() {}
-}
 
-pub(crate) unsafe fn init(config: Config) {
     if let Some(hse_config) = config.hse {
-        enable_hse(hse_config.source);
+        RCC.cr().modify(|w| {
+            w.set_hsebyp(match hse_config.source {
+                HSESrc::Bypass => true,
+                HSESrc::Crystal => false,
+            });
+            w.set_hseon(true)
+        });
+        while !RCC.cr().read().hserdy() {}
     }
+
+    let pll_src_freq = match config.pll_mux {
+        PLLSrc::HSE => {
+            config
+                .hse
+                .expect("HSE must be configured to be used as PLL input")
+                .frequency
+        }
+        PLLSrc::HSI => HSI,
+    };
+
+    // Reference: STM32F215xx/217xx datasheet Table 33. Main PLL characteristics
+    let pll_clocks = config.pll.clocks(pll_src_freq);
+    assert!(Hertz(950_000) <= pll_clocks.in_freq && pll_clocks.in_freq <= Hertz(2_100_000));
+    assert!(Hertz(192_000_000) <= pll_clocks.vco_freq && pll_clocks.vco_freq <= Hertz(432_000_000));
+    assert!(
+        Hertz(24_000_000) <= pll_clocks.main_freq && pll_clocks.main_freq <= Hertz(120_000_000)
+    );
+    // USB actually requires == 48 MHz, but other PLL48 peripherals are fine with <= 48MHz
+    assert!(pll_clocks.pll48_freq <= Hertz(48_000_000));
+
+    RCC.pllcfgr().write(|w| {
+        w.set_pllsrc(config.pll_mux.into());
+        w.set_pllm(config.pll.pre_div.0);
+        w.set_plln(config.pll.mul.0);
+        w.set_pllp(config.pll.main_div.into());
+        w.set_pllq(config.pll.pll48_div.0);
+    });
+
     let (sys_clk, sw) = match config.mux {
         ClockSrc::HSI => {
-            enable_hsi();
+            assert!(config.hsi, "HSI must be enabled to be used as system clock");
             (HSI, Sw::HSI)
         }
         ClockSrc::HSE => {
@@ -267,6 +461,11 @@ pub(crate) unsafe fn init(config: Config) {
                 .expect("HSE must be configured to be used as system clock");
             (hse_config.frequency, Sw::HSE)
         }
+        ClockSrc::PLL => {
+            RCC.cr().modify(|w| w.set_pllon(true));
+            while !RCC.cr().read().pllrdy() {}
+            (pll_clocks.main_freq, Sw::PLL)
+        }
     };
     // RM0033 Figure 9. Clock tree suggests max SYSCLK/HCLK is 168 MHz, but datasheet specifies PLL
     // max output to be 120 MHz, so there's no way to get higher frequencies
@@ -285,6 +484,12 @@ pub(crate) unsafe fn init(config: Config) {
         w.set_ppre1(config.apb1_pre.into());
         w.set_ppre2(config.apb2_pre.into());
     });
+    while RCC.cfgr().read().sws() != sw.0 {}
+
+    // Turn off HSI to save power if we don't need it
+    if !config.hsi {
+        RCC.cr().modify(|w| w.set_hsion(false));
+    }
 
     let (apb1_freq, apb1_tim_freq) = match config.apb1_pre {
         APBPrescaler::NotDivided => (ahb_freq, ahb_freq),
@@ -315,5 +520,6 @@ pub(crate) unsafe fn init(config: Config) {
         apb1_tim: apb1_tim_freq,
         apb2: apb2_freq,
         apb2_tim: apb2_tim_freq,
+        pll48: Some(pll_clocks.pll48_freq),
     });
 }
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 9a95836a6..d3710b8c3 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -53,7 +53,7 @@ pub struct Clocks {
     #[cfg(any(rcc_h7, rcc_h7ab))]
     pub ahb4: Hertz,
 
-    #[cfg(any(rcc_f4, rcc_f410, rcc_f7))]
+    #[cfg(any(rcc_f2, rcc_f4, rcc_f410, rcc_f7))]
     pub pll48: Option<Hertz>,
 
     #[cfg(rcc_f1)]

From 1d5f9b86fb0dbfd1556f2e161d193f4475e2db6e Mon Sep 17 00:00:00 2001
From: Joonas Javanainen <joonas.javanainen@gmail.com>
Date: Fri, 29 Apr 2022 18:13:59 +0300
Subject: [PATCH 3/4] Add PLL example for F2

---
 examples/stm32f2/src/bin/pll.rs | 56 +++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 examples/stm32f2/src/bin/pll.rs

diff --git a/examples/stm32f2/src/bin/pll.rs b/examples/stm32f2/src/bin/pll.rs
new file mode 100644
index 000000000..348a583ab
--- /dev/null
+++ b/examples/stm32f2/src/bin/pll.rs
@@ -0,0 +1,56 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use core::convert::TryFrom;
+use defmt::*;
+use embassy::executor::Spawner;
+use embassy::time::{Duration, Timer};
+use embassy_stm32::{
+    rcc::{
+        APBPrescaler, ClockSrc, HSEConfig, HSESrc, PLL48Div, PLLConfig, PLLMainDiv, PLLMul,
+        PLLPreDiv, PLLSrc,
+    },
+    time::Hertz,
+    Config, Peripherals,
+};
+
+use defmt_rtt as _; // global logger
+use panic_probe as _;
+
+// Example config for maximum performance on a NUCLEO-F207ZG board
+fn config() -> Config {
+    let mut config = Config::default();
+    // By default, HSE on the board comes from a 8 MHz clock signal (not a crystal)
+    config.rcc.hse = Some(HSEConfig {
+        frequency: Hertz(8_000_000),
+        source: HSESrc::Bypass,
+    });
+    // PLL uses HSE as the clock source
+    config.rcc.pll_mux = PLLSrc::HSE;
+    config.rcc.pll = PLLConfig {
+        // 8 MHz clock source / 8 = 1 MHz PLL input
+        pre_div: PLLPreDiv::try_from(8).unwrap(),
+        // 1 MHz PLL input * 240 = 240 MHz PLL VCO
+        mul: PLLMul::try_from(240).unwrap(),
+        // 240 MHz PLL VCO / 2 = 120 MHz main PLL output
+        main_div: PLLMainDiv::Div2,
+        // 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
+        pll48_div: PLL48Div::try_from(5).unwrap(),
+    };
+    // System clock comes from PLL (= the 120 MHz main PLL output)
+    config.rcc.mux = ClockSrc::PLL;
+    // 120 MHz / 4 = 30 MHz APB1 frequency
+    config.rcc.apb1_pre = APBPrescaler::Div4;
+    // 120 MHz / 2 = 60 MHz APB2 frequency
+    config.rcc.apb2_pre = APBPrescaler::Div2;
+    config
+}
+
+#[embassy::main(config = "config()")]
+async fn main(_spawner: Spawner, _p: Peripherals) {
+    loop {
+        Timer::after(Duration::from_millis(1000)).await;
+        info!("1s elapsed");
+    }
+}

From e88559c5ca2450bbcfd6fe65e73fe0fe47465680 Mon Sep 17 00:00:00 2001
From: Joonas Javanainen <joonas.javanainen@gmail.com>
Date: Sat, 30 Apr 2022 11:41:17 +0300
Subject: [PATCH 4/4] Use defmt-friendly error handling

---
 embassy-stm32/src/rcc/f2.rs     | 10 +++++-----
 examples/stm32f2/src/bin/pll.rs |  6 +++---
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs
index 7074d7c3a..7e5992bba 100644
--- a/embassy-stm32/src/rcc/f2.rs
+++ b/embassy-stm32/src/rcc/f2.rs
@@ -424,10 +424,10 @@ pub(crate) unsafe fn init(config: Config) {
 
     let pll_src_freq = match config.pll_mux {
         PLLSrc::HSE => {
-            config
+            let hse_config = config
                 .hse
-                .expect("HSE must be configured to be used as PLL input")
-                .frequency
+                .unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
+            hse_config.frequency
         }
         PLLSrc::HSI => HSI,
     };
@@ -458,7 +458,7 @@ pub(crate) unsafe fn init(config: Config) {
         ClockSrc::HSE => {
             let hse_config = config
                 .hse
-                .expect("HSE must be configured to be used as system clock");
+                .unwrap_or_else(|| panic!("HSE must be configured to be used as PLL input"));
             (hse_config.frequency, Sw::HSE)
         }
         ClockSrc::PLL => {
@@ -475,7 +475,7 @@ pub(crate) unsafe fn init(config: Config) {
     // Reference: STM32F215xx/217xx datasheet Table 13. General operating conditions
     assert!(ahb_freq <= Hertz(120_000_000));
 
-    let flash_ws = config.voltage.wait_states(ahb_freq).expect("Invalid HCLK");
+    let flash_ws = unwrap!(config.voltage.wait_states(ahb_freq));
     FLASH.acr().modify(|w| w.set_latency(flash_ws));
 
     RCC.cfgr().modify(|w| {
diff --git a/examples/stm32f2/src/bin/pll.rs b/examples/stm32f2/src/bin/pll.rs
index 348a583ab..4bd74f0bd 100644
--- a/examples/stm32f2/src/bin/pll.rs
+++ b/examples/stm32f2/src/bin/pll.rs
@@ -30,13 +30,13 @@ fn config() -> Config {
     config.rcc.pll_mux = PLLSrc::HSE;
     config.rcc.pll = PLLConfig {
         // 8 MHz clock source / 8 = 1 MHz PLL input
-        pre_div: PLLPreDiv::try_from(8).unwrap(),
+        pre_div: unwrap!(PLLPreDiv::try_from(8)),
         // 1 MHz PLL input * 240 = 240 MHz PLL VCO
-        mul: PLLMul::try_from(240).unwrap(),
+        mul: unwrap!(PLLMul::try_from(240)),
         // 240 MHz PLL VCO / 2 = 120 MHz main PLL output
         main_div: PLLMainDiv::Div2,
         // 240 MHz PLL VCO / 5 = 48 MHz PLL48 output
-        pll48_div: PLL48Div::try_from(5).unwrap(),
+        pll48_div: unwrap!(PLL48Div::try_from(5)),
     };
     // System clock comes from PLL (= the 120 MHz main PLL output)
     config.rcc.mux = ClockSrc::PLL;