diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs
index 77d89293d..32732e96e 100644
--- a/embassy-stm32/src/rtc/datetime.rs
+++ b/embassy-stm32/src/rtc/datetime.rs
@@ -1,65 +1,6 @@
 #[cfg(feature = "chrono")]
 use chrono::{Datelike, NaiveDate, Timelike, Weekday};
 
-#[cfg(any(feature = "defmt", feature = "time"))]
-use crate::peripherals::RTC;
-#[cfg(any(feature = "defmt", feature = "time"))]
-use crate::rtc::SealedInstance;
-
-/// Represents an instant in time that can be substracted to compute a duration
-pub struct RtcInstant {
-    /// 0..59
-    pub second: u8,
-    /// 0..256
-    pub subsecond: u16,
-}
-
-impl RtcInstant {
-    #[cfg(not(rtc_v2f2))]
-    pub(super) const fn from(second: u8, subsecond: u16) -> Result<Self, Error> {
-        if second > 59 {
-            Err(Error::InvalidSecond)
-        } else {
-            Ok(Self { second, subsecond })
-        }
-    }
-}
-
-#[cfg(feature = "defmt")]
-impl defmt::Format for RtcInstant {
-    fn format(&self, fmt: defmt::Formatter) {
-        defmt::write!(
-            fmt,
-            "{}:{}",
-            self.second,
-            RTC::regs().prer().read().prediv_s() - self.subsecond,
-        )
-    }
-}
-
-#[cfg(feature = "time")]
-impl core::ops::Sub for RtcInstant {
-    type Output = embassy_time::Duration;
-
-    fn sub(self, rhs: Self) -> Self::Output {
-        use embassy_time::{Duration, TICK_HZ};
-
-        let second = if self.second < rhs.second {
-            self.second + 60
-        } else {
-            self.second
-        };
-
-        let psc = RTC::regs().prer().read().prediv_s() as u32;
-
-        let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
-        let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
-        let rtc_ticks = self_ticks - other_ticks;
-
-        Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
-    }
-}
-
 /// Errors regarding the [`DateTime`] struct.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Error {
diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs
new file mode 100644
index 000000000..a4ff1acbc
--- /dev/null
+++ b/embassy-stm32/src/rtc/low_power.rs
@@ -0,0 +1,230 @@
+use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError};
+use crate::peripherals::RTC;
+use crate::rtc::SealedInstance;
+
+/// Represents an instant in time that can be substracted to compute a duration
+pub(super) struct RtcInstant {
+    /// 0..59
+    second: u8,
+    /// 0..256
+    subsecond: u16,
+}
+
+impl RtcInstant {
+    #[cfg(not(rtc_v2f2))]
+    const fn from(second: u8, subsecond: u16) -> Result<Self, DateTimeError> {
+        if second > 59 {
+            Err(DateTimeError::InvalidSecond)
+        } else {
+            Ok(Self { second, subsecond })
+        }
+    }
+}
+
+#[cfg(feature = "defmt")]
+impl defmt::Format for RtcInstant {
+    fn format(&self, fmt: defmt::Formatter) {
+        defmt::write!(
+            fmt,
+            "{}:{}",
+            self.second,
+            RTC::regs().prer().read().prediv_s() - self.subsecond,
+        )
+    }
+}
+
+#[cfg(feature = "time")]
+impl core::ops::Sub for RtcInstant {
+    type Output = embassy_time::Duration;
+
+    fn sub(self, rhs: Self) -> Self::Output {
+        use embassy_time::{Duration, TICK_HZ};
+
+        let second = if self.second < rhs.second {
+            self.second + 60
+        } else {
+            self.second
+        };
+
+        let psc = RTC::regs().prer().read().prediv_s() as u32;
+
+        let self_ticks = second as u32 * (psc + 1) + (psc - self.subsecond as u32);
+        let other_ticks = rhs.second as u32 * (psc + 1) + (psc - rhs.subsecond as u32);
+        let rtc_ticks = self_ticks - other_ticks;
+
+        Duration::from_ticks(((rtc_ticks * TICK_HZ as u32) / (psc + 1)) as u64)
+    }
+}
+
+#[repr(u8)]
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum WakeupPrescaler {
+    Div2 = 2,
+    Div4 = 4,
+    Div8 = 8,
+    Div16 = 16,
+}
+
+#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))]
+impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
+    fn from(val: WakeupPrescaler) -> Self {
+        use crate::pac::rtc::vals::Wucksel;
+
+        match val {
+            WakeupPrescaler::Div2 => Wucksel::DIV2,
+            WakeupPrescaler::Div4 => Wucksel::DIV4,
+            WakeupPrescaler::Div8 => Wucksel::DIV8,
+            WakeupPrescaler::Div16 => Wucksel::DIV16,
+        }
+    }
+}
+
+#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))]
+impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
+    fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
+        use crate::pac::rtc::vals::Wucksel;
+
+        match val {
+            Wucksel::DIV2 => WakeupPrescaler::Div2,
+            Wucksel::DIV4 => WakeupPrescaler::Div4,
+            Wucksel::DIV8 => WakeupPrescaler::Div8,
+            Wucksel::DIV16 => WakeupPrescaler::Div16,
+            _ => unreachable!(),
+        }
+    }
+}
+
+impl WakeupPrescaler {
+    pub fn compute_min(val: u32) -> Self {
+        *[
+            WakeupPrescaler::Div2,
+            WakeupPrescaler::Div4,
+            WakeupPrescaler::Div8,
+            WakeupPrescaler::Div16,
+        ]
+        .iter()
+        .find(|psc| **psc as u32 > val)
+        .unwrap_or(&WakeupPrescaler::Div16)
+    }
+}
+
+impl Rtc {
+    /// Return the current instant.
+    fn instant(&self) -> Result<RtcInstant, RtcError> {
+        self.time_provider().read(|_, tr, ss| {
+            let second = bcd2_to_byte((tr.st(), tr.su()));
+
+            RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime)
+        })
+    }
+
+    /// start the wakeup alarm and with a duration that is as close to but less than
+    /// the requested duration, and record the instant the wakeup alarm was started
+    pub(crate) fn start_wakeup_alarm(
+        &self,
+        requested_duration: embassy_time::Duration,
+        cs: critical_section::CriticalSection,
+    ) {
+        use embassy_time::{Duration, TICK_HZ};
+
+        #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
+        use crate::pac::rtc::vals::Calrf;
+
+        // Panic if the rcc mod knows we're not using low-power rtc
+        #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
+        unsafe { crate::rcc::get_freqs() }.rtc.unwrap();
+
+        let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
+        let rtc_hz = Self::frequency().0 as u64;
+        let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
+        let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
+
+        // adjust the rtc ticks to the prescaler and subtract one rtc tick
+        let rtc_ticks = rtc_ticks / prescaler as u64;
+        let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16;
+
+        self.write(false, |regs| {
+            regs.cr().modify(|w| w.set_wute(false));
+
+            #[cfg(any(
+                rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
+            ))]
+            {
+                regs.isr().modify(|w| w.set_wutf(false));
+                while !regs.isr().read().wutwf() {}
+            }
+
+            #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
+            {
+                regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
+                while !regs.icsr().read().wutwf() {}
+            }
+
+            regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
+            regs.wutr().write(|w| w.set_wut(rtc_ticks));
+            regs.cr().modify(|w| w.set_wute(true));
+            regs.cr().modify(|w| w.set_wutie(true));
+        });
+
+        let instant = self.instant().unwrap();
+        trace!(
+            "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
+            Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
+            prescaler as u32,
+            rtc_ticks,
+            instant,
+        );
+
+        assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
+    }
+
+    /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
+    /// was called, otherwise none
+    pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
+        use crate::interrupt::typelevel::Interrupt;
+        #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
+        use crate::pac::rtc::vals::Calrf;
+
+        let instant = self.instant().unwrap();
+        if RTC::regs().cr().read().wute() {
+            trace!("rtc: stop wakeup alarm at {}", instant);
+
+            self.write(false, |regs| {
+                regs.cr().modify(|w| w.set_wutie(false));
+                regs.cr().modify(|w| w.set_wute(false));
+
+                #[cfg(any(
+                    rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
+                ))]
+                regs.isr().modify(|w| w.set_wutf(false));
+
+                #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
+                regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
+
+                // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar,
+                // there is a table for every "Event input" / "EXTI Line".
+                // If you find the EXTI line related to "RTC wakeup" marks as "Configurable" (not "Direct"),
+                // then write 1 to related field of Pending Register, to clean it's pending state.
+                #[cfg(any(exti_v1, stm32h7, stm32wb))]
+                crate::pac::EXTI
+                    .pr(0)
+                    .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
+
+                <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
+            });
+        }
+
+        self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
+    }
+
+    pub(crate) fn enable_wakeup_line(&self) {
+        use crate::interrupt::typelevel::Interrupt;
+        use crate::pac::EXTI;
+
+        <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
+        unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() };
+
+        EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
+        EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
+    }
+}
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 92a58ee9a..cb9c10676 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -1,6 +1,9 @@
 //! Real Time Clock (RTC)
 mod datetime;
 
+#[cfg(feature = "low-power")]
+mod low_power;
+
 #[cfg(feature = "low-power")]
 use core::cell::Cell;
 
@@ -9,8 +12,6 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 #[cfg(feature = "low-power")]
 use embassy_sync::blocking_mutex::Mutex;
 
-#[cfg(not(rtc_v2f2))]
-use self::datetime::RtcInstant;
 use self::datetime::{day_of_week_from_u8, day_of_week_to_u8};
 pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError};
 use crate::pac::rtc::regs::{Dr, Tr};
@@ -32,60 +33,6 @@ use embassy_hal_internal::Peripheral;
 
 use crate::peripherals::RTC;
 
-#[allow(dead_code)]
-#[repr(u8)]
-#[derive(Clone, Copy, Debug)]
-pub(crate) enum WakeupPrescaler {
-    Div2 = 2,
-    Div4 = 4,
-    Div8 = 8,
-    Div16 = 16,
-}
-
-#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))]
-impl From<WakeupPrescaler> for crate::pac::rtc::vals::Wucksel {
-    fn from(val: WakeupPrescaler) -> Self {
-        use crate::pac::rtc::vals::Wucksel;
-
-        match val {
-            WakeupPrescaler::Div2 => Wucksel::DIV2,
-            WakeupPrescaler::Div4 => Wucksel::DIV4,
-            WakeupPrescaler::Div8 => Wucksel::DIV8,
-            WakeupPrescaler::Div16 => Wucksel::DIV16,
-        }
-    }
-}
-
-#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l5, stm32wb, stm32h5, stm32g0))]
-impl From<crate::pac::rtc::vals::Wucksel> for WakeupPrescaler {
-    fn from(val: crate::pac::rtc::vals::Wucksel) -> Self {
-        use crate::pac::rtc::vals::Wucksel;
-
-        match val {
-            Wucksel::DIV2 => WakeupPrescaler::Div2,
-            Wucksel::DIV4 => WakeupPrescaler::Div4,
-            Wucksel::DIV8 => WakeupPrescaler::Div8,
-            Wucksel::DIV16 => WakeupPrescaler::Div16,
-            _ => unreachable!(),
-        }
-    }
-}
-
-#[cfg(feature = "low-power")]
-impl WakeupPrescaler {
-    pub fn compute_min(val: u32) -> Self {
-        *[
-            WakeupPrescaler::Div2,
-            WakeupPrescaler::Div4,
-            WakeupPrescaler::Div8,
-            WakeupPrescaler::Div16,
-        ]
-        .iter()
-        .find(|psc| **psc as u32 > val)
-        .unwrap_or(&WakeupPrescaler::Div16)
-    }
-}
-
 /// Errors that can occur on methods on [RtcClock]
 #[non_exhaustive]
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -106,15 +53,6 @@ pub struct RtcTimeProvider {
 }
 
 impl RtcTimeProvider {
-    #[cfg(not(rtc_v2f2))]
-    pub(crate) fn instant(&self) -> Result<RtcInstant, RtcError> {
-        self.read(|_, tr, ss| {
-            let second = bcd2_to_byte((tr.st(), tr.su()));
-
-            RtcInstant::from(second, ss).map_err(RtcError::InvalidDateTime)
-        })
-    }
-
     /// Return the current datetime.
     ///
     /// # Errors
@@ -165,8 +103,7 @@ impl RtcTimeProvider {
 /// RTC driver.
 pub struct Rtc {
     #[cfg(feature = "low-power")]
-    stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<RtcInstant>>>,
-    #[cfg(not(feature = "low-power"))]
+    stop_time: Mutex<CriticalSectionRawMutex, Cell<Option<low_power::RtcInstant>>>,
     _private: (),
 }
 
@@ -210,7 +147,6 @@ impl Rtc {
         let mut this = Self {
             #[cfg(feature = "low-power")]
             stop_time: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
-            #[cfg(not(feature = "low-power"))]
             _private: (),
         };
 
@@ -223,9 +159,8 @@ impl Rtc {
         // Wait for the clock to update after initialization
         #[cfg(not(rtc_v2f2))]
         {
-            let now = this.instant().unwrap();
-
-            while this.instant().unwrap().subsecond == now.subsecond {}
+            let now = this.time_provider().read(|_, _, ss| Ok(ss)).unwrap();
+            while now == this.time_provider().read(|_, _, ss| Ok(ss)).unwrap() {}
         }
 
         this
@@ -284,12 +219,6 @@ impl Rtc {
         Ok(())
     }
 
-    #[cfg(not(rtc_v2f2))]
-    /// Return the current instant.
-    fn instant(&self) -> Result<RtcInstant, RtcError> {
-        self.time_provider().instant()
-    }
-
     /// Return the current datetime.
     ///
     /// # Errors
@@ -330,119 +259,6 @@ impl Rtc {
     pub fn write_backup_register(&self, register: usize, value: u32) {
         RTC::write_backup_register(RTC::regs(), register, value)
     }
-
-    #[cfg(feature = "low-power")]
-    /// start the wakeup alarm and with a duration that is as close to but less than
-    /// the requested duration, and record the instant the wakeup alarm was started
-    pub(crate) fn start_wakeup_alarm(
-        &self,
-        requested_duration: embassy_time::Duration,
-        cs: critical_section::CriticalSection,
-    ) {
-        use embassy_time::{Duration, TICK_HZ};
-
-        #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
-        use crate::pac::rtc::vals::Calrf;
-
-        // Panic if the rcc mod knows we're not using low-power rtc
-        #[cfg(any(rcc_wb, rcc_f4, rcc_f410))]
-        unsafe { crate::rcc::get_freqs() }.rtc.unwrap();
-
-        let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64);
-        let rtc_hz = Self::frequency().0 as u64;
-        let rtc_ticks = requested_duration * rtc_hz / TICK_HZ;
-        let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32);
-
-        // adjust the rtc ticks to the prescaler and subtract one rtc tick
-        let rtc_ticks = rtc_ticks / prescaler as u64;
-        let rtc_ticks = rtc_ticks.clamp(0, (u16::MAX - 1) as u64).saturating_sub(1) as u16;
-
-        self.write(false, |regs| {
-            regs.cr().modify(|w| w.set_wute(false));
-
-            #[cfg(any(
-                rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
-            ))]
-            {
-                regs.isr().modify(|w| w.set_wutf(false));
-                while !regs.isr().read().wutwf() {}
-            }
-
-            #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
-            {
-                regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
-                while !regs.icsr().read().wutwf() {}
-            }
-
-            regs.cr().modify(|w| w.set_wucksel(prescaler.into()));
-            regs.wutr().write(|w| w.set_wut(rtc_ticks));
-            regs.cr().modify(|w| w.set_wute(true));
-            regs.cr().modify(|w| w.set_wutie(true));
-        });
-
-        let instant = self.instant().unwrap();
-        trace!(
-            "rtc: start wakeup alarm for {} ms (psc: {}, ticks: {}) at {}",
-            Duration::from_ticks(rtc_ticks as u64 * TICK_HZ * prescaler as u64 / rtc_hz).as_millis(),
-            prescaler as u32,
-            rtc_ticks,
-            instant,
-        );
-
-        assert!(self.stop_time.borrow(cs).replace(Some(instant)).is_none())
-    }
-
-    #[cfg(feature = "low-power")]
-    /// stop the wakeup alarm and return the time elapsed since `start_wakeup_alarm`
-    /// was called, otherwise none
-    pub(crate) fn stop_wakeup_alarm(&self, cs: critical_section::CriticalSection) -> Option<embassy_time::Duration> {
-        use crate::interrupt::typelevel::Interrupt;
-        #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
-        use crate::pac::rtc::vals::Calrf;
-
-        let instant = self.instant().unwrap();
-        if RTC::regs().cr().read().wute() {
-            trace!("rtc: stop wakeup alarm at {}", instant);
-
-            self.write(false, |regs| {
-                regs.cr().modify(|w| w.set_wutie(false));
-                regs.cr().modify(|w| w.set_wute(false));
-
-                #[cfg(any(
-                    rtc_v2f0, rtc_v2f2, rtc_v2f3, rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l0, rtc_v2l1, rtc_v2l4, rtc_v2wb
-                ))]
-                regs.isr().modify(|w| w.set_wutf(false));
-
-                #[cfg(any(rtc_v3, rtc_v3u5, rtc_v3l5))]
-                regs.scr().write(|w| w.set_cwutf(Calrf::CLEAR));
-
-                // Check RM for EXTI and/or NVIC section, "Event event input mapping" or "EXTI interrupt/event mapping" or something similar,
-                // there is a table for every "Event input" / "EXTI Line".
-                // If you find the EXTI line related to "RTC wakeup" marks as "Configurable" (not "Direct"),
-                // then write 1 to related field of Pending Register, to clean it's pending state.
-                #[cfg(any(exti_v1, stm32h7, stm32wb))]
-                crate::pac::EXTI
-                    .pr(0)
-                    .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
-
-                <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
-            });
-        }
-
-        self.stop_time.borrow(cs).take().map(|stop_time| instant - stop_time)
-    }
-
-    #[cfg(feature = "low-power")]
-    pub(crate) fn enable_wakeup_line(&self) {
-        use crate::interrupt::typelevel::Interrupt;
-        use crate::pac::EXTI;
-
-        <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::unpend();
-        unsafe { <RTC as crate::rtc::SealedInstance>::WakeupInterrupt::enable() };
-
-        EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
-        EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true));
-    }
 }
 
 pub(crate) fn byte_to_bcd2(byte: u8) -> (u8, u8) {