diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs
index 9401af4c3..ff8f97541 100644
--- a/embassy-stm32/src/rcc/g4.rs
+++ b/embassy-stm32/src/rcc/g4.rs
@@ -3,6 +3,7 @@ use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
 use stm32_metapac::FLASH;
 
 use crate::pac::{PWR, RCC};
+use crate::rcc::sealed::RccPeripheral;
 use crate::rcc::{set_freqs, Clocks};
 use crate::time::Hertz;
 
@@ -316,6 +317,27 @@ impl Into<Hpre> for AHBPrescaler {
     }
 }
 
+/// Sets the source for the 48MHz clock to the USB and RNG peripherals.
+pub enum Clock48MhzSrc {
+    /// Use the High Speed Internal Oscillator. For USB usage, the CRS must be used to calibrate the
+    /// oscillator to comply with the USB specification for oscillator tolerance.
+    Hsi48(Option<CrsConfig>),
+    /// Use the PLLQ output. The PLL must be configured to output a 48MHz clock. For USB usage the
+    /// PLL needs to be using the HSE source to comply with the USB specification for oscillator
+    /// tolerance.
+    PllQ,
+}
+
+/// Sets the sync source for the Clock Recovery System (CRS).
+pub enum CrsSyncSource {
+    /// Use an external GPIO to sync the CRS.
+    Gpio,
+    /// Use the Low Speed External oscillator to sync the CRS.
+    Lse,
+    /// Use the USB SOF to sync the CRS.
+    Usb,
+}
+
 /// Clocks configutation
 pub struct Config {
     pub mux: ClockSrc,
@@ -326,6 +348,14 @@ pub struct Config {
     /// Iff PLL is requested as the main clock source in the `mux` field then the PLL configuration
     /// MUST turn on the PLLR output.
     pub pll: Option<Pll>,
+    /// Sets the clock source for the 48MHz clock used by the USB and RNG peripherals.
+    pub clock_48mhz_src: Option<Clock48MhzSrc>,
+}
+
+/// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
+pub struct CrsConfig {
+    /// Sync source for the CRS.
+    pub sync_src: CrsSyncSource,
 }
 
 impl Default for Config {
@@ -338,6 +368,7 @@ impl Default for Config {
             apb2_pre: APBPrescaler::NotDivided,
             low_power_run: false,
             pll: None,
+            clock_48mhz_src: None,
         }
     }
 }
@@ -430,7 +461,7 @@ pub(crate) unsafe fn init(config: Config) {
             assert!(pll_freq.is_some());
             assert!(pll_freq.as_ref().unwrap().pll_r.is_some());
 
-            let freq = pll_freq.unwrap().pll_r.unwrap().0;
+            let freq = pll_freq.as_ref().unwrap().pll_r.unwrap().0;
 
             assert!(freq <= 170_000_000);
 
@@ -497,6 +528,50 @@ pub(crate) unsafe fn init(config: Config) {
         }
     };
 
+    // Setup the 48 MHz clock if needed
+    if let Some(clock_48mhz_src) = config.clock_48mhz_src {
+        let source = match clock_48mhz_src {
+            Clock48MhzSrc::PllQ => {
+                // Make sure the PLLQ is enabled and running at 48Mhz
+                let pllq_freq = pll_freq.as_ref().and_then(|f| f.pll_q);
+                assert!(pllq_freq.is_some() && pllq_freq.unwrap().0 == 48_000_000);
+
+                crate::pac::rcc::vals::Clk48sel::PLLQCLK
+            }
+            Clock48MhzSrc::Hsi48(crs_config) => {
+                // Enable HSI48
+                RCC.crrcr().modify(|w| w.set_hsi48on(true));
+                // Wait for HSI48 to turn on
+                while RCC.crrcr().read().hsi48rdy() == false {}
+
+                // Enable and setup CRS if needed
+                if let Some(crs_config) = crs_config {
+                    crate::peripherals::CRS::enable();
+
+                    let sync_src = match crs_config.sync_src {
+                        CrsSyncSource::Gpio => crate::pac::crs::vals::Syncsrc::GPIO,
+                        CrsSyncSource::Lse => crate::pac::crs::vals::Syncsrc::LSE,
+                        CrsSyncSource::Usb => crate::pac::crs::vals::Syncsrc::USB,
+                    };
+
+                    crate::pac::CRS.cfgr().modify(|w| {
+                        w.set_syncsrc(sync_src);
+                    });
+
+                    // These are the correct settings for standard USB operation. If other settings
+                    // are needed there will need to be additional config options for the CRS.
+                    crate::pac::CRS.cr().modify(|w| {
+                        w.set_autotrimen(true);
+                        w.set_cen(true);
+                    });
+                }
+                crate::pac::rcc::vals::Clk48sel::HSI48
+            }
+        };
+
+        RCC.ccipr().modify(|w| w.set_clk48sel(source));
+    }
+
     if config.low_power_run {
         assert!(sys_clk <= 2_000_000);
         PWR.cr1().modify(|w| w.set_lpr(true));
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs
index 289d0ed86..77cfa67d3 100644
--- a/examples/stm32g4/src/bin/usb_serial.rs
+++ b/examples/stm32g4/src/bin/usb_serial.rs
@@ -4,10 +4,10 @@
 
 use defmt::{panic, *};
 use embassy_executor::Spawner;
-use embassy_stm32::rcc::{ClockSrc, Pll, PllM, PllN, PllQ, PllR, PllSrc};
+use embassy_stm32::rcc::{Clock48MhzSrc, ClockSrc, CrsConfig, CrsSyncSource, Pll, PllM, PllN, PllQ, PllR, PllSrc};
 use embassy_stm32::time::Hertz;
 use embassy_stm32::usb::{self, Driver, Instance};
-use embassy_stm32::{bind_interrupts, pac, peripherals, Config};
+use embassy_stm32::{bind_interrupts, peripherals, Config};
 use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
 use embassy_usb::driver::EndpointError;
 use embassy_usb::Builder;
@@ -22,25 +22,35 @@ bind_interrupts!(struct Irqs {
 async fn main(_spawner: Spawner) {
     let mut config = Config::default();
 
+    // Change this to `false` to use the HSE clock source for the USB. This example assumes an 8MHz HSE.
+    const USE_HSI48: bool = true;
+
+    let pllq_div = if USE_HSI48 { None } else { Some(PllQ::Div6) };
+
     config.rcc.pll = Some(Pll {
-        source: PllSrc::HSE(Hertz(8000000)),
+        source: PllSrc::HSE(Hertz(8_000_000)),
         prediv_m: PllM::Div2,
         mul_n: PllN::Mul72,
         div_p: None,
-        // USB and CAN at 48 MHz
-        div_q: Some(PllQ::Div6),
+        div_q: pllq_div,
         // Main system clock at 144 MHz
         div_r: Some(PllR::Div2),
     });
 
     config.rcc.mux = ClockSrc::PLL;
 
-    let p = embassy_stm32::init(config);
-    info!("Hello World!");
+    if USE_HSI48 {
+        // Sets up the Clock Recovery System (CRS) to use the USB SOF to trim the HSI48 oscillator.
+        config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::Hsi48(Some(CrsConfig {
+            sync_src: CrsSyncSource::Usb,
+        })));
+    } else {
+        config.rcc.clock_48mhz_src = Some(Clock48MhzSrc::PllQ);
+    }
 
-    pac::RCC.ccipr().write(|w| {
-        w.set_clk48sel(pac::rcc::vals::Clk48sel::PLLQCLK);
-    });
+    let p = embassy_stm32::init(config);
+
+    info!("Hello World!");
 
     let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);