diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index d169107df..306945962 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -58,7 +58,7 @@ sdio-host = "0.5.0"
 embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true }
 critical-section = "1.1"
 atomic-polyfill = "1.0.1"
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3" }
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7e2310f49fa123fbc3225b91be73522b212703f0" }
 vcell = "0.1.3"
 bxcan = "0.7.0"
 nb = "1.0.0"
@@ -77,7 +77,7 @@ critical-section = { version = "1.1", features = ["std"] }
 [build-dependencies]
 proc-macro2 = "1.0.36"
 quote = "1.0.15"
-stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9a61a1f090462df8bd1751f89951f04934fdceb3", default-features = false, features = ["metadata"]}
+stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7e2310f49fa123fbc3225b91be73522b212703f0", default-features = false, features = ["metadata"]}
 
 [features]
 default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index d3bfd24fb..bb81736ba 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -308,20 +308,11 @@ fn main() {
     // ========
     // Generate RccPeripheral impls
 
-    // TODO: maybe get this from peripheral kind? Not sure
-    let mut refcounted_peripherals = HashSet::from(["usart"]);
+    let refcounted_peripherals = HashSet::from(["usart", "adc"]);
     let mut refcount_statics = HashSet::new();
 
-    if chip_name.starts_with("stm32f3") {
-        refcounted_peripherals.insert("adc");
-    }
-
     for p in METADATA.peripherals {
-        // generating RccPeripheral impl for H7 ADC3 would result in bad frequency
-        if !singletons.contains(&p.name.to_string())
-            || (p.name == "ADC3" && METADATA.line.starts_with("STM32H7"))
-            || (p.name.starts_with("ADC") && p.registers.as_ref().map_or(false, |r| r.version == "v4"))
-        {
+        if !singletons.contains(&p.name.to_string()) {
             continue;
         }
 
@@ -711,6 +702,10 @@ fn main() {
 
                 // ADC is special
                 if regs.kind == "adc" {
+                    if p.rcc.is_none() {
+                        continue;
+                    }
+
                     let peri = format_ident!("{}", p.name);
                     let pin_name = format_ident!("{}", pin.pin);
 
diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs
index a127445d8..0eeadfa93 100644
--- a/embassy-stm32/src/adc/mod.rs
+++ b/embassy-stm32/src/adc/mod.rs
@@ -56,7 +56,7 @@ pub trait Instance: sealed::Instance + crate::Peripheral<P = Self> + crate::rcc:
 pub trait AdcPin<T: Instance>: sealed::AdcPin<T> {}
 pub trait InternalChannel<T>: sealed::InternalChannel<T> {}
 
-#[cfg(not(any(stm32h7, adc_f3)))]
+#[cfg(not(any(stm32h7, adc_f3, adc_v4)))]
 foreach_peripheral!(
     (adc, $inst:ident) => {
         impl crate::adc::sealed::Instance for peripherals::$inst {
@@ -77,9 +77,10 @@ foreach_peripheral!(
     };
 );
 
-#[cfg(any(stm32h7, adc_f3))]
+#[cfg(any(stm32h7, adc_f3, adc_v4))]
 foreach_peripheral!(
     (adc, ADC3) => {
+        #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
         impl crate::adc::sealed::Instance for peripherals::ADC3 {
             fn regs() -> crate::pac::adc::Adc {
                 crate::pac::ADC3
@@ -99,9 +100,11 @@ foreach_peripheral!(
             }
         }
 
+        #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
         impl crate::adc::Instance for peripherals::ADC3 {}
     };
     (adc, ADC4) => {
+        #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
         impl crate::adc::sealed::Instance for peripherals::ADC4 {
             fn regs() -> crate::pac::adc::Adc {
                 crate::pac::ADC4
@@ -121,7 +124,11 @@ foreach_peripheral!(
             }
         }
 
+        #[cfg(not(any(stm32g4x1, stm32g4x2, stm32g4x3, stm32g4x4)))]
         impl crate::adc::Instance for peripherals::ADC4 {}
+    };
+    (adc, ADC5) => {
+
     };
     (adc, $inst:ident) => {
         impl crate::adc::sealed::Instance for peripherals::$inst {
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index 64d0f0c75..655c0cb6a 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -1,6 +1,5 @@
-use core::sync::atomic::{AtomicU8, Ordering};
-
 use embedded_hal_02::blocking::delay::DelayUs;
+#[allow(unused)]
 use pac::adc::vals::{Adcaldif, Boost, Difsel, Exten, Pcsel};
 use pac::adccommon::vals::Presc;
 
@@ -13,12 +12,31 @@ pub const VREF_DEFAULT_MV: u32 = 3300;
 /// VREF voltage used for factory calibration of VREFINTCAL register.
 pub const VREF_CALIB_MV: u32 = 3300;
 
-// NOTE: Vrefint/Temperature/Vbat are only available on ADC3 on H7, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
+/// Max single ADC operation clock frequency
+#[cfg(stm32g4)]
+const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
+#[cfg(stm32h7)]
+const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50);
+
+#[cfg(stm32g4)]
+const VREF_CHANNEL: u8 = 18;
+#[cfg(stm32g4)]
+const TEMP_CHANNEL: u8 = 16;
+
+#[cfg(stm32h7)]
+const VREF_CHANNEL: u8 = 19;
+#[cfg(stm32h7)]
+const TEMP_CHANNEL: u8 = 18;
+
+// TODO this should be 14 for H7a/b/35
+const VBAT_CHANNEL: u8 = 17;
+
+// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs
 pub struct VrefInt;
 impl<T: Instance> InternalChannel<T> for VrefInt {}
 impl<T: Instance> super::sealed::InternalChannel<T> for VrefInt {
     fn channel(&self) -> u8 {
-        19
+        VREF_CHANNEL
     }
 }
 
@@ -26,7 +44,7 @@ pub struct Temperature;
 impl<T: Instance> InternalChannel<T> for Temperature {}
 impl<T: Instance> super::sealed::InternalChannel<T> for Temperature {
     fn channel(&self) -> u8 {
-        18
+        TEMP_CHANNEL
     }
 }
 
@@ -34,128 +52,10 @@ pub struct Vbat;
 impl<T: Instance> InternalChannel<T> for Vbat {}
 impl<T: Instance> super::sealed::InternalChannel<T> for Vbat {
     fn channel(&self) -> u8 {
-        // TODO this should be 14 for H7a/b/35
-        17
+        VBAT_CHANNEL
     }
 }
 
-static ADC12_ENABLE_COUNTER: AtomicU8 = AtomicU8::new(0);
-
-#[cfg(stm32h7)]
-foreach_peripheral!(
-    (adc, ADC1) => {
-        impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 {
-            fn frequency() -> crate::time::Hertz {
-                critical_section::with(|_| {
-                    match unsafe { crate::rcc::get_freqs() }.adc {
-                        Some(ck) => ck,
-                        None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.")
-                    }
-                })
-            }
-
-            fn enable() {
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true))
-                });
-                ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst);
-            }
-
-            fn disable() {
-                if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| {
-                        crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false));
-                    })
-                }
-                ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst);
-            }
-
-            fn reset() {
-                if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| {
-                        crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true));
-                        crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false));
-                    });
-                }
-            }
-        }
-
-        impl crate::rcc::RccPeripheral for crate::peripherals::ADC1 {}
-    };
-    (adc, ADC2) => {
-        impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 {
-            fn frequency() -> crate::time::Hertz {
-                critical_section::with(|_| {
-                    match unsafe { crate::rcc::get_freqs() }.adc {
-                        Some(ck) => ck,
-                        None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.")
-                    }
-                })
-            }
-
-            fn enable() {
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true))
-                });
-                ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst);
-            }
-
-            fn disable() {
-                if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| {
-                        crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false));
-                    })
-                }
-                ADC12_ENABLE_COUNTER.fetch_sub(1, Ordering::SeqCst);
-            }
-
-            fn reset() {
-                if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| {
-                        crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true));
-                        crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false));
-                    });
-                }
-            }
-        }
-
-        impl crate::rcc::RccPeripheral for crate::peripherals::ADC2 {}
-    };
-    (adc, ADC3) => {
-        impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 {
-            fn frequency() -> crate::time::Hertz {
-                critical_section::with(|_| {
-                    match unsafe { crate::rcc::get_freqs() }.adc {
-                        Some(ck) => ck,
-                        None => panic!("Invalid ADC clock configuration, AdcClockSource was likely not properly configured.")
-                    }
-                })
-            }
-
-            fn enable() {
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true))
-                });
-            }
-
-            fn disable() {
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false));
-                })
-            }
-
-            fn reset() {
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true));
-                    crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false));
-                });
-            }
-        }
-
-        impl crate::rcc::RccPeripheral for crate::peripherals::ADC3 {}
-    };
-);
-
 // NOTE (unused): The prescaler enum closely copies the hardware capabilities,
 // but high prescaling doesn't make a lot of sense in the current implementation and is ommited.
 #[allow(unused)]
@@ -176,7 +76,7 @@ enum Prescaler {
 
 impl Prescaler {
     fn from_ker_ck(frequency: Hertz) -> Self {
-        let raw_prescaler = frequency.0 / 50_000_000;
+        let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0;
         match raw_prescaler {
             0 => Self::NotDivided,
             1 => Self::DividedBy2,
@@ -237,20 +137,23 @@ impl<'d, T: Instance> Adc<'d, T> {
         let frequency = Hertz(T::frequency().0 / prescaler.divisor());
         info!("ADC frequency set to {} Hz", frequency.0);
 
-        if frequency > Hertz::mhz(50) {
-            panic!("Maximal allowed frequency for the ADC is 50 MHz and it varies with different packages, refer to ST docs for more information.");
+        if frequency > MAX_ADC_CLK_FREQ {
+            panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 /  1_000_000 );
         }
-        let boost = if frequency < Hertz::khz(6_250) {
-            Boost::LT6_25
-        } else if frequency < Hertz::khz(12_500) {
-            Boost::LT12_5
-        } else if frequency < Hertz::mhz(25) {
-            Boost::LT25
-        } else {
-            Boost::LT50
-        };
-        T::regs().cr().modify(|w| w.set_boost(boost));
 
+        #[cfg(stm32h7)]
+        {
+            let boost = if frequency < Hertz::khz(6_250) {
+                Boost::LT6_25
+            } else if frequency < Hertz::khz(12_500) {
+                Boost::LT12_5
+            } else if frequency < Hertz::mhz(25) {
+                Boost::LT25
+            } else {
+                Boost::LT50
+            };
+            T::regs().cr().modify(|w| w.set_boost(boost));
+        }
         let mut s = Self {
             adc,
             sample_time: Default::default(),
@@ -379,10 +282,14 @@ impl<'d, T: Instance> Adc<'d, T> {
         // Configure channel
         Self::set_channel_sample_time(channel, self.sample_time);
 
-        T::regs().cfgr2().modify(|w| w.set_lshift(0));
-        T::regs()
-            .pcsel()
-            .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
+        #[cfg(stm32h7)]
+        {
+            T::regs().cfgr2().modify(|w| w.set_lshift(0));
+            T::regs()
+                .pcsel()
+                .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED));
+        }
+
         T::regs().sqr1().write(|reg| {
             reg.set_sq(0, channel);
             reg.set_l(0);
diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs
index 3b044cd11..2359f39c1 100644
--- a/embassy-stm32/src/rcc/g4.rs
+++ b/embassy-stm32/src/rcc/g4.rs
@@ -1,5 +1,5 @@
 use stm32_metapac::flash::vals::Latency;
-use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw};
+use stm32_metapac::rcc::vals::{Adcsel, Hpre, Pllsrc, Ppre, Sw};
 use stm32_metapac::FLASH;
 
 pub use super::bus::{AHBPrescaler, APBPrescaler};
@@ -14,6 +14,29 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000);
 /// LSI speed
 pub const LSI_FREQ: Hertz = Hertz(32_000);
 
+#[derive(Clone, Copy)]
+pub enum AdcClockSource {
+    NoClk,
+    SysClk,
+    PllP,
+}
+
+impl AdcClockSource {
+    pub fn adcsel(&self) -> Adcsel {
+        match self {
+            AdcClockSource::NoClk => Adcsel::NOCLK,
+            AdcClockSource::SysClk => Adcsel::SYSCLK,
+            AdcClockSource::PllP => Adcsel::PLLP,
+        }
+    }
+}
+
+impl Default for AdcClockSource {
+    fn default() -> Self {
+        Self::NoClk
+    }
+}
+
 /// System clock mux source
 #[derive(Clone, Copy)]
 pub enum ClockSrc {
@@ -327,6 +350,8 @@ pub struct Config {
     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>,
+    pub adc12_clock_source: AdcClockSource,
+    pub adc345_clock_source: AdcClockSource,
 }
 
 /// Configuration for the Clock Recovery System (CRS) used to trim the HSI48 oscillator.
@@ -346,6 +371,8 @@ impl Default for Config {
             low_power_run: false,
             pll: None,
             clock_48mhz_src: None,
+            adc12_clock_source: Default::default(),
+            adc345_clock_source: Default::default(),
         }
     }
 }
@@ -549,6 +576,29 @@ pub(crate) unsafe fn init(config: Config) {
         RCC.ccipr().modify(|w| w.set_clk48sel(source));
     }
 
+    RCC.ccipr()
+        .modify(|w| w.set_adc12sel(config.adc12_clock_source.adcsel()));
+    RCC.ccipr()
+        .modify(|w| w.set_adc345sel(config.adc345_clock_source.adcsel()));
+
+    let adc12_ck = match config.adc12_clock_source {
+        AdcClockSource::NoClk => None,
+        AdcClockSource::PllP => match &pll_freq {
+            Some(pll) => pll.pll_p,
+            None => None,
+        },
+        AdcClockSource::SysClk => Some(Hertz(sys_clk)),
+    };
+
+    let adc345_ck = match config.adc345_clock_source {
+        AdcClockSource::NoClk => None,
+        AdcClockSource::PllP => match &pll_freq {
+            Some(pll) => pll.pll_p,
+            None => None,
+        },
+        AdcClockSource::SysClk => Some(Hertz(sys_clk)),
+    };
+
     if config.low_power_run {
         assert!(sys_clk <= 2_000_000);
         PWR.cr1().modify(|w| w.set_lpr(true));
@@ -562,5 +612,7 @@ pub(crate) unsafe fn init(config: Config) {
         apb1_tim: Hertz(apb1_tim_freq),
         apb2: Hertz(apb2_freq),
         apb2_tim: Hertz(apb2_tim_freq),
+        adc: adc12_ck,
+        adc34: adc345_ck,
     });
 }
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index 2e1f60358..535ab6ad4 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -74,10 +74,10 @@ pub struct Clocks {
     #[cfg(stm32f1)]
     pub adc: Hertz,
 
-    #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3))]
+    #[cfg(any(rcc_h5, rcc_h50, rcc_h7, rcc_h7ab, rcc_f3, rcc_g4))]
     pub adc: Option<Hertz>,
 
-    #[cfg(rcc_f3)]
+    #[cfg(any(rcc_f3, rcc_g4))]
     pub adc34: Option<Hertz>,
 
     #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 0c1cdd67c..2e81d2060 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -11,6 +11,8 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de
 embassy-executor = { version = "0.3.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
 embassy-time = { version = "0.1.3", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
 embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
+embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
+usbd-hid = "0.6.0"
 
 defmt = "0.3"
 defmt-rtt = "0.4"
diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs
new file mode 100644
index 000000000..a792748bc
--- /dev/null
+++ b/examples/stm32g4/src/bin/adc.rs
@@ -0,0 +1,41 @@
+#![no_std]
+#![no_main]
+#![feature(type_alias_impl_trait)]
+
+use defmt::*;
+use embassy_executor::Spawner;
+use embassy_stm32::adc::{Adc, SampleTime};
+use embassy_stm32::rcc::{AdcClockSource, ClockSrc, Pll, PllM, PllN, PllR, PllSrc};
+use embassy_stm32::Config;
+use embassy_time::{Delay, Duration, Timer};
+use {defmt_rtt as _, panic_probe as _};
+
+#[embassy_executor::main]
+async fn main(_spawner: Spawner) {
+    let mut config = Config::default();
+
+    config.rcc.pll = Some(Pll {
+        source: PllSrc::HSI16,
+        prediv_m: PllM::Div4,
+        mul_n: PllN::Mul85,
+        div_p: None,
+        div_q: None,
+        // Main system clock at 170 MHz
+        div_r: Some(PllR::Div2),
+    });
+
+    config.rcc.adc12_clock_source = AdcClockSource::SysClk;
+    config.rcc.mux = ClockSrc::PLL;
+
+    let mut p = embassy_stm32::init(config);
+    info!("Hello World!");
+
+    let mut adc = Adc::new(p.ADC2, &mut Delay);
+    adc.set_sample_time(SampleTime::Cycles32_5);
+
+    loop {
+        let measured = adc.read(&mut p.PA7);
+        info!("measured: {}", measured);
+        Timer::after(Duration::from_millis(500)).await;
+    }
+}