From 801a36c7b4e476388e55c842dec09d7ef2607a5a Mon Sep 17 00:00:00 2001
From: Derek Hageman <hageman@inthat.cloud>
Date: Thu, 4 Jan 2024 07:52:53 -0700
Subject: [PATCH] stm32: Add G0 USB RCC

Add configuration for STM32G0 USB clock.
---
 embassy-stm32/src/rcc/g0.rs | 51 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs
index aedc95bf3..b38fe1dcc 100644
--- a/embassy-stm32/src/rcc/g0.rs
+++ b/embassy-stm32/src/rcc/g0.rs
@@ -72,6 +72,22 @@ pub enum PllSource {
     HSE(Hertz, HseMode),
 }
 
+/// Sets the source for the 48MHz clock to the USB peripheral.
+#[cfg(any(stm32g0b1, stm32g0c1, stm32g0b0))]
+pub enum UsbSrc {
+    /// Use the High Speed Internal Oscillator. The CRS must be used to calibrate the
+    /// oscillator to comply with the USB specification for oscillator tolerance.
+    #[cfg(any(stm32g0b1, stm32g0c1))]
+    Hsi48(super::Hsi48Config),
+    /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. The
+    /// PLL needs to be using the HSE source to comply with the USB specification for oscillator
+    /// tolerance.
+    PllQ,
+    /// Use the HSE source directly.  The HSE must be a 48MHz source.  The HSE source must comply
+    /// with the USB specification for oscillator tolerance.
+    HSE,
+}
+
 /// Clocks configutation
 pub struct Config {
     pub mux: ClockSrc,
@@ -79,6 +95,8 @@ pub struct Config {
     pub apb_pre: APBPrescaler,
     pub low_power_run: bool,
     pub ls: super::LsConfig,
+    #[cfg(any(stm32g0b1, stm32g0c1, stm32g0b0))]
+    pub usb_src: Option<UsbSrc>,
 }
 
 impl Default for Config {
@@ -90,6 +108,8 @@ impl Default for Config {
             apb_pre: APBPrescaler::DIV1,
             low_power_run: false,
             ls: Default::default(),
+            #[cfg(any(stm32g0b1, stm32g0c1, stm32g0b0))]
+            usb_src: None,
         }
     }
 }
@@ -303,13 +323,42 @@ pub(crate) unsafe fn init(config: Config) {
     let lsi_freq = (sw == Sw::LSI).then_some(super::LSI_FREQ);
     let hse_freq = (sw == Sw::HSE).then_some(sys_clk);
 
+    #[cfg(any(stm32g0b1, stm32g0c1, stm32g0b0))]
+    let hsi48_freq = config.usb_src.and_then(|config| {
+        match config {
+            UsbSrc::PllQ => {
+                // Make sure the PLLQ is enabled and running at 48Mhz
+                assert!(pll1_q_freq.is_some() && pll1_q_freq.unwrap().0 == 48_000_000);
+                RCC.ccipr2()
+                    .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::PLL1_Q));
+                None
+            }
+            UsbSrc::HSE => {
+                // Make sure the HSE is enabled and running at 48Mhz
+                assert!(hse_freq.is_some() && hse_freq.unwrap().0 == 48_000_000);
+                RCC.ccipr2()
+                    .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSE));
+                None
+            }
+            #[cfg(any(stm32g0b1, stm32g0c1))]
+            UsbSrc::Hsi48(config) => {
+                let freq = super::init_hsi48(config);
+                RCC.ccipr2()
+                    .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48));
+                Some(freq)
+            }
+        }
+    });
+    #[cfg(not(any(stm32g0b1, stm32g0c1, stm32g0b0)))]
+    let hsi48_freq: Option<Hertz> = None;
+
     set_freqs(Clocks {
         sys: sys_clk,
         hclk1: ahb_freq,
         pclk1: apb_freq,
         pclk1_tim: apb_tim_freq,
         hsi: hsi_freq,
-        hsi48: None,
+        hsi48: hsi48_freq,
         hsi_div_8: hsi_div_8_freq,
         hse: hse_freq,
         lse: lse_freq,