diff --git a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs
index 743d0c342..aecba0755 100644
--- a/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs
+++ b/docs/modules/ROOT/examples/layer-by-layer/blinky-irq/src/main.rs
@@ -20,13 +20,13 @@ fn main() -> ! {
     let led = Output::new(p.PB14, Level::Low, Speed::Low);
     let mut button = Input::new(p.PC13, Pull::Up);
 
-    cortex_m::interrupt::free(|cs| unsafe {
+    cortex_m::interrupt::free(|cs| {
         enable_interrupt(&mut button);
 
         LED.borrow(cs).borrow_mut().replace(led);
         BUTTON.borrow(cs).borrow_mut().replace(button);
 
-        NVIC::unmask(pac::Interrupt::EXTI15_10);
+        unsafe { NVIC::unmask(pac::Interrupt::EXTI15_10) };
     });
 
     loop {
@@ -64,25 +64,21 @@ const PORT: u8 = 2;
 const PIN: usize = 13;
 fn check_interrupt<P: Pin>(_pin: &mut Input<'static, P>) -> bool {
     let exti = pac::EXTI;
-    unsafe {
-        let pin = PIN;
-        let lines = exti.pr(0).read();
-        lines.line(pin)
-    }
+    let pin = PIN;
+    let lines = exti.pr(0).read();
+    lines.line(pin)
 }
 
 fn clear_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
     let exti = pac::EXTI;
-    unsafe {
-        let pin = PIN;
-        let mut lines = exti.pr(0).read();
-        lines.set_line(pin, true);
-        exti.pr(0).write_value(lines);
-    }
+    let pin = PIN;
+    let mut lines = exti.pr(0).read();
+    lines.set_line(pin, true);
+    exti.pr(0).write_value(lines);
 }
 
 fn enable_interrupt<P: Pin>(_pin: &mut Input<'static, P>) {
-    cortex_m::interrupt::free(|_| unsafe {
+    cortex_m::interrupt::free(|_| {
         let rcc = pac::RCC;
         rcc.apb2enr().modify(|w| w.set_syscfgen(true));
 
diff --git a/embassy-futures/src/block_on.rs b/embassy-futures/src/block_on.rs
index da90351ec..77695216c 100644
--- a/embassy-futures/src/block_on.rs
+++ b/embassy-futures/src/block_on.rs
@@ -31,3 +31,15 @@ pub fn block_on<F: Future>(mut fut: F) -> F::Output {
         }
     }
 }
+
+/// Poll a future once.
+pub fn poll_once<F: Future>(mut fut: F) -> Poll<F::Output> {
+    // safety: we don't move the future after this line.
+    let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
+
+    let raw_waker = RawWaker::new(ptr::null(), &VTABLE);
+    let waker = unsafe { Waker::from_raw(raw_waker) };
+    let mut cx = Context::from_waker(&waker);
+
+    fut.as_mut().poll(&mut cx)
+}
diff --git a/embassy-lora/src/iv.rs b/embassy-lora/src/iv.rs
index 2e0b68d1a..136973fe3 100644
--- a/embassy-lora/src/iv.rs
+++ b/embassy-lora/src/iv.rs
@@ -68,29 +68,23 @@ where
     }
     async fn set_nss_low(&mut self) -> Result<(), RadioError> {
         let pwr = pac::PWR;
-        unsafe {
-            pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
-        }
+        pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
         Ok(())
     }
     async fn set_nss_high(&mut self) -> Result<(), RadioError> {
         let pwr = pac::PWR;
-        unsafe {
-            pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
-        }
+        pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
         Ok(())
     }
     async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
         let rcc = pac::RCC;
-        unsafe {
-            rcc.csr().modify(|w| w.set_rfrst(true));
-            rcc.csr().modify(|w| w.set_rfrst(false));
-        }
+        rcc.csr().modify(|w| w.set_rfrst(true));
+        rcc.csr().modify(|w| w.set_rfrst(false));
         Ok(())
     }
     async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
         let pwr = pac::PWR;
-        while unsafe { pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY } {}
+        while pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY {}
         Ok(())
     }
 
diff --git a/embassy-stm32-wpan/src/ble.rs b/embassy-stm32-wpan/src/ble.rs
index 57348a925..f0bd6f48c 100644
--- a/embassy-stm32-wpan/src/ble.rs
+++ b/embassy-stm32-wpan/src/ble.rs
@@ -1,18 +1,20 @@
+use core::marker::PhantomData;
+
 use embassy_stm32::ipcc::Ipcc;
 
-use crate::cmd::{CmdPacket, CmdSerial};
+use crate::cmd::CmdPacket;
 use crate::consts::TlPacketType;
 use crate::evt::EvtBox;
 use crate::tables::BleTable;
 use crate::unsafe_linked_list::LinkedListNode;
-use crate::{
-    channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_CHANNEL, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE, TL_REF_TABLE,
-};
+use crate::{channels, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE};
 
-pub struct Ble;
+pub struct Ble {
+    phantom: PhantomData<Ble>,
+}
 
 impl Ble {
-    pub(super) fn enable() {
+    pub(crate) fn new() -> Self {
         unsafe {
             LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr());
 
@@ -24,54 +26,38 @@ impl Ble {
             });
         }
 
-        Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, true);
+        Self { phantom: PhantomData }
     }
-
-    pub(super) fn evt_handler() {
-        unsafe {
-            while !LinkedListNode::is_empty(EVT_QUEUE.as_mut_ptr()) {
-                let node_ptr = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr());
-
-                let event = node_ptr.cast();
-                let event = EvtBox::new(event);
-
-                EVT_CHANNEL.try_send(event).unwrap();
+    /// `HW_IPCC_BLE_EvtNot`
+    pub async fn read(&self) -> EvtBox {
+        Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe {
+            if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) {
+                Some(EvtBox::new(node_ptr.cast()))
+            } else {
+                None
             }
-        }
-
-        Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_BLE_EVENT_CHANNEL);
+        })
+        .await
     }
 
-    pub(super) fn acl_data_handler() {
-        Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, false);
-
-        // TODO: ACL data ack to the user
+    /// `TL_BLE_SendCmd`
+    pub async fn write(&self, opcode: u16, payload: &[u8]) {
+        Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe {
+            CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload);
+        })
+        .await;
     }
 
-    pub fn ble_send_cmd(buf: &[u8]) {
-        debug!("writing ble cmd");
-
-        unsafe {
-            let pcmd_buffer: *mut CmdPacket = (*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer;
-            let pcmd_serial: *mut CmdSerial = &mut (*pcmd_buffer).cmdserial;
-            let pcmd_serial_buf: *mut u8 = pcmd_serial.cast();
-
-            core::ptr::copy(buf.as_ptr(), pcmd_serial_buf, buf.len());
-
-            let cmd_packet = &mut *(*TL_REF_TABLE.assume_init().ble_table).pcmd_buffer;
-            cmd_packet.cmdserial.ty = TlPacketType::BleCmd as u8;
-        }
-
-        Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_BLE_CMD_CHANNEL);
-    }
-
-    #[allow(dead_code)] // Not used currently but reserved
-    pub(super) fn ble_send_acl_data() {
-        let cmd_packet = unsafe { &mut *(*TL_REF_TABLE.assume_init().ble_table).phci_acl_data_buffer };
-
-        cmd_packet.acl_data_serial.ty = TlPacketType::AclData as u8;
-
-        Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL);
-        Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, true);
+    /// `TL_BLE_SendAclData`
+    pub async fn acl_write(&self, handle: u16, payload: &[u8]) {
+        Ipcc::send(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, || unsafe {
+            CmdPacket::write_into(
+                HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _,
+                TlPacketType::AclData,
+                handle,
+                payload,
+            );
+        })
+        .await;
     }
 }
diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs
index 1f7dae7f7..edca82390 100644
--- a/embassy-stm32-wpan/src/cmd.rs
+++ b/embassy-stm32-wpan/src/cmd.rs
@@ -1,5 +1,7 @@
-use crate::evt::{EvtPacket, EvtSerial};
-use crate::{PacketHeader, TL_EVT_HEADER_SIZE};
+use core::ptr;
+
+use crate::consts::TlPacketType;
+use crate::PacketHeader;
 
 #[derive(Copy, Clone)]
 #[repr(C, packed)]
@@ -26,6 +28,14 @@ pub struct CmdSerial {
     pub cmd: Cmd,
 }
 
+#[derive(Copy, Clone, Default)]
+#[repr(C, packed)]
+pub struct CmdSerialStub {
+    pub ty: u8,
+    pub cmd_code: u16,
+    pub payload_len: u8,
+}
+
 #[derive(Copy, Clone, Default)]
 #[repr(C, packed)]
 pub struct CmdPacket {
@@ -34,29 +44,20 @@ pub struct CmdPacket {
 }
 
 impl CmdPacket {
-    /// Writes an underlying CmdPacket into the provided buffer.
-    /// Returns a number of bytes that were written.
-    /// Returns an error if event kind is unknown or if provided buffer size is not enough.
-    #[allow(clippy::result_unit_err)]
-    pub fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
-        unsafe {
-            let cmd_ptr: *const CmdPacket = self;
-            let self_as_evt_ptr: *const EvtPacket = cmd_ptr.cast();
-            let evt_serial: *const EvtSerial = &(*self_as_evt_ptr).evt_serial;
+    pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) {
+        let p_cmd_serial = &mut (*cmd_buf).cmdserial as *mut _ as *mut CmdSerialStub;
+        let p_payload = &mut (*cmd_buf).cmdserial.cmd.payload as *mut _;
 
-            let acl_data: *const AclDataPacket = cmd_ptr.cast();
-            let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
-            let acl_serial_buf: *const u8 = acl_serial.cast();
+        ptr::write_volatile(
+            p_cmd_serial,
+            CmdSerialStub {
+                ty: packet_type as u8,
+                cmd_code: cmd_code,
+                payload_len: payload.len() as u8,
+            },
+        );
 
-            let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;
-            if len > buf.len() {
-                return Err(());
-            }
-
-            core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len);
-
-            Ok(len)
-        }
+        ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
     }
 }
 
@@ -69,9 +70,35 @@ pub struct AclDataSerial {
     pub acl_data: [u8; 1],
 }
 
+#[derive(Copy, Clone)]
+#[repr(C, packed)]
+pub struct AclDataSerialStub {
+    pub ty: u8,
+    pub handle: u16,
+    pub length: u16,
+}
+
 #[derive(Copy, Clone)]
 #[repr(C, packed)]
 pub struct AclDataPacket {
     pub header: PacketHeader,
     pub acl_data_serial: AclDataSerial,
 }
+
+impl AclDataPacket {
+    pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) {
+        let p_cmd_serial = &mut (*cmd_buf).acl_data_serial as *mut _ as *mut AclDataSerialStub;
+        let p_payload = &mut (*cmd_buf).acl_data_serial.acl_data as *mut _;
+
+        ptr::write_volatile(
+            p_cmd_serial,
+            AclDataSerialStub {
+                ty: packet_type as u8,
+                handle: handle,
+                length: payload.len() as u16,
+            },
+        );
+
+        ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len());
+    }
+}
diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs
index b53fe506e..3a9d03576 100644
--- a/embassy-stm32-wpan/src/evt.rs
+++ b/embassy-stm32-wpan/src/evt.rs
@@ -1,8 +1,6 @@
-use core::mem::MaybeUninit;
+use core::{ptr, slice};
 
-use super::cmd::{AclDataPacket, AclDataSerial};
-use super::consts::TlPacketType;
-use super::{PacketHeader, TL_EVT_HEADER_SIZE};
+use super::PacketHeader;
 use crate::mm;
 
 /**
@@ -63,6 +61,12 @@ pub struct EvtSerial {
     pub evt: Evt,
 }
 
+#[derive(Copy, Clone, Default)]
+pub struct EvtStub {
+    pub kind: u8,
+    pub evt_code: u8,
+}
+
 /// This format shall be used for all events (asynchronous and command response) reported
 /// by the CPU2 except for the command response of a system command where the header is not there
 /// and the format to be used shall be `EvtSerial`.
@@ -101,76 +105,91 @@ impl EvtBox {
         Self { ptr }
     }
 
-    /// copies event data from inner pointer and returns an event structure
-    pub fn evt(&self) -> EvtPacket {
-        let mut evt = MaybeUninit::uninit();
+    /// Returns information about the event
+    pub fn stub(&self) -> EvtStub {
         unsafe {
-            self.ptr.copy_to(evt.as_mut_ptr(), 1);
-            evt.assume_init()
+            let p_evt_stub = &(*self.ptr).evt_serial as *const _ as *const EvtStub;
+
+            ptr::read_volatile(p_evt_stub)
         }
     }
 
-    /// writes an underlying [`EvtPacket`] into the provided buffer.
-    /// Returns the number of bytes that were written.
-    /// Returns an error if event kind is unknown or if provided buffer size is not enough.
-    #[allow(clippy::result_unit_err)]
-    pub fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
+    pub fn payload<'a>(&self) -> &'a [u8] {
         unsafe {
-            let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?;
+            let p_payload_len = &(*self.ptr).evt_serial.evt.payload_len as *const u8;
+            let p_payload = &(*self.ptr).evt_serial.evt.payload as *const u8;
 
-            let evt_data: *const EvtPacket = self.ptr.cast();
-            let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
-            let evt_serial_buf: *const u8 = evt_serial.cast();
+            let payload_len = ptr::read_volatile(p_payload_len);
 
-            let acl_data: *const AclDataPacket = self.ptr.cast();
-            let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
-            let acl_serial_buf: *const u8 = acl_serial.cast();
-
-            if let TlPacketType::AclData = evt_kind {
-                let len = (*acl_serial).length as usize + 5;
-                if len > buf.len() {
-                    return Err(());
-                }
-
-                core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len);
-
-                Ok(len)
-            } else {
-                let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;
-                if len > buf.len() {
-                    return Err(());
-                }
-
-                core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len);
-
-                Ok(len)
-            }
+            slice::from_raw_parts(p_payload, payload_len as usize)
         }
     }
 
-    /// returns the size of a buffer required to hold this event
-    #[allow(clippy::result_unit_err)]
-    pub fn size(&self) -> Result<usize, ()> {
-        unsafe {
-            let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?;
+    // TODO: bring back acl
 
-            let evt_data: *const EvtPacket = self.ptr.cast();
-            let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
-
-            let acl_data: *const AclDataPacket = self.ptr.cast();
-            let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
-
-            if let TlPacketType::AclData = evt_kind {
-                Ok((*acl_serial).length as usize + 5)
-            } else {
-                Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE)
-            }
-        }
-    }
+    //     /// writes an underlying [`EvtPacket`] into the provided buffer.
+    //     /// Returns the number of bytes that were written.
+    //     /// Returns an error if event kind is unknown or if provided buffer size is not enough.
+    //     #[allow(clippy::result_unit_err)]
+    //     pub fn write(&self, buf: &mut [u8]) -> Result<usize, ()> {
+    //         unsafe {
+    //             let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?;
+    //
+    //             let evt_data: *const EvtPacket = self.ptr.cast();
+    //             let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
+    //             let evt_serial_buf: *const u8 = evt_serial.cast();
+    //
+    //             let acl_data: *const AclDataPacket = self.ptr.cast();
+    //             let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
+    //             let acl_serial_buf: *const u8 = acl_serial.cast();
+    //
+    //             if let TlPacketType::AclData = evt_kind {
+    //                 let len = (*acl_serial).length as usize + 5;
+    //                 if len > buf.len() {
+    //                     return Err(());
+    //                 }
+    //
+    //                 core::ptr::copy(evt_serial_buf, buf.as_mut_ptr(), len);
+    //
+    //                 Ok(len)
+    //             } else {
+    //                 let len = (*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE;
+    //                 if len > buf.len() {
+    //                     return Err(());
+    //                 }
+    //
+    //                 core::ptr::copy(acl_serial_buf, buf.as_mut_ptr(), len);
+    //
+    //                 Ok(len)
+    //             }
+    //         }
+    //     }
+    //
+    //     /// returns the size of a buffer required to hold this event
+    //     #[allow(clippy::result_unit_err)]
+    //     pub fn size(&self) -> Result<usize, ()> {
+    //         unsafe {
+    //             let evt_kind = TlPacketType::try_from((*self.ptr).evt_serial.kind)?;
+    //
+    //             let evt_data: *const EvtPacket = self.ptr.cast();
+    //             let evt_serial: *const EvtSerial = &(*evt_data).evt_serial;
+    //
+    //             let acl_data: *const AclDataPacket = self.ptr.cast();
+    //             let acl_serial: *const AclDataSerial = &(*acl_data).acl_data_serial;
+    //
+    //             if let TlPacketType::AclData = evt_kind {
+    //                 Ok((*acl_serial).length as usize + 5)
+    //             } else {
+    //                 Ok((*evt_serial).evt.payload_len as usize + TL_EVT_HEADER_SIZE)
+    //             }
+    //         }
+    //     }
 }
 
 impl Drop for EvtBox {
     fn drop(&mut self) {
-        mm::MemoryManager::evt_drop(self.ptr);
+        trace!("evt box drop packet");
+
+        unsafe { mm::MemoryManager::drop_event_packet(self.ptr) };
     }
 }
diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs
index 2852d6270..833db0df3 100644
--- a/embassy-stm32-wpan/src/lib.rs
+++ b/embassy-stm32-wpan/src/lib.rs
@@ -6,20 +6,21 @@ pub mod fmt;
 use core::mem::MaybeUninit;
 use core::sync::atomic::{compiler_fence, Ordering};
 
+use ble::Ble;
 use cmd::CmdPacket;
-use embassy_futures::block_on;
 use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
 use embassy_stm32::interrupt;
 use embassy_stm32::interrupt::typelevel::Interrupt;
-use embassy_stm32::ipcc::{Config, Ipcc};
+use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler};
 use embassy_stm32::peripherals::IPCC;
 use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
 use embassy_sync::channel::Channel;
 use embassy_sync::signal::Signal;
 use evt::{CcEvt, EvtBox};
+use mm::MemoryManager;
+use sys::Sys;
 use tables::{
     BleTable, DeviceInfoTable, Mac802_15_4Table, MemManagerTable, RefTable, SysTable, ThreadTable, TracesTable,
-    WirelessFwInfoTable,
 };
 use unsafe_linked_list::LinkedListNode;
 
@@ -29,50 +30,11 @@ pub mod cmd;
 pub mod consts;
 pub mod evt;
 pub mod mm;
-pub mod rc;
 pub mod shci;
 pub mod sys;
 pub mod tables;
 pub mod unsafe_linked_list;
 
-/// Interrupt handler.
-pub struct ReceiveInterruptHandler {}
-
-impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for ReceiveInterruptHandler {
-    unsafe fn on_interrupt() {
-        if Ipcc::is_rx_pending(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL) {
-            debug!("RX SYS evt");
-            sys::Sys::evt_handler();
-        } else if Ipcc::is_rx_pending(channels::cpu2::IPCC_BLE_EVENT_CHANNEL) {
-            debug!("RX BLE evt");
-            ble::Ble::evt_handler();
-        }
-
-        STATE.signal(());
-    }
-}
-
-pub struct TransmitInterruptHandler {}
-
-impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {
-    unsafe fn on_interrupt() {
-        if Ipcc::is_tx_pending(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL) {
-            debug!("TX SYS cmd rsp");
-            let cc = sys::Sys::cmd_evt_handler();
-
-            LAST_CC_EVT.signal(cc);
-        } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL) {
-            debug!("TX MM release");
-            mm::MemoryManager::free_buf_handler();
-        } else if Ipcc::is_tx_pending(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL) {
-            debug!("TX HCI acl");
-            ble::Ble::acl_data_handler();
-        }
-
-        STATE.signal(());
-    }
-}
-
 #[link_section = "TL_REF_TABLE"]
 pub static mut TL_REF_TABLE: MaybeUninit<RefTable> = MaybeUninit::uninit();
 
@@ -167,10 +129,14 @@ static mut BLE_CMD_BUFFER: MaybeUninit<CmdPacket> = MaybeUninit::uninit();
 //                                 fuck these "magic" numbers from ST ---v---v
 static mut HCI_ACL_DATA_BUFFER: MaybeUninit<[u8; TL_PACKET_HEADER_SIZE + 5 + 251]> = MaybeUninit::uninit();
 
+// TODO: remove these items
+
+#[allow(dead_code)]
 /// current event that is produced during IPCC IRQ handler execution
 /// on SYS channel
 static EVT_CHANNEL: Channel<CriticalSectionRawMutex, EvtBox, 32> = Channel::new();
 
+#[allow(dead_code)]
 /// last received Command Complete event
 static LAST_CC_EVT: Signal<CriticalSectionRawMutex, CcEvt> = Signal::new();
 
@@ -178,6 +144,10 @@ static STATE: Signal<CriticalSectionRawMutex, ()> = Signal::new();
 
 pub struct TlMbox<'d> {
     _ipcc: PeripheralRef<'d, IPCC>,
+
+    pub sys_subsystem: Sys,
+    pub mm_subsystem: MemoryManager,
+    pub ble_subsystem: Ble,
 }
 
 impl<'d> TlMbox<'d> {
@@ -262,9 +232,9 @@ impl<'d> TlMbox<'d> {
 
         Ipcc::enable(config);
 
-        sys::Sys::enable();
-        ble::Ble::enable();
-        mm::MemoryManager::enable();
+        let sys = sys::Sys::new();
+        let ble = ble::Ble::new();
+        let mm = mm::MemoryManager::new();
 
         // enable interrupts
         interrupt::typelevel::IPCC_C1_RX::unpend();
@@ -275,36 +245,11 @@ impl<'d> TlMbox<'d> {
 
         STATE.reset();
 
-        Self { _ipcc: ipcc }
-    }
-
-    /// Returns CPU2 wireless firmware information (if present).
-    pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {
-        let info = unsafe { &(*(*TL_REF_TABLE.as_ptr()).device_info_table).wireless_fw_info_table };
-
-        // Zero version indicates that CPU2 wasn't active and didn't fill the information table
-        if info.version != 0 {
-            Some(*info)
-        } else {
-            None
-        }
-    }
-
-    /// picks single [`EvtBox`] from internal event queue.
-    ///
-    /// Internal event queu is populated in IPCC_RX_IRQ handler
-    pub fn dequeue_event(&mut self) -> Option<EvtBox> {
-        EVT_CHANNEL.try_recv().ok()
-    }
-
-    /// retrieves last Command Complete event and removes it from mailbox
-    pub fn pop_last_cc_evt(&mut self) -> Option<CcEvt> {
-        if LAST_CC_EVT.signaled() {
-            let cc = block_on(LAST_CC_EVT.wait());
-            LAST_CC_EVT.reset();
-            Some(cc)
-        } else {
-            None
+        Self {
+            _ipcc: ipcc,
+            sys_subsystem: sys,
+            ble_subsystem: ble,
+            mm_subsystem: mm,
         }
     }
 }
diff --git a/embassy-stm32-wpan/src/mm.rs b/embassy-stm32-wpan/src/mm.rs
index 06063b89a..21f42409a 100644
--- a/embassy-stm32-wpan/src/mm.rs
+++ b/embassy-stm32-wpan/src/mm.rs
@@ -1,19 +1,29 @@
 //! Memory manager routines
 
+use core::future::poll_fn;
+use core::marker::PhantomData;
+use core::task::Poll;
+
+use cortex_m::interrupt;
 use embassy_stm32::ipcc::Ipcc;
+use embassy_sync::waitqueue::AtomicWaker;
 
 use crate::evt::EvtPacket;
 use crate::tables::MemManagerTable;
 use crate::unsafe_linked_list::LinkedListNode;
 use crate::{
     channels, BLE_SPARE_EVT_BUF, EVT_POOL, FREE_BUF_QUEUE, LOCAL_FREE_BUF_QUEUE, POOL_SIZE, SYS_SPARE_EVT_BUF,
-    TL_MEM_MANAGER_TABLE, TL_REF_TABLE,
+    TL_MEM_MANAGER_TABLE,
 };
 
-pub(super) struct MemoryManager;
+static MM_WAKER: AtomicWaker = AtomicWaker::new();
+
+pub struct MemoryManager {
+    phantom: PhantomData<MemoryManager>,
+}
 
 impl MemoryManager {
-    pub fn enable() {
+    pub(crate) fn new() -> Self {
         unsafe {
             LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr());
             LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
@@ -28,44 +38,40 @@ impl MemoryManager {
                 tracespoolsize: 0,
             });
         }
+
+        Self { phantom: PhantomData }
     }
 
-    pub fn evt_drop(evt: *mut EvtPacket) {
-        unsafe {
-            let list_node = evt.cast();
+    /// SAFETY: passing a pointer to something other than an event packet is UB
+    pub(crate) unsafe fn drop_event_packet(evt: *mut EvtPacket) {
+        interrupt::free(|_| unsafe {
+            LinkedListNode::insert_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), evt as *mut _);
+        });
 
-            LinkedListNode::insert_tail(LOCAL_FREE_BUF_QUEUE.as_mut_ptr(), list_node);
+        MM_WAKER.wake();
+    }
 
-            let channel_is_busy = Ipcc::c1_is_active_flag(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
+    pub async fn run_queue(&self) {
+        loop {
+            poll_fn(|cx| unsafe {
+                MM_WAKER.register(cx.waker());
+                if LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
+                    Poll::Pending
+                } else {
+                    Poll::Ready(())
+                }
+            })
+            .await;
 
-            // postpone event buffer freeing to IPCC interrupt handler
-            if channel_is_busy {
-                Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, true);
-            } else {
-                Self::send_free_buf();
-                Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
-            }
+            Ipcc::send(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, || {
+                interrupt::free(|_| unsafe {
+                    // CS required while moving nodes
+                    while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
+                        LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr);
+                    }
+                })
+            })
+            .await;
         }
     }
-
-    /// gives free event buffers back to CPU2 from local buffer queue
-    pub fn send_free_buf() {
-        unsafe {
-            while !LinkedListNode::is_empty(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) {
-                let node_ptr = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr());
-
-                LinkedListNode::insert_tail(
-                    (*(*TL_REF_TABLE.as_ptr()).mem_manager_table).pevt_free_buffer_queue,
-                    node_ptr,
-                );
-            }
-        }
-    }
-
-    /// free buffer channel interrupt handler
-    pub fn free_buf_handler() {
-        Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, false);
-        Self::send_free_buf();
-        Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL);
-    }
 }
diff --git a/embassy-stm32-wpan/src/rc.rs b/embassy-stm32-wpan/src/rc.rs
deleted file mode 100644
index aae2265ed..000000000
--- a/embassy-stm32-wpan/src/rc.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use crate::ble::Ble;
-use crate::consts::TlPacketType;
-use crate::{shci, TlMbox, STATE};
-
-pub struct RadioCoprocessor<'d> {
-    mbox: TlMbox<'d>,
-    rx_buf: [u8; 500],
-}
-
-impl<'d> RadioCoprocessor<'d> {
-    pub fn new(mbox: TlMbox<'d>) -> Self {
-        Self {
-            mbox,
-            rx_buf: [0u8; 500],
-        }
-    }
-
-    pub fn write(&self, buf: &[u8]) {
-        let cmd_code = buf[0];
-        let cmd = TlPacketType::try_from(cmd_code).unwrap();
-
-        match &cmd {
-            TlPacketType::BleCmd => Ble::ble_send_cmd(buf),
-            _ => todo!(),
-        }
-    }
-
-    pub async fn read(&mut self) -> &[u8] {
-        loop {
-            STATE.wait().await;
-
-            while let Some(evt) = self.mbox.dequeue_event() {
-                let event = evt.evt();
-
-                evt.write(&mut self.rx_buf).unwrap();
-
-                if event.kind() == 18 {
-                    shci::shci_ble_init(Default::default());
-                    self.rx_buf[0] = 0x04;
-                }
-            }
-
-            if self.mbox.pop_last_cc_evt().is_some() {
-                continue;
-            }
-
-            return &self.rx_buf;
-        }
-    }
-}
diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs
index 8537995ff..cdf027d5e 100644
--- a/embassy-stm32-wpan/src/shci.rs
+++ b/embassy-stm32-wpan/src/shci.rs
@@ -1,8 +1,8 @@
-use super::cmd::CmdPacket;
-use super::consts::TlPacketType;
-use super::{sys, TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE, TL_SYS_TABLE};
+use core::{mem, slice};
 
-const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66;
+use super::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE};
+
+pub const SCHI_OPCODE_BLE_INIT: u16 = 0xfc66;
 
 #[derive(Debug, Clone, Copy)]
 #[repr(C, packed)]
@@ -32,6 +32,12 @@ pub struct ShciBleInitCmdParam {
     pub hw_version: u8,
 }
 
+impl ShciBleInitCmdParam {
+    pub fn payload<'a>(&self) -> &'a [u8] {
+        unsafe { slice::from_raw_parts(self as *const _ as *const u8, mem::size_of::<Self>()) }
+    }
+}
+
 impl Default for ShciBleInitCmdParam {
     fn default() -> Self {
         Self {
@@ -66,35 +72,10 @@ pub struct ShciHeader {
 #[derive(Debug, Clone, Copy)]
 #[repr(C, packed)]
 pub struct ShciBleInitCmdPacket {
-    header: ShciHeader,
-    param: ShciBleInitCmdParam,
+    pub header: ShciHeader,
+    pub param: ShciBleInitCmdParam,
 }
 
 pub const TL_BLE_EVT_CS_PACKET_SIZE: usize = TL_EVT_HEADER_SIZE + TL_CS_EVT_SIZE;
 #[allow(dead_code)] // Not used currently but reserved
 const TL_BLE_EVT_CS_BUFFER_SIZE: usize = TL_PACKET_HEADER_SIZE + TL_BLE_EVT_CS_PACKET_SIZE;
-
-pub fn shci_ble_init(param: ShciBleInitCmdParam) {
-    debug!("sending SHCI");
-
-    let mut packet = ShciBleInitCmdPacket {
-        header: ShciHeader::default(),
-        param,
-    };
-
-    let packet_ptr: *mut _ = &mut packet;
-
-    unsafe {
-        let cmd_ptr: *mut CmdPacket = packet_ptr.cast();
-
-        (*cmd_ptr).cmdserial.cmd.cmd_code = SCHI_OPCODE_BLE_INIT;
-        (*cmd_ptr).cmdserial.cmd.payload_len = core::mem::size_of::<ShciBleInitCmdParam>() as u8;
-
-        let p_cmd_buffer = &mut *(*TL_SYS_TABLE.as_mut_ptr()).pcmd_buffer;
-        core::ptr::write(p_cmd_buffer, *cmd_ptr);
-
-        p_cmd_buffer.cmdserial.ty = TlPacketType::SysCmd as u8;
-
-        sys::Sys::send_cmd();
-    }
-}
diff --git a/embassy-stm32-wpan/src/sys.rs b/embassy-stm32-wpan/src/sys.rs
index 0cff5c746..a185cd4f1 100644
--- a/embassy-stm32-wpan/src/sys.rs
+++ b/embassy-stm32-wpan/src/sys.rs
@@ -1,65 +1,70 @@
-use embassy_stm32::ipcc::Ipcc;
+use core::marker::PhantomData;
 
-use crate::cmd::{CmdPacket, CmdSerial};
-use crate::evt::{CcEvt, EvtBox, EvtSerial};
-use crate::tables::SysTable;
+use crate::cmd::CmdPacket;
+use crate::consts::TlPacketType;
+use crate::evt::EvtBox;
+use crate::shci::{ShciBleInitCmdParam, SCHI_OPCODE_BLE_INIT};
+use crate::tables::{SysTable, WirelessFwInfoTable};
 use crate::unsafe_linked_list::LinkedListNode;
-use crate::{channels, EVT_CHANNEL, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_SYS_TABLE};
+use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE};
 
-pub struct Sys;
+pub struct Sys {
+    phantom: PhantomData<Sys>,
+}
 
 impl Sys {
-    pub fn enable() {
+    /// TL_Sys_Init
+    pub(crate) fn new() -> Self {
         unsafe {
             LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
 
             TL_SYS_TABLE.as_mut_ptr().write_volatile(SysTable {
                 pcmd_buffer: SYS_CMD_BUF.as_mut_ptr(),
                 sys_queue: SYSTEM_EVT_QUEUE.as_ptr(),
-            })
+            });
         }
 
-        Ipcc::c1_set_rx_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, true);
+        Self { phantom: PhantomData }
     }
 
-    pub fn cmd_evt_handler() -> CcEvt {
-        Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, false);
+    /// Returns CPU2 wireless firmware information (if present).
+    pub fn wireless_fw_info(&self) -> Option<WirelessFwInfoTable> {
+        let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table };
 
-        // ST's command response data structure is really convoluted.
-        //
-        // for command response events on SYS channel, the header is missing
-        // and one should:
-        // 1. interpret the content of CMD_BUFFER as CmdPacket
-        // 2. Access CmdPacket's cmdserial field and interpret its content as EvtSerial
-        // 3. Access EvtSerial's evt field (as Evt) and interpret its payload as CcEvt
-        // 4. CcEvt type is the actual SHCI response
-        // 5. profit
-        unsafe {
-            let pcmd: *const CmdPacket = (*TL_SYS_TABLE.as_ptr()).pcmd_buffer;
-            let cmd_serial: *const CmdSerial = &(*pcmd).cmdserial;
-            let evt_serial: *const EvtSerial = cmd_serial.cast();
-            let cc: *const CcEvt = (*evt_serial).evt.payload.as_ptr().cast();
-            *cc
+        // Zero version indicates that CPU2 wasn't active and didn't fill the information table
+        if info.version != 0 {
+            Some(info)
+        } else {
+            None
         }
     }
 
-    pub fn evt_handler() {
+    pub fn write(&self, opcode: u16, payload: &[u8]) {
         unsafe {
-            while !LinkedListNode::is_empty(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
-                let node_ptr = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr());
+            CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode, payload);
+        }
+    }
 
-                let event = node_ptr.cast();
-                let event = EvtBox::new(event);
+    pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) {
+        debug!("sending SHCI");
 
-                EVT_CHANNEL.try_send(event).unwrap();
+        Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || {
+            self.write(SCHI_OPCODE_BLE_INIT, param.payload());
+        })
+        .await;
+
+        Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await;
+    }
+
+    /// `HW_IPCC_SYS_EvtNot`
+    pub async fn read(&self) -> EvtBox {
+        Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe {
+            if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) {
+                Some(EvtBox::new(node_ptr.cast()))
+            } else {
+                None
             }
-        }
-
-        Ipcc::c1_clear_flag_channel(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL);
-    }
-
-    pub fn send_cmd() {
-        Ipcc::c1_set_flag_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL);
-        Ipcc::c1_set_tx_channel(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, true);
+        })
+        .await
     }
 }
diff --git a/embassy-stm32-wpan/src/unsafe_linked_list.rs b/embassy-stm32-wpan/src/unsafe_linked_list.rs
index a312178b3..d8bc29763 100644
--- a/embassy-stm32-wpan/src/unsafe_linked_list.rs
+++ b/embassy-stm32-wpan/src/unsafe_linked_list.rs
@@ -117,7 +117,12 @@ impl LinkedListNode {
     /// Remove `node` from the linked list
     pub unsafe fn remove_node(mut p_node: *mut LinkedListNode) {
         interrupt::free(|_| {
-            let node = ptr::read_volatile(p_node);
+            // trace!("remove node: {:x}", p_node);
+            // apparently linked list nodes are not always aligned.
+            // if more hardfaults occur, more of these may need to be converted to unaligned.
+            let node = ptr::read_unaligned(p_node);
+            // trace!("remove node: prev/next {:x}/{:x}", node.prev, node.next);
+
             if node.next != node.prev {
                 let mut node_next = ptr::read_volatile(node.next);
                 let mut node_prev = ptr::read_volatile(node.prev);
@@ -139,28 +144,36 @@ impl LinkedListNode {
     }
 
     /// Remove `list_head` and return a pointer to the `node`.
-    pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> *mut LinkedListNode {
+    pub unsafe fn remove_head(mut p_list_head: *mut LinkedListNode) -> Option<*mut LinkedListNode> {
         interrupt::free(|_| {
             let list_head = ptr::read_volatile(p_list_head);
 
-            // Allowed because a removed node is not seen by another core
-            let p_node = list_head.next;
-            Self::remove_node(p_node);
+            if list_head.next == p_list_head {
+                None
+            } else {
+                // Allowed because a removed node is not seen by another core
+                let p_node = list_head.next;
+                Self::remove_node(p_node);
 
-            p_node
+                Some(p_node)
+            }
         })
     }
 
     /// Remove `list_tail` and return a pointer to the `node`.
-    pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> *mut LinkedListNode {
+    pub unsafe fn remove_tail(mut p_list_tail: *mut LinkedListNode) -> Option<*mut LinkedListNode> {
         interrupt::free(|_| {
             let list_tail = ptr::read_volatile(p_list_tail);
 
-            // Allowed because a removed node is not seen by another core
-            let p_node = list_tail.prev;
-            Self::remove_node(p_node);
+            if list_tail.prev == p_list_tail {
+                None
+            } else {
+                // Allowed because a removed node is not seen by another core
+                let p_node = list_tail.prev;
+                Self::remove_node(p_node);
 
-            p_node
+                Some(p_node)
+            }
         })
     }
 
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index f876c7146..3d9ee8261 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -57,7 +57,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 = "9"
+stm32-metapac = "10"
 vcell = "0.1.3"
 bxcan = "0.7.0"
 nb = "1.0.0"
@@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] }
 [build-dependencies]
 proc-macro2 = "1.0.36"
 quote = "1.0.15"
-stm32-metapac = { version = "9", default-features = false, features = ["metadata"]}
+stm32-metapac = { version = "10", default-features = false, features = ["metadata"]}
 
 [features]
 default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index e2bd01d7b..f7a25743c 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -322,7 +322,7 @@ fn main() {
                     let rst_reg = format_ident!("{}", rst.register.to_ascii_lowercase());
                     let set_rst_field = format_ident!("set_{}", rst.field.to_ascii_lowercase());
                     quote! {
-                        critical_section::with(|_| unsafe {
+                        critical_section::with(|_| {
                             crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(true));
                             crate::pac::RCC.#rst_reg().modify(|w| w.#set_rst_field(false));
                         });
@@ -353,13 +353,13 @@ fn main() {
                         })
                     }
                     fn enable() {
-                        critical_section::with(|_| unsafe {
+                        critical_section::with(|_| {
                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(true));
                             #after_enable
                         })
                     }
                     fn disable() {
-                        critical_section::with(|_| unsafe {
+                        critical_section::with(|_| {
                             crate::pac::RCC.#en_reg().modify(|w| w.#set_en_field(false));
                         })
                     }
diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs
index d30ec001d..2322204d5 100644
--- a/embassy-stm32/src/adc/f1.rs
+++ b/embassy-stm32/src/adc/f1.rs
@@ -32,26 +32,22 @@ impl<'d, T: Instance> Adc<'d, T> {
         into_ref!(adc);
         T::enable();
         T::reset();
-        unsafe {
-            T::regs().cr2().modify(|reg| reg.set_adon(true));
-        }
+        T::regs().cr2().modify(|reg| reg.set_adon(true));
 
         // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’)
         // for at least two ADC clock cycles
         delay.delay_us((1_000_000 * 2) / Self::freq().0 + 1);
 
-        unsafe {
-            // Reset calibration
-            T::regs().cr2().modify(|reg| reg.set_rstcal(true));
-            while T::regs().cr2().read().rstcal() {
-                // spin
-            }
+        // Reset calibration
+        T::regs().cr2().modify(|reg| reg.set_rstcal(true));
+        while T::regs().cr2().read().rstcal() {
+            // spin
+        }
 
-            // Calibrate
-            T::regs().cr2().modify(|reg| reg.set_cal(true));
-            while T::regs().cr2().read().cal() {
-                // spin
-            }
+        // Calibrate
+        T::regs().cr2().modify(|reg| reg.set_cal(true));
+        while T::regs().cr2().read().cal() {
+            // spin
         }
 
         // One cycle after calibration
@@ -81,20 +77,16 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn enable_vref(&self, _delay: &mut impl DelayUs<u32>) -> Vref {
-        unsafe {
-            T::regs().cr2().modify(|reg| {
-                reg.set_tsvrefe(true);
-            })
-        }
+        T::regs().cr2().modify(|reg| {
+            reg.set_tsvrefe(true);
+        });
         Vref {}
     }
 
     pub fn enable_temperature(&self) -> Temperature {
-        unsafe {
-            T::regs().cr2().modify(|reg| {
-                reg.set_tsvrefe(true);
-            })
-        }
+        T::regs().cr2().modify(|reg| {
+            reg.set_tsvrefe(true);
+        });
         Temperature {}
     }
 
@@ -104,41 +96,37 @@ impl<'d, T: Instance> Adc<'d, T> {
 
     /// Perform a single conversion.
     fn convert(&mut self) -> u16 {
-        unsafe {
-            T::regs().cr2().modify(|reg| {
-                reg.set_adon(true);
-                reg.set_swstart(true);
-            });
-            while T::regs().cr2().read().swstart() {}
-            while !T::regs().sr().read().eoc() {}
+        T::regs().cr2().modify(|reg| {
+            reg.set_adon(true);
+            reg.set_swstart(true);
+        });
+        while T::regs().cr2().read().swstart() {}
+        while !T::regs().sr().read().eoc() {}
 
-            T::regs().dr().read().0 as u16
-        }
+        T::regs().dr().read().0 as u16
     }
 
     pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
-        unsafe {
-            Self::set_channel_sample_time(pin.channel(), self.sample_time);
-            T::regs().cr1().modify(|reg| {
-                reg.set_scan(false);
-                reg.set_discen(false);
-            });
-            T::regs().sqr1().modify(|reg| reg.set_l(0));
+        Self::set_channel_sample_time(pin.channel(), self.sample_time);
+        T::regs().cr1().modify(|reg| {
+            reg.set_scan(false);
+            reg.set_discen(false);
+        });
+        T::regs().sqr1().modify(|reg| reg.set_l(0));
 
-            T::regs().cr2().modify(|reg| {
-                reg.set_cont(false);
-                reg.set_exttrig(true);
-                reg.set_swstart(false);
-                reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART);
-            });
-        }
+        T::regs().cr2().modify(|reg| {
+            reg.set_cont(false);
+            reg.set_exttrig(true);
+            reg.set_swstart(false);
+            reg.set_extsel(crate::pac::adc::vals::Extsel::SWSTART);
+        });
 
         // Configure the channel to sample
-        unsafe { T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel())) }
+        T::regs().sqr3().write(|reg| reg.set_sq(0, pin.channel()));
         self.convert()
     }
 
-    unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
+    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
         let sample_time = sample_time.into();
         if ch <= 9 {
             T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs
index 82a8c3efb..d9af0c55e 100644
--- a/embassy-stm32/src/adc/v1.rs
+++ b/embassy-stm32/src/adc/v1.rs
@@ -57,18 +57,14 @@ impl<'d, T: Instance> Adc<'d, T> {
         //
         // 6.3.20 Vbat monitoring characteristics
         // ts_vbat ≥ 4μs
-        unsafe {
-            T::regs().ccr().modify(|reg| reg.set_vbaten(true));
-        }
+        T::regs().ccr().modify(|reg| reg.set_vbaten(true));
         Vbat
     }
 
     pub fn enable_vref(&self, delay: &mut impl DelayUs<u32>) -> Vref {
         // Table 28. Embedded internal reference voltage
         // tstart = 10μs
-        unsafe {
-            T::regs().ccr().modify(|reg| reg.set_vrefen(true));
-        }
+        T::regs().ccr().modify(|reg| reg.set_vrefen(true));
         delay.delay_us(10);
         Vref
     }
@@ -79,27 +75,23 @@ impl<'d, T: Instance> Adc<'d, T> {
         // 6.3.19 Temperature sensor characteristics
         // tstart ≤ 10μs
         // ts_temp ≥ 4μs
-        unsafe {
-            T::regs().ccr().modify(|reg| reg.set_tsen(true));
-        }
+        T::regs().ccr().modify(|reg| reg.set_tsen(true));
         delay.delay_us(10);
         Temperature
     }
 
     fn calibrate(&self) {
-        unsafe {
-            // A.7.1 ADC calibration code example
-            if T::regs().cr().read().aden() {
-                T::regs().cr().modify(|reg| reg.set_addis(true));
-            }
-            while T::regs().cr().read().aden() {
-                // spin
-            }
-            T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
-            T::regs().cr().modify(|reg| reg.set_adcal(true));
-            while T::regs().cr().read().adcal() {
-                // spin
-            }
+        // A.7.1 ADC calibration code example
+        if T::regs().cr().read().aden() {
+            T::regs().cr().modify(|reg| reg.set_addis(true));
+        }
+        while T::regs().cr().read().aden() {
+            // spin
+        }
+        T::regs().cfgr1().modify(|reg| reg.set_dmaen(false));
+        T::regs().cr().modify(|reg| reg.set_adcal(true));
+        while T::regs().cr().read().adcal() {
+            // spin
         }
     }
 
@@ -108,9 +100,7 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn set_resolution(&mut self, resolution: Resolution) {
-        unsafe {
-            T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
-        }
+        T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
     }
 
     pub fn read<P>(&mut self, pin: &mut P) -> u16
@@ -118,18 +108,16 @@ impl<'d, T: Instance> Adc<'d, T> {
         P: AdcPin<T> + crate::gpio::sealed::Pin,
     {
         let channel = pin.channel();
-        unsafe {
-            pin.set_as_analog();
-            self.read_channel(channel)
-        }
+        pin.set_as_analog();
+        self.read_channel(channel)
     }
 
     pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
         let channel = channel.channel();
-        unsafe { self.read_channel(channel) }
+        self.read_channel(channel)
     }
 
-    unsafe fn read_channel(&mut self, channel: u8) -> u16 {
+    fn read_channel(&mut self, channel: u8) -> u16 {
         // A.7.2 ADC enable sequence code example
         if T::regs().isr().read().adrdy() {
             T::regs().isr().modify(|reg| reg.set_adrdy(true));
diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs
index 11a51f993..091c1d447 100644
--- a/embassy-stm32/src/adc/v2.rs
+++ b/embassy-stm32/src/adc/v2.rs
@@ -100,13 +100,10 @@ where
         T::reset();
 
         let presc = Prescaler::from_pclk2(T::frequency());
-        unsafe {
-            T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre()));
-
-            T::regs().cr2().modify(|reg| {
-                reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
-            });
-        }
+        T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre()));
+        T::regs().cr2().modify(|reg| {
+            reg.set_adon(crate::pac::adc::vals::Adon::ENABLED);
+        });
 
         delay.delay_us(ADC_POWERUP_TIME_US);
 
@@ -121,19 +118,15 @@ where
     }
 
     pub fn set_resolution(&mut self, resolution: Resolution) {
-        unsafe {
-            T::regs().cr1().modify(|reg| reg.set_res(resolution.into()));
-        }
+        T::regs().cr1().modify(|reg| reg.set_res(resolution.into()));
     }
 
     /// Enables internal voltage reference and returns [VrefInt], which can be used in
     /// [Adc::read_internal()] to perform conversion.
     pub fn enable_vrefint(&self) -> VrefInt {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
+        });
 
         VrefInt {}
     }
@@ -144,11 +137,9 @@ where
     /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled,
     /// temperature sensor will return vbat value.
     pub fn enable_temperature(&self) -> Temperature {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_tsvrefe(crate::pac::adccommon::vals::Tsvrefe::ENABLED);
+        });
 
         Temperature {}
     }
@@ -156,37 +147,33 @@ where
     /// Enables vbat input and returns [Vbat], which can be used in
     /// [Adc::read_internal()] to perform conversion.
     pub fn enable_vbat(&self) -> Vbat {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vbate(crate::pac::adccommon::vals::Vbate::ENABLED);
+        });
 
         Vbat {}
     }
 
     /// Perform a single conversion.
     fn convert(&mut self) -> u16 {
-        unsafe {
-            // clear end of conversion flag
-            T::regs().sr().modify(|reg| {
-                reg.set_eoc(crate::pac::adc::vals::Eoc::NOTCOMPLETE);
-            });
+        // clear end of conversion flag
+        T::regs().sr().modify(|reg| {
+            reg.set_eoc(crate::pac::adc::vals::Eoc::NOTCOMPLETE);
+        });
 
-            // Start conversion
-            T::regs().cr2().modify(|reg| {
-                reg.set_swstart(true);
-            });
+        // Start conversion
+        T::regs().cr2().modify(|reg| {
+            reg.set_swstart(true);
+        });
 
-            while T::regs().sr().read().strt() == crate::pac::adc::vals::Strt::NOTSTARTED {
-                // spin //wait for actual start
-            }
-            while T::regs().sr().read().eoc() == crate::pac::adc::vals::Eoc::NOTCOMPLETE {
-                // spin //wait for finish
-            }
-
-            T::regs().dr().read().0 as u16
+        while T::regs().sr().read().strt() == crate::pac::adc::vals::Strt::NOTSTARTED {
+            // spin //wait for actual start
         }
+        while T::regs().sr().read().eoc() == crate::pac::adc::vals::Eoc::NOTCOMPLETE {
+            // spin //wait for finish
+        }
+
+        T::regs().dr().read().0 as u16
     }
 
     pub fn read<P>(&mut self, pin: &mut P) -> u16
@@ -194,18 +181,16 @@ where
         P: AdcPin<T>,
         P: crate::gpio::sealed::Pin,
     {
-        unsafe {
-            pin.set_as_analog();
+        pin.set_as_analog();
 
-            self.read_channel(pin.channel())
-        }
+        self.read_channel(pin.channel())
     }
 
     pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
-        unsafe { self.read_channel(channel.channel()) }
+        self.read_channel(channel.channel())
     }
 
-    unsafe fn read_channel(&mut self, channel: u8) -> u16 {
+    fn read_channel(&mut self, channel: u8) -> u16 {
         // Configure ADC
 
         // Select channel
@@ -219,7 +204,7 @@ where
         val
     }
 
-    unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
+    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
         let sample_time = sample_time.into();
         if ch <= 9 {
             T::regs().smpr2().modify(|reg| reg.set_smp(ch as _, sample_time));
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 90aa7d3b9..94cdc86cd 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -12,7 +12,7 @@ pub const VREF_CALIB_MV: u32 = 3000;
 /// Sadly we cannot use `RccPeripheral::enable` since devices are quite inconsistent ADC clock
 /// configuration.
 fn enable() {
-    critical_section::with(|_| unsafe {
+    critical_section::with(|_| {
         #[cfg(stm32h7)]
         crate::pac::RCC.apb2enr().modify(|w| w.set_adcen(true));
         #[cfg(stm32g0)]
@@ -62,29 +62,25 @@ impl<'d, T: Instance> Adc<'d, T> {
     pub fn new(adc: impl Peripheral<P = T> + 'd, delay: &mut impl DelayUs<u32>) -> Self {
         into_ref!(adc);
         enable();
-        unsafe {
-            T::regs().cr().modify(|reg| {
-                #[cfg(not(adc_g0))]
-                reg.set_deeppwd(false);
-                reg.set_advregen(true);
-            });
+        T::regs().cr().modify(|reg| {
+            #[cfg(not(adc_g0))]
+            reg.set_deeppwd(false);
+            reg.set_advregen(true);
+        });
 
-            #[cfg(adc_g0)]
-            T::regs().cfgr1().modify(|reg| {
-                reg.set_chselrmod(false);
-            });
-        }
+        #[cfg(adc_g0)]
+        T::regs().cfgr1().modify(|reg| {
+            reg.set_chselrmod(false);
+        });
 
         delay.delay_us(20);
 
-        unsafe {
-            T::regs().cr().modify(|reg| {
-                reg.set_adcal(true);
-            });
+        T::regs().cr().modify(|reg| {
+            reg.set_adcal(true);
+        });
 
-            while T::regs().cr().read().adcal() {
-                // spin
-            }
+        while T::regs().cr().read().adcal() {
+            // spin
         }
 
         delay.delay_us(1);
@@ -96,11 +92,9 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn enable_vrefint(&self, delay: &mut impl DelayUs<u32>) -> VrefInt {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_vrefen(true);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vrefen(true);
+        });
 
         // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us
         // to stabilize the internal voltage reference, we wait a little more.
@@ -112,21 +106,17 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn enable_temperature(&self) -> Temperature {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_ch17sel(true);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_ch17sel(true);
+        });
 
         Temperature {}
     }
 
     pub fn enable_vbat(&self) -> Vbat {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_ch18sel(true);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_ch18sel(true);
+        });
 
         Vbat {}
     }
@@ -136,12 +126,10 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn set_resolution(&mut self, resolution: Resolution) {
-        unsafe {
-            #[cfg(not(stm32g0))]
-            T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
-            #[cfg(stm32g0)]
-            T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
-        }
+        #[cfg(not(stm32g0))]
+        T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
+        #[cfg(stm32g0)]
+        T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into()));
     }
 
     /*
@@ -155,77 +143,73 @@ impl<'d, T: Instance> Adc<'d, T> {
 
     /// Perform a single conversion.
     fn convert(&mut self) -> u16 {
-        unsafe {
-            T::regs().isr().modify(|reg| {
-                reg.set_eos(true);
-                reg.set_eoc(true);
-            });
+        T::regs().isr().modify(|reg| {
+            reg.set_eos(true);
+            reg.set_eoc(true);
+        });
 
-            // Start conversion
-            T::regs().cr().modify(|reg| {
-                reg.set_adstart(true);
-            });
+        // Start conversion
+        T::regs().cr().modify(|reg| {
+            reg.set_adstart(true);
+        });
 
-            while !T::regs().isr().read().eos() {
-                // spin
-            }
-
-            T::regs().dr().read().0 as u16
+        while !T::regs().isr().read().eos() {
+            // spin
         }
+
+        T::regs().dr().read().0 as u16
     }
 
     pub fn read(&mut self, pin: &mut impl AdcPin<T>) -> u16 {
-        unsafe {
-            // Make sure bits are off
-            while T::regs().cr().read().addis() {
-                // spin
-            }
-
-            // Enable ADC
-            T::regs().isr().modify(|reg| {
-                reg.set_adrdy(true);
-            });
-            T::regs().cr().modify(|reg| {
-                reg.set_aden(true);
-            });
-
-            while !T::regs().isr().read().adrdy() {
-                // spin
-            }
-
-            // Configure channel
-            Self::set_channel_sample_time(pin.channel(), self.sample_time);
-
-            // Select channel
-            #[cfg(not(stm32g0))]
-            T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
-            #[cfg(stm32g0)]
-            T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
-
-            // Some models are affected by an erratum:
-            // If we perform conversions slower than 1 kHz, the first read ADC value can be
-            // corrupted, so we discard it and measure again.
-            //
-            // STM32L471xx: Section 2.7.3
-            // STM32G4: Section 2.7.3
-            #[cfg(any(rcc_l4, rcc_g4))]
-            let _ = self.convert();
-
-            let val = self.convert();
-
-            T::regs().cr().modify(|reg| reg.set_addis(true));
-
-            val
+        // Make sure bits are off
+        while T::regs().cr().read().addis() {
+            // spin
         }
+
+        // Enable ADC
+        T::regs().isr().modify(|reg| {
+            reg.set_adrdy(true);
+        });
+        T::regs().cr().modify(|reg| {
+            reg.set_aden(true);
+        });
+
+        while !T::regs().isr().read().adrdy() {
+            // spin
+        }
+
+        // Configure channel
+        Self::set_channel_sample_time(pin.channel(), self.sample_time);
+
+        // Select channel
+        #[cfg(not(stm32g0))]
+        T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel()));
+        #[cfg(stm32g0)]
+        T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel()));
+
+        // Some models are affected by an erratum:
+        // If we perform conversions slower than 1 kHz, the first read ADC value can be
+        // corrupted, so we discard it and measure again.
+        //
+        // STM32L471xx: Section 2.7.3
+        // STM32G4: Section 2.7.3
+        #[cfg(any(rcc_l4, rcc_g4))]
+        let _ = self.convert();
+
+        let val = self.convert();
+
+        T::regs().cr().modify(|reg| reg.set_addis(true));
+
+        val
     }
 
     #[cfg(stm32g0)]
-    unsafe fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
+    fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) {
         T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into()));
     }
 
     #[cfg(not(stm32g0))]
-    unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
+    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
         let sample_time = sample_time.into();
         if ch <= 9 {
             T::regs().smpr1().modify(|reg| reg.set_smp(ch as _, sample_time));
diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs
index 4707b7c95..c51c6840f 100644
--- a/embassy-stm32/src/adc/v4.rs
+++ b/embassy-stm32/src/adc/v4.rs
@@ -46,8 +46,8 @@ foreach_peripheral!(
     (adc, ADC1) => {
         impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC1 {
             fn frequency() -> crate::time::Hertz {
-                critical_section::with(|_| unsafe {
-                    match crate::rcc::get_freqs().adc {
+                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.")
                     }
@@ -55,7 +55,7 @@ foreach_peripheral!(
             }
 
             fn enable() {
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true))
                 });
                 ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst);
@@ -63,7 +63,7 @@ foreach_peripheral!(
 
             fn disable() {
                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| unsafe {
+                    critical_section::with(|_| {
                         crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false));
                     })
                 }
@@ -72,7 +72,7 @@ foreach_peripheral!(
 
             fn reset() {
                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| unsafe {
+                    critical_section::with(|_| {
                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true));
                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false));
                     });
@@ -85,8 +85,8 @@ foreach_peripheral!(
     (adc, ADC2) => {
         impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC2 {
             fn frequency() -> crate::time::Hertz {
-                critical_section::with(|_| unsafe {
-                    match crate::rcc::get_freqs().adc {
+                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.")
                     }
@@ -94,7 +94,7 @@ foreach_peripheral!(
             }
 
             fn enable() {
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(true))
                 });
                 ADC12_ENABLE_COUNTER.fetch_add(1, Ordering::SeqCst);
@@ -102,7 +102,7 @@ foreach_peripheral!(
 
             fn disable() {
                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| unsafe {
+                    critical_section::with(|_| {
                         crate::pac::RCC.ahb1enr().modify(|w| w.set_adc12en(false));
                     })
                 }
@@ -111,7 +111,7 @@ foreach_peripheral!(
 
             fn reset() {
                 if ADC12_ENABLE_COUNTER.load(Ordering::SeqCst) == 1 {
-                    critical_section::with(|_| unsafe {
+                    critical_section::with(|_| {
                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(true));
                         crate::pac::RCC.ahb1rstr().modify(|w| w.set_adc12rst(false));
                     });
@@ -124,8 +124,8 @@ foreach_peripheral!(
     (adc, ADC3) => {
         impl crate::rcc::sealed::RccPeripheral for crate::peripherals::ADC3 {
             fn frequency() -> crate::time::Hertz {
-                critical_section::with(|_| unsafe {
-                    match crate::rcc::get_freqs().adc {
+                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.")
                     }
@@ -133,22 +133,22 @@ foreach_peripheral!(
             }
 
             fn enable() {
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(true))
                 });
             }
 
             fn disable() {
-                    critical_section::with(|_| unsafe {
-                        crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false));
-                    })
+                critical_section::with(|_| {
+                    crate::pac::RCC.ahb4enr().modify(|w| w.set_adc3en(false));
+                })
             }
 
             fn reset() {
-                    critical_section::with(|_| unsafe {
-                        crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true));
-                        crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false));
-                    });
+                critical_section::with(|_| {
+                    crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(true));
+                    crate::pac::RCC.ahb4rstr().modify(|w| w.set_adc3rst(false));
+                });
             }
         }
 
@@ -232,9 +232,7 @@ impl<'d, T: Instance> Adc<'d, T> {
 
         let prescaler = Prescaler::from_ker_ck(T::frequency());
 
-        unsafe {
-            T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc()));
-        }
+        T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc()));
 
         let frequency = Hertz(T::frequency().0 / prescaler.divisor());
         info!("ADC frequency set to {} Hz", frequency.0);
@@ -251,9 +249,7 @@ impl<'d, T: Instance> Adc<'d, T> {
         } else {
             Boost::LT50
         };
-        unsafe {
-            T::regs().cr().modify(|w| w.set_boost(boost));
-        }
+        T::regs().cr().modify(|w| w.set_boost(boost));
 
         let mut s = Self {
             adc,
@@ -272,84 +268,68 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     fn power_up(&mut self, delay: &mut impl DelayUs<u16>) {
-        unsafe {
-            T::regs().cr().modify(|reg| {
-                reg.set_deeppwd(false);
-                reg.set_advregen(true);
-            });
-        }
+        T::regs().cr().modify(|reg| {
+            reg.set_deeppwd(false);
+            reg.set_advregen(true);
+        });
 
         delay.delay_us(10);
     }
 
     fn configure_differential_inputs(&mut self) {
-        unsafe {
-            T::regs().difsel().modify(|w| {
-                for n in 0..20 {
-                    w.set_difsel(n, Difsel::SINGLEENDED);
-                }
-            })
-        };
+        T::regs().difsel().modify(|w| {
+            for n in 0..20 {
+                w.set_difsel(n, Difsel::SINGLEENDED);
+            }
+        });
     }
 
     fn calibrate(&mut self) {
-        unsafe {
-            T::regs().cr().modify(|w| {
-                w.set_adcaldif(Adcaldif::SINGLEENDED);
-                w.set_adcallin(true);
-            });
+        T::regs().cr().modify(|w| {
+            w.set_adcaldif(Adcaldif::SINGLEENDED);
+            w.set_adcallin(true);
+        });
 
-            T::regs().cr().modify(|w| w.set_adcal(true));
+        T::regs().cr().modify(|w| w.set_adcal(true));
 
-            while T::regs().cr().read().adcal() {}
-        }
+        while T::regs().cr().read().adcal() {}
     }
 
     fn enable(&mut self) {
-        unsafe {
-            T::regs().isr().write(|w| w.set_adrdy(true));
-            T::regs().cr().modify(|w| w.set_aden(true));
-            while !T::regs().isr().read().adrdy() {}
-            T::regs().isr().write(|w| w.set_adrdy(true));
-        }
+        T::regs().isr().write(|w| w.set_adrdy(true));
+        T::regs().cr().modify(|w| w.set_aden(true));
+        while !T::regs().isr().read().adrdy() {}
+        T::regs().isr().write(|w| w.set_adrdy(true));
     }
 
     fn configure(&mut self) {
         // single conversion mode, software trigger
-        unsafe {
-            T::regs().cfgr().modify(|w| {
-                w.set_cont(false);
-                w.set_exten(Exten::DISABLED);
-            })
-        }
+        T::regs().cfgr().modify(|w| {
+            w.set_cont(false);
+            w.set_exten(Exten::DISABLED);
+        });
     }
 
     pub fn enable_vrefint(&self) -> VrefInt {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_vrefen(true);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vrefen(true);
+        });
 
         VrefInt {}
     }
 
     pub fn enable_temperature(&self) -> Temperature {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_vsenseen(true);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vsenseen(true);
+        });
 
         Temperature {}
     }
 
     pub fn enable_vbat(&self) -> Vbat {
-        unsafe {
-            T::common_regs().ccr().modify(|reg| {
-                reg.set_vbaten(true);
-            });
-        }
+        T::common_regs().ccr().modify(|reg| {
+            reg.set_vbaten(true);
+        });
 
         Vbat {}
     }
@@ -359,30 +339,26 @@ impl<'d, T: Instance> Adc<'d, T> {
     }
 
     pub fn set_resolution(&mut self, resolution: Resolution) {
-        unsafe {
-            T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
-        }
+        T::regs().cfgr().modify(|reg| reg.set_res(resolution.into()));
     }
 
     /// Perform a single conversion.
     fn convert(&mut self) -> u16 {
-        unsafe {
-            T::regs().isr().modify(|reg| {
-                reg.set_eos(true);
-                reg.set_eoc(true);
-            });
+        T::regs().isr().modify(|reg| {
+            reg.set_eos(true);
+            reg.set_eoc(true);
+        });
 
-            // Start conversion
-            T::regs().cr().modify(|reg| {
-                reg.set_adstart(true);
-            });
+        // Start conversion
+        T::regs().cr().modify(|reg| {
+            reg.set_adstart(true);
+        });
 
-            while !T::regs().isr().read().eos() {
-                // spin
-            }
-
-            T::regs().dr().read().0 as u16
+        while !T::regs().isr().read().eos() {
+            // spin
         }
+
+        T::regs().dr().read().0 as u16
     }
 
     pub fn read<P>(&mut self, pin: &mut P) -> u16
@@ -390,18 +366,16 @@ impl<'d, T: Instance> Adc<'d, T> {
         P: AdcPin<T>,
         P: crate::gpio::sealed::Pin,
     {
-        unsafe {
-            pin.set_as_analog();
+        pin.set_as_analog();
 
-            self.read_channel(pin.channel())
-        }
+        self.read_channel(pin.channel())
     }
 
     pub fn read_internal(&mut self, channel: &mut impl InternalChannel<T>) -> u16 {
-        unsafe { self.read_channel(channel.channel()) }
+        self.read_channel(channel.channel())
     }
 
-    unsafe fn read_channel(&mut self, channel: u8) -> u16 {
+    fn read_channel(&mut self, channel: u8) -> u16 {
         // Configure channel
         Self::set_channel_sample_time(channel, self.sample_time);
 
@@ -417,7 +391,7 @@ impl<'d, T: Instance> Adc<'d, T> {
         self.convert()
     }
 
-    unsafe fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
+    fn set_channel_sample_time(ch: u8, sample_time: SampleTime) {
         let sample_time = sample_time.into();
         if ch <= 9 {
             T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time));
diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs
index bd92b35a0..85f6e99ac 100644
--- a/embassy-stm32/src/can/bxcan.rs
+++ b/embassy-stm32/src/can/bxcan.rs
@@ -20,10 +20,8 @@ impl<'d, T: Instance> Can<'d, T> {
     ) -> Self {
         into_ref!(peri, rx, tx);
 
-        unsafe {
-            rx.set_as_af(rx.af_num(), AFType::Input);
-            tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
-        }
+        rx.set_as_af(rx.af_num(), AFType::Input);
+        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
 
         T::enable();
         T::reset();
@@ -42,10 +40,8 @@ impl<'d, T: Instance> Can<'d, T> {
     ) -> Self {
         into_ref!(peri, rx, tx);
 
-        unsafe {
-            rx.set_as_af(rx.af_num(), AFType::Input);
-            tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
-        }
+        rx.set_as_af(rx.af_num(), AFType::Input);
+        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
 
         T::enable();
         T::reset();
@@ -60,7 +56,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> {
     fn drop(&mut self) {
         // Cannot call `free()` because it moves the instance.
         // Manually reset the peripheral.
-        unsafe { T::regs().mcr().write(|w| w.set_reset(true)) }
+        T::regs().mcr().write(|w| w.set_reset(true));
         T::disable();
     }
 }
@@ -98,7 +94,7 @@ unsafe impl<'d, T: Instance> bxcan::Instance for BxcanInstance<'d, T> {
 foreach_peripheral!(
     (can, $inst:ident) => {
         impl sealed::Instance for peripherals::$inst {
-            const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.0 as *mut _;
+            const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _;
 
             fn regs() -> &'static crate::pac::can::Can {
                 &crate::pac::$inst
diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs
index 393089eed..3946a2d47 100644
--- a/embassy-stm32/src/crc/v1.rs
+++ b/embassy-stm32/src/crc/v1.rs
@@ -27,26 +27,24 @@ impl<'d> Crc<'d> {
 
     /// Resets the CRC unit to default value (0xFFFF_FFFF)
     pub fn reset(&mut self) {
-        unsafe { PAC_CRC.cr().write(|w| w.set_reset(true)) };
+        PAC_CRC.cr().write(|w| w.set_reset(true));
     }
 
     /// Feeds a word to the peripheral and returns the current CRC value
     pub fn feed_word(&mut self, word: u32) -> u32 {
         // write a single byte to the device, and return the result
-        unsafe {
-            PAC_CRC.dr().write_value(word);
-        }
+        PAC_CRC.dr().write_value(word);
         self.read()
     }
     /// Feed a slice of words to the peripheral and return the result.
     pub fn feed_words(&mut self, words: &[u32]) -> u32 {
         for word in words {
-            unsafe { PAC_CRC.dr().write_value(*word) }
+            PAC_CRC.dr().write_value(*word);
         }
 
         self.read()
     }
     pub fn read(&self) -> u32 {
-        unsafe { PAC_CRC.dr().read() }
+        PAC_CRC.dr().read()
     }
 }
diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs
index 8acb3a770..f337055a7 100644
--- a/embassy-stm32/src/crc/v2v3.rs
+++ b/embassy-stm32/src/crc/v2v3.rs
@@ -85,95 +85,79 @@ impl<'d> Crc<'d> {
     }
 
     pub fn reset(&mut self) {
-        unsafe {
-            PAC_CRC.cr().modify(|w| w.set_reset(true));
-        }
+        PAC_CRC.cr().modify(|w| w.set_reset(true));
     }
 
     /// Reconfigures the CRC peripheral. Doesn't reset.
     fn reconfigure(&mut self) {
-        unsafe {
-            // Init CRC value
-            PAC_CRC.init().write_value(self._config.crc_init_value);
-            #[cfg(crc_v3)]
-            PAC_CRC.pol().write_value(self._config.crc_poly);
+        // Init CRC value
+        PAC_CRC.init().write_value(self._config.crc_init_value);
+        #[cfg(crc_v3)]
+        PAC_CRC.pol().write_value(self._config.crc_poly);
 
-            // configure CR components
-            // (reverse I/O, polysize, poly)
-            PAC_CRC.cr().write(|w| {
-                // configure reverse output
-                w.set_rev_out(match self._config.reverse_out {
-                    true => vals::RevOut::REVERSED,
-                    false => vals::RevOut::NORMAL,
-                });
-                // configure reverse input
-                w.set_rev_in(match self._config.reverse_in {
-                    InputReverseConfig::None => vals::RevIn::NORMAL,
-                    InputReverseConfig::Byte => vals::RevIn::BYTE,
-                    InputReverseConfig::Halfword => vals::RevIn::HALFWORD,
-                    InputReverseConfig::Word => vals::RevIn::WORD,
-                });
-                // configure the polynomial.
-                #[cfg(crc_v3)]
-                w.set_polysize(match self._config.poly_size {
-                    PolySize::Width7 => vals::Polysize::POLYSIZE7,
-                    PolySize::Width8 => vals::Polysize::POLYSIZE8,
-                    PolySize::Width16 => vals::Polysize::POLYSIZE16,
-                    PolySize::Width32 => vals::Polysize::POLYSIZE32,
-                });
-            })
-        }
+        // configure CR components
+        // (reverse I/O, polysize, poly)
+        PAC_CRC.cr().write(|w| {
+            // configure reverse output
+            w.set_rev_out(match self._config.reverse_out {
+                true => vals::RevOut::REVERSED,
+                false => vals::RevOut::NORMAL,
+            });
+            // configure reverse input
+            w.set_rev_in(match self._config.reverse_in {
+                InputReverseConfig::None => vals::RevIn::NORMAL,
+                InputReverseConfig::Byte => vals::RevIn::BYTE,
+                InputReverseConfig::Halfword => vals::RevIn::HALFWORD,
+                InputReverseConfig::Word => vals::RevIn::WORD,
+            });
+            // configure the polynomial.
+            #[cfg(crc_v3)]
+            w.set_polysize(match self._config.poly_size {
+                PolySize::Width7 => vals::Polysize::POLYSIZE7,
+                PolySize::Width8 => vals::Polysize::POLYSIZE8,
+                PolySize::Width16 => vals::Polysize::POLYSIZE16,
+                PolySize::Width32 => vals::Polysize::POLYSIZE32,
+            });
+        });
 
         self.reset();
     }
 
     /// Feeds a byte into the CRC peripheral. Returns the computed checksum.
     pub fn feed_byte(&mut self, byte: u8) -> u32 {
-        unsafe {
-            PAC_CRC.dr8().write_value(byte);
-            PAC_CRC.dr().read()
-        }
+        PAC_CRC.dr8().write_value(byte);
+        PAC_CRC.dr().read()
     }
 
     /// Feeds an slice of bytes into the CRC peripheral. Returns the computed checksum.
     pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 {
         for byte in bytes {
-            unsafe {
-                PAC_CRC.dr8().write_value(*byte);
-            }
+            PAC_CRC.dr8().write_value(*byte);
         }
-        unsafe { PAC_CRC.dr().read() }
+        PAC_CRC.dr().read()
     }
     /// Feeds a halfword into the CRC peripheral. Returns the computed checksum.
     pub fn feed_halfword(&mut self, halfword: u16) -> u32 {
-        unsafe {
-            PAC_CRC.dr16().write_value(halfword);
-            PAC_CRC.dr().read()
-        }
+        PAC_CRC.dr16().write_value(halfword);
+        PAC_CRC.dr().read()
     }
     /// Feeds an slice of halfwords into the CRC peripheral. Returns the computed checksum.
     pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 {
         for halfword in halfwords {
-            unsafe {
-                PAC_CRC.dr16().write_value(*halfword);
-            }
+            PAC_CRC.dr16().write_value(*halfword);
         }
-        unsafe { PAC_CRC.dr().read() }
+        PAC_CRC.dr().read()
     }
     /// Feeds a words into the CRC peripheral. Returns the computed checksum.
     pub fn feed_word(&mut self, word: u32) -> u32 {
-        unsafe {
-            PAC_CRC.dr().write_value(word as u32);
-            PAC_CRC.dr().read()
-        }
+        PAC_CRC.dr().write_value(word as u32);
+        PAC_CRC.dr().read()
     }
     /// Feeds an slice of words into the CRC peripheral. Returns the computed checksum.
     pub fn feed_words(&mut self, words: &[u32]) -> u32 {
         for word in words {
-            unsafe {
-                PAC_CRC.dr().write_value(*word as u32);
-            }
+            PAC_CRC.dr().write_value(*word as u32);
         }
-        unsafe { PAC_CRC.dr().read() }
+        PAC_CRC.dr().read()
     }
 }
diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs
index 41305d273..78b026cb6 100644
--- a/embassy-stm32/src/dcmi.rs
+++ b/embassy-stm32/src/dcmi.rs
@@ -96,8 +96,7 @@ impl Default for Config {
 macro_rules! config_pins {
     ($($pin:ident),*) => {
         into_ref!($($pin),*);
-        // NOTE(unsafe) Exclusive access to the registers
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             $(
                 $pin.set_as_af($pin.af_num(), AFType::Input);
                 $pin.set_speed(Speed::VeryHigh);
@@ -334,17 +333,15 @@ where
         T::reset();
         T::enable();
 
-        unsafe {
-            peri.regs().cr().modify(|r| {
-                r.set_cm(true); // disable continuous mode (snapshot mode)
-                r.set_ess(use_embedded_synchronization);
-                r.set_pckpol(config.pixclk_polarity == PixelClockPolarity::RisingEdge);
-                r.set_vspol(config.vsync_level == VSyncDataInvalidLevel::High);
-                r.set_hspol(config.hsync_level == HSyncDataInvalidLevel::High);
-                r.set_fcrc(0x00); // capture every frame
-                r.set_edm(edm); // extended data mode
-            });
-        }
+        peri.regs().cr().modify(|r| {
+            r.set_cm(true); // disable continuous mode (snapshot mode)
+            r.set_ess(use_embedded_synchronization);
+            r.set_pckpol(config.pixclk_polarity == PixelClockPolarity::RisingEdge);
+            r.set_vspol(config.vsync_level == VSyncDataInvalidLevel::High);
+            r.set_hspol(config.hsync_level == HSyncDataInvalidLevel::High);
+            r.set_fcrc(0x00); // capture every frame
+            r.set_edm(edm); // extended data mode
+        });
 
         T::Interrupt::unpend();
         unsafe { T::Interrupt::enable() };
@@ -352,7 +349,7 @@ where
         Self { inner: peri, dma }
     }
 
-    unsafe fn toggle(enable: bool) {
+    fn toggle(enable: bool) {
         crate::pac::DCMI.cr().modify(|r| {
             r.set_enable(enable);
             r.set_capture(enable);
@@ -360,23 +357,19 @@ where
     }
 
     fn enable_irqs() {
-        unsafe {
-            crate::pac::DCMI.ier().modify(|r| {
-                r.set_err_ie(true);
-                r.set_ovr_ie(true);
-                r.set_frame_ie(true);
-            });
-        }
+        crate::pac::DCMI.ier().modify(|r| {
+            r.set_err_ie(true);
+            r.set_ovr_ie(true);
+            r.set_frame_ie(true);
+        });
     }
 
     fn clear_interrupt_flags() {
-        unsafe {
-            crate::pac::DCMI.icr().write(|r| {
-                r.set_ovr_isc(true);
-                r.set_err_isc(true);
-                r.set_frame_isc(true);
-            })
-        }
+        crate::pac::DCMI.icr().write(|r| {
+            r.set_ovr_isc(true);
+            r.set_err_isc(true);
+            r.set_frame_isc(true);
+        })
     }
 
     /// This method starts the capture and finishes when both the dma transfer and DCMI finish the frame transfer.
@@ -392,41 +385,30 @@ where
             return self.capture_giant(buffer).await;
         }
     }
+
     async fn capture_small(&mut self, buffer: &mut [u32]) -> Result<(), Error> {
         let r = self.inner.regs();
-        let src = r.dr().ptr() as *mut u32;
+        let src = r.dr().as_ptr() as *mut u32;
         let request = self.dma.request();
         let dma_read = unsafe { Transfer::new_read(&mut self.dma, request, src, buffer, Default::default()) };
 
         Self::clear_interrupt_flags();
         Self::enable_irqs();
 
-        unsafe { Self::toggle(true) };
+        Self::toggle(true);
 
         let result = poll_fn(|cx| {
             STATE.waker.register(cx.waker());
 
-            let ris = unsafe { crate::pac::DCMI.ris().read() };
+            let ris = crate::pac::DCMI.ris().read();
             if ris.err_ris() {
-                unsafe {
-                    crate::pac::DCMI.icr().write(|r| {
-                        r.set_err_isc(true);
-                    })
-                };
+                crate::pac::DCMI.icr().write(|r| r.set_err_isc(true));
                 Poll::Ready(Err(Error::PeripheralError))
             } else if ris.ovr_ris() {
-                unsafe {
-                    crate::pac::DCMI.icr().write(|r| {
-                        r.set_ovr_isc(true);
-                    })
-                };
+                crate::pac::DCMI.icr().write(|r| r.set_ovr_isc(true));
                 Poll::Ready(Err(Error::Overrun))
             } else if ris.frame_ris() {
-                unsafe {
-                    crate::pac::DCMI.icr().write(|r| {
-                        r.set_frame_isc(true);
-                    })
-                };
+                crate::pac::DCMI.icr().write(|r| r.set_frame_isc(true));
                 Poll::Ready(Ok(()))
             } else {
                 Poll::Pending
@@ -435,7 +417,7 @@ where
 
         let (_, result) = embassy_futures::join::join(dma_read, result).await;
 
-        unsafe { Self::toggle(false) };
+        Self::toggle(false);
 
         result
     }
@@ -468,7 +450,7 @@ where
         let request = channel.request();
 
         let r = self.inner.regs();
-        let src = r.dr().ptr() as *mut u32;
+        let src = r.dr().as_ptr() as *mut u32;
 
         let mut transfer = unsafe {
             crate::dma::DoubleBuffered::new_read(
@@ -526,38 +508,26 @@ where
         let result = poll_fn(|cx| {
             STATE.waker.register(cx.waker());
 
-            let ris = unsafe { crate::pac::DCMI.ris().read() };
+            let ris = crate::pac::DCMI.ris().read();
             if ris.err_ris() {
-                unsafe {
-                    crate::pac::DCMI.icr().write(|r| {
-                        r.set_err_isc(true);
-                    })
-                };
+                crate::pac::DCMI.icr().write(|r| r.set_err_isc(true));
                 Poll::Ready(Err(Error::PeripheralError))
             } else if ris.ovr_ris() {
-                unsafe {
-                    crate::pac::DCMI.icr().write(|r| {
-                        r.set_ovr_isc(true);
-                    })
-                };
+                crate::pac::DCMI.icr().write(|r| r.set_ovr_isc(true));
                 Poll::Ready(Err(Error::Overrun))
             } else if ris.frame_ris() {
-                unsafe {
-                    crate::pac::DCMI.icr().write(|r| {
-                        r.set_frame_isc(true);
-                    })
-                };
+                crate::pac::DCMI.icr().write(|r| r.set_frame_isc(true));
                 Poll::Ready(Ok(()))
             } else {
                 Poll::Pending
             }
         });
 
-        unsafe { Self::toggle(true) };
+        Self::toggle(true);
 
         let (_, result) = embassy_futures::join::join(dma_result, result).await;
 
-        unsafe { Self::toggle(false) };
+        Self::toggle(false);
 
         result
     }
diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs
index ca18047e7..be4571440 100644
--- a/embassy-stm32/src/dma/bdma.rs
+++ b/embassy-stm32/src/dma/bdma.rs
@@ -114,7 +114,7 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::bdma::Dma, channel_num: usize, index
     let cr = dma.ch(channel_num).cr();
 
     if isr.teif(channel_num) {
-        panic!("DMA: error on BDMA@{:08x} channel {}", dma.0 as u32, channel_num);
+        panic!("DMA: error on BDMA@{:08x} channel {}", dma.as_ptr() as u32, channel_num);
     }
 
     if isr.htif(channel_num) && cr.read().htie() {
@@ -306,29 +306,25 @@ impl<'a, C: Channel> Transfer<'a, C> {
     }
 
     fn clear_irqs(&mut self) {
-        unsafe {
-            self.channel.regs().ifcr().write(|w| {
-                w.set_tcif(self.channel.num(), true);
-                w.set_teif(self.channel.num(), true);
-            })
-        }
+        self.channel.regs().ifcr().write(|w| {
+            w.set_tcif(self.channel.num(), true);
+            w.set_teif(self.channel.num(), true);
+        });
     }
 
     pub fn request_stop(&mut self) {
         let ch = self.channel.regs().ch(self.channel.num());
 
         // Disable the channel. Keep the IEs enabled so the irqs still fire.
-        unsafe {
-            ch.cr().write(|w| {
-                w.set_teie(true);
-                w.set_tcie(true);
-            })
-        }
+        ch.cr().write(|w| {
+            w.set_teie(true);
+            w.set_tcie(true);
+        });
     }
 
     pub fn is_running(&mut self) -> bool {
         let ch = self.channel.regs().ch(self.channel.num());
-        let en = unsafe { ch.cr().read() }.en();
+        let en = ch.cr().read().en();
         let circular = unsafe { ch.cr().read() }.circ() == vals::Circ::ENABLED;
         let tcif = STATE.complete_count[self.channel.index()].load(Ordering::Acquire) != 0;
         en && (circular || !tcif)
@@ -338,7 +334,7 @@ impl<'a, C: Channel> Transfer<'a, C> {
     /// Note: this will be zero for transfers that completed without cancellation.
     pub fn get_remaining_transfers(&self) -> u16 {
         let ch = self.channel.regs().ch(self.channel.num());
-        unsafe { ch.ndtr().read() }.ndt()
+        ch.ndtr().read().ndt()
     }
 
     pub fn blocking_wait(mut self) {
@@ -382,7 +378,7 @@ struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>);
 impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
     fn get_remaining_transfers(&self) -> usize {
         let ch = self.0.regs().ch(self.0.num());
-        unsafe { ch.ndtr().read() }.ndt() as usize
+        ch.ndtr().read().ndt() as usize
     }
 
     fn get_complete_count(&self) -> usize {
@@ -458,7 +454,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
 
     pub fn start(&mut self) {
         let ch = self.channel.regs().ch(self.channel.num());
-        unsafe { ch.cr().write_value(self.cr) }
+        ch.cr().write_value(self.cr)
     }
 
     pub fn clear(&mut self) {
@@ -485,13 +481,11 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
 
     fn clear_irqs(&mut self) {
         let dma = self.channel.regs();
-        unsafe {
-            dma.ifcr().write(|w| {
-                w.set_htif(self.channel.num(), true);
-                w.set_tcif(self.channel.num(), true);
-                w.set_teif(self.channel.num(), true);
-            })
-        }
+        dma.ifcr().write(|w| {
+            w.set_htif(self.channel.num(), true);
+            w.set_tcif(self.channel.num(), true);
+            w.set_teif(self.channel.num(), true);
+        });
     }
 
     pub fn request_stop(&mut self) {
@@ -500,18 +494,16 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
         // Disable the channel. Keep the IEs enabled so the irqs still fire.
         // If the channel is enabled and transfer is not completed, we need to perform
         // two separate write access to the CR register to disable the channel.
-        unsafe {
-            ch.cr().write(|w| {
-                w.set_teie(true);
-                w.set_htie(true);
-                w.set_tcie(true);
-            })
-        }
+        ch.cr().write(|w| {
+            w.set_teie(true);
+            w.set_htie(true);
+            w.set_tcie(true);
+        });
     }
 
     pub fn is_running(&mut self) -> bool {
         let ch = self.channel.regs().ch(self.channel.num());
-        unsafe { ch.cr().read() }.en()
+        ch.cr().read().en()
     }
 }
 
diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs
index 874cb013a..8abe541d3 100644
--- a/embassy-stm32/src/dma/dma.rs
+++ b/embassy-stm32/src/dma/dma.rs
@@ -183,7 +183,7 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::dma::Dma, channel_num: usize, index:
     let isr = dma.isr(channel_num / 4).read();
 
     if isr.teif(channel_num % 4) {
-        panic!("DMA: error on DMA@{:08x} channel {}", dma.0 as u32, channel_num);
+        panic!("DMA: error on DMA@{:08x} channel {}", dma.as_ptr() as u32, channel_num);
     }
 
     if isr.htif(channel_num % 4) && cr.read().htie() {
@@ -387,36 +387,32 @@ impl<'a, C: Channel> Transfer<'a, C> {
         let isrn = self.channel.num() / 4;
         let isrbit = self.channel.num() % 4;
 
-        unsafe {
-            self.channel.regs().ifcr(isrn).write(|w| {
-                w.set_tcif(isrbit, true);
-                w.set_teif(isrbit, true);
-            })
-        }
+        self.channel.regs().ifcr(isrn).write(|w| {
+            w.set_tcif(isrbit, true);
+            w.set_teif(isrbit, true);
+        });
     }
 
     pub fn request_stop(&mut self) {
         let ch = self.channel.regs().st(self.channel.num());
 
         // Disable the channel. Keep the IEs enabled so the irqs still fire.
-        unsafe {
-            ch.cr().write(|w| {
-                w.set_teie(true);
-                w.set_tcie(true);
-            })
-        }
+        ch.cr().write(|w| {
+            w.set_teie(true);
+            w.set_tcie(true);
+        });
     }
 
     pub fn is_running(&mut self) -> bool {
         let ch = self.channel.regs().st(self.channel.num());
-        unsafe { ch.cr().read() }.en()
+        ch.cr().read().en()
     }
 
     /// Gets the total remaining transfers for the channel
     /// Note: this will be zero for transfers that completed without cancellation.
     pub fn get_remaining_transfers(&self) -> u16 {
         let ch = self.channel.regs().st(self.channel.num());
-        unsafe { ch.ndtr().read() }.ndt()
+        ch.ndtr().read().ndt()
     }
 
     pub fn blocking_wait(mut self) {
@@ -537,13 +533,11 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
         let isrn = channel_number / 4;
         let isrbit = channel_number % 4;
 
-        unsafe {
-            dma.ifcr(isrn).write(|w| {
-                w.set_htif(isrbit, true);
-                w.set_tcif(isrbit, true);
-                w.set_teif(isrbit, true);
-            })
-        }
+        dma.ifcr(isrn).write(|w| {
+            w.set_htif(isrbit, true);
+            w.set_tcif(isrbit, true);
+            w.set_teif(isrbit, true);
+        });
     }
 
     pub unsafe fn set_buffer0(&mut self, buffer: *mut W) {
@@ -558,7 +552,7 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
 
     pub fn is_buffer0_accessible(&mut self) -> bool {
         let ch = self.channel.regs().st(self.channel.num());
-        unsafe { ch.cr().read() }.ct() == vals::Ct::MEMORY1
+        ch.cr().read().ct() == vals::Ct::MEMORY1
     }
 
     pub fn set_waker(&mut self, waker: &Waker) {
@@ -569,24 +563,22 @@ impl<'a, C: Channel, W: Word> DoubleBuffered<'a, C, W> {
         let ch = self.channel.regs().st(self.channel.num());
 
         // Disable the channel. Keep the IEs enabled so the irqs still fire.
-        unsafe {
-            ch.cr().write(|w| {
-                w.set_teie(true);
-                w.set_tcie(true);
-            })
-        }
+        ch.cr().write(|w| {
+            w.set_teie(true);
+            w.set_tcie(true);
+        });
     }
 
     pub fn is_running(&mut self) -> bool {
         let ch = self.channel.regs().st(self.channel.num());
-        unsafe { ch.cr().read() }.en()
+        ch.cr().read().en()
     }
 
     /// Gets the total remaining transfers for the channel
     /// Note: this will be zero for transfers that completed without cancellation.
     pub fn get_remaining_transfers(&self) -> u16 {
         let ch = self.channel.regs().st(self.channel.num());
-        unsafe { ch.ndtr().read() }.ndt()
+        ch.ndtr().read().ndt()
     }
 }
 
@@ -607,7 +599,7 @@ struct DmaCtrlImpl<'a, C: Channel>(PeripheralRef<'a, C>);
 impl<'a, C: Channel> DmaCtrl for DmaCtrlImpl<'a, C> {
     fn get_remaining_transfers(&self) -> usize {
         let ch = self.0.regs().st(self.0.num());
-        unsafe { ch.ndtr().read() }.ndt() as usize
+        ch.ndtr().read().ndt() as usize
     }
 
     fn get_complete_count(&self) -> usize {
@@ -698,7 +690,7 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
 
     pub fn start(&mut self) {
         let ch = self.channel.regs().st(self.channel.num());
-        unsafe { ch.cr().write_value(self.cr) }
+        ch.cr().write_value(self.cr);
     }
 
     pub fn clear(&mut self) {
@@ -729,31 +721,27 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> {
         let isrn = channel_number / 4;
         let isrbit = channel_number % 4;
 
-        unsafe {
-            dma.ifcr(isrn).write(|w| {
-                w.set_htif(isrbit, true);
-                w.set_tcif(isrbit, true);
-                w.set_teif(isrbit, true);
-            })
-        }
+        dma.ifcr(isrn).write(|w| {
+            w.set_htif(isrbit, true);
+            w.set_tcif(isrbit, true);
+            w.set_teif(isrbit, true);
+        });
     }
 
     pub fn request_stop(&mut self) {
         let ch = self.channel.regs().st(self.channel.num());
 
         // Disable the channel. Keep the IEs enabled so the irqs still fire.
-        unsafe {
-            ch.cr().write(|w| {
-                w.set_teie(true);
-                w.set_htie(true);
-                w.set_tcie(true);
-            })
-        }
+        ch.cr().write(|w| {
+            w.set_teie(true);
+            w.set_htie(true);
+            w.set_tcie(true);
+        });
     }
 
     pub fn is_running(&mut self) -> bool {
         let ch = self.channel.regs().st(self.channel.num());
-        unsafe { ch.cr().read() }.en()
+        ch.cr().read().en()
     }
 }
 
diff --git a/embassy-stm32/src/dma/dmamux.rs b/embassy-stm32/src/dma/dmamux.rs
index a8c4c5827..36fc03403 100644
--- a/embassy-stm32/src/dma/dmamux.rs
+++ b/embassy-stm32/src/dma/dmamux.rs
@@ -2,7 +2,7 @@
 
 use crate::{pac, peripherals};
 
-pub(crate) unsafe fn configure_dmamux<M: MuxChannel>(channel: &mut M, request: u8) {
+pub(crate) fn configure_dmamux<M: MuxChannel>(channel: &mut M, request: u8) {
     let ch_mux_regs = channel.mux_regs().ccr(channel.mux_num());
     ch_mux_regs.write(|reg| {
         reg.set_nbreq(0);
diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs
index 3f0d5e8fa..c600df92d 100644
--- a/embassy-stm32/src/dma/gpdma.rs
+++ b/embassy-stm32/src/dma/gpdma.rs
@@ -92,13 +92,15 @@ pub(crate) unsafe fn on_irq_inner(dma: pac::gpdma::Gpdma, channel_num: usize, in
     if sr.dtef() {
         panic!(
             "DMA: data transfer error on DMA@{:08x} channel {}",
-            dma.0 as u32, channel_num
+            dma.as_ptr() as u32,
+            channel_num
         );
     }
     if sr.usef() {
         panic!(
             "DMA: user settings error on DMA@{:08x} channel {}",
-            dma.0 as u32, channel_num
+            dma.as_ptr() as u32,
+            channel_num
         );
     }
 
@@ -298,26 +300,24 @@ impl<'a, C: Channel> Transfer<'a, C> {
         let ch = self.channel.regs().ch(self.channel.num());
 
         // Disable the channel. Keep the IEs enabled so the irqs still fire.
-        unsafe {
-            ch.cr().write(|w| {
-                w.set_tcie(true);
-                w.set_useie(true);
-                w.set_dteie(true);
-                w.set_suspie(true);
-            })
-        }
+        ch.cr().write(|w| {
+            w.set_tcie(true);
+            w.set_useie(true);
+            w.set_dteie(true);
+            w.set_suspie(true);
+        })
     }
 
     pub fn is_running(&mut self) -> bool {
         let ch = self.channel.regs().ch(self.channel.num());
-        !unsafe { ch.sr().read() }.tcf()
+        !ch.sr().read().tcf()
     }
 
     /// Gets the total remaining transfers for the channel
     /// Note: this will be zero for transfers that completed without cancellation.
     pub fn get_remaining_transfers(&self) -> u16 {
         let ch = self.channel.regs().ch(self.channel.num());
-        unsafe { ch.br1().read() }.bndt()
+        ch.br1().read().bndt()
     }
 
     pub fn blocking_wait(mut self) {
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs
index 540cdd027..b53c2d0fa 100644
--- a/embassy-stm32/src/eth/v1/mod.rs
+++ b/embassy-stm32/src/eth/v1/mod.rs
@@ -29,18 +29,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandl
         WAKER.wake();
 
         // TODO: Check and clear more flags
-        unsafe {
-            let dma = ETH.ethernet_dma();
+        let dma = ETH.ethernet_dma();
 
-            dma.dmasr().modify(|w| {
-                w.set_ts(true);
-                w.set_rs(true);
-                w.set_nis(true);
-            });
-            // Delay two peripheral's clock
-            dma.dmasr().read();
-            dma.dmasr().read();
-        }
+        dma.dmasr().modify(|w| {
+            w.set_ts(true);
+            w.set_rs(true);
+            w.set_nis(true);
+        });
+        // Delay two peripheral's clock
+        dma.dmasr().read();
+        dma.dmasr().read();
     }
 }
 
@@ -59,7 +57,6 @@ pub struct Ethernet<'d, T: Instance, P: PHY> {
 #[cfg(eth_v1a)]
 macro_rules! config_in_pins {
     ($($pin:ident),*) => {
-        // NOTE(unsafe) Exclusive access to the registers
         critical_section::with(|_| {
             $(
                 // TODO properly create a set_as_input function
@@ -72,7 +69,6 @@ macro_rules! config_in_pins {
 #[cfg(eth_v1a)]
 macro_rules! config_af_pins {
     ($($pin:ident),*) => {
-        // NOTE(unsafe) Exclusive access to the registers
         critical_section::with(|_| {
             $(
                 // We are lucky here, this configures to max speed (50MHz)
@@ -85,7 +81,6 @@ macro_rules! config_af_pins {
 #[cfg(any(eth_v1b, eth_v1c))]
 macro_rules! config_pins {
     ($($pin:ident),*) => {
-        // NOTE(unsafe) Exclusive access to the registers
         critical_section::with(|_| {
             $(
                 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
@@ -116,222 +111,208 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
     ) -> Self {
         into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
 
-        unsafe {
-            // Enable the necessary Clocks
-            // NOTE(unsafe) We have exclusive access to the registers
-            #[cfg(eth_v1a)]
-            critical_section::with(|_| {
-                RCC.apb2enr().modify(|w| w.set_afioen(true));
+        // Enable the necessary Clocks
+        #[cfg(eth_v1a)]
+        critical_section::with(|_| {
+            RCC.apb2enr().modify(|w| w.set_afioen(true));
 
-                // Select RMII (Reduced Media Independent Interface)
-                // Must be done prior to enabling peripheral clock
-                AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true));
+            // Select RMII (Reduced Media Independent Interface)
+            // Must be done prior to enabling peripheral clock
+            AFIO.mapr().modify(|w| w.set_mii_rmii_sel(true));
 
-                RCC.ahbenr().modify(|w| {
-                    w.set_ethen(true);
-                    w.set_ethtxen(true);
-                    w.set_ethrxen(true);
-                });
+            RCC.ahbenr().modify(|w| {
+                w.set_ethen(true);
+                w.set_ethtxen(true);
+                w.set_ethrxen(true);
+            });
+        });
+
+        #[cfg(any(eth_v1b, eth_v1c))]
+        critical_section::with(|_| {
+            RCC.apb2enr().modify(|w| w.set_syscfgen(true));
+            RCC.ahb1enr().modify(|w| {
+                w.set_ethen(true);
+                w.set_ethtxen(true);
+                w.set_ethrxen(true);
             });
 
-            #[cfg(any(eth_v1b, eth_v1c))]
-            critical_section::with(|_| {
-                RCC.apb2enr().modify(|w| w.set_syscfgen(true));
-                RCC.ahb1enr().modify(|w| {
-                    w.set_ethen(true);
-                    w.set_ethtxen(true);
-                    w.set_ethrxen(true);
-                });
+            // RMII (Reduced Media Independent Interface)
+            SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true));
+        });
 
-                // RMII (Reduced Media Independent Interface)
-                SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true));
-            });
-
-            #[cfg(eth_v1a)]
-            {
-                config_in_pins!(ref_clk, rx_d0, rx_d1);
-                config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en);
-            }
-
-            #[cfg(any(eth_v1b, eth_v1c))]
-            config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
-
-            // NOTE(unsafe) We have exclusive access to the registers
-            let dma = ETH.ethernet_dma();
-            let mac = ETH.ethernet_mac();
-
-            // Reset and wait
-            dma.dmabmr().modify(|w| w.set_sr(true));
-            while dma.dmabmr().read().sr() {}
-
-            mac.maccr().modify(|w| {
-                w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times
-                w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping
-                w.set_fes(Fes::FES100); // fast ethernet speed
-                w.set_dm(Dm::FULLDUPLEX); // full duplex
-                                          // TODO: Carrier sense ? ECRSFD
-            });
-
-            // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
-            // so the LR write must happen after the HR write.
-            mac.maca0hr()
-                .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
-            mac.maca0lr().write(|w| {
-                w.set_maca0l(
-                    u32::from(mac_addr[0])
-                        | (u32::from(mac_addr[1]) << 8)
-                        | (u32::from(mac_addr[2]) << 16)
-                        | (u32::from(mac_addr[3]) << 24),
-                )
-            });
-
-            // pause time
-            mac.macfcr().modify(|w| w.set_pt(0x100));
-
-            // Transfer and Forward, Receive and Forward
-            dma.dmaomr().modify(|w| {
-                w.set_tsf(Tsf::STOREFORWARD);
-                w.set_rsf(Rsf::STOREFORWARD);
-            });
-
-            dma.dmabmr().modify(|w| {
-                w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ?
-            });
-
-            // TODO MTU size setting not found for v1 ethernet, check if correct
-
-            // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
-            let hclk = crate::rcc::get_freqs().ahb1;
-            let hclk_mhz = hclk.0 / 1_000_000;
-
-            // Set the MDC clock frequency in the range 1MHz - 2.5MHz
-            let clock_range = match hclk_mhz {
-                0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
-                25..=34 => Cr::CR_20_35,     // Divide by 16
-                35..=59 => Cr::CR_35_60,     // Divide by 26
-                60..=99 => Cr::CR_60_100,    // Divide by 42
-                100..=149 => Cr::CR_100_150, // Divide by 62
-                150..=216 => Cr::CR_150_168, // Divide by 102
-                _ => {
-                    panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
-                }
-            };
-
-            let pins = [
-                ref_clk.map_into(),
-                mdio.map_into(),
-                mdc.map_into(),
-                crs.map_into(),
-                rx_d0.map_into(),
-                rx_d1.map_into(),
-                tx_d0.map_into(),
-                tx_d1.map_into(),
-                tx_en.map_into(),
-            ];
-
-            let mut this = Self {
-                _peri: peri,
-                pins,
-                _phy: phy,
-                clock_range,
-                phy_addr,
-                mac_addr,
-                tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
-                rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
-            };
-
-            fence(Ordering::SeqCst);
-
-            let mac = ETH.ethernet_mac();
-            let dma = ETH.ethernet_dma();
-
-            mac.maccr().modify(|w| {
-                w.set_re(true);
-                w.set_te(true);
-            });
-            dma.dmaomr().modify(|w| {
-                w.set_ftf(Ftf::FLUSH); // flush transmit fifo (queue)
-                w.set_st(St::STARTED); // start transmitting channel
-                w.set_sr(DmaomrSr::STARTED); // start receiving channel
-            });
-
-            this.rx.demand_poll();
-
-            // Enable interrupts
-            dma.dmaier().modify(|w| {
-                w.set_nise(true);
-                w.set_rie(true);
-                w.set_tie(true);
-            });
-
-            P::phy_reset(&mut this);
-            P::phy_init(&mut this);
-
-            interrupt::ETH.unpend();
-            interrupt::ETH.enable();
-
-            this
+        #[cfg(eth_v1a)]
+        {
+            config_in_pins!(ref_clk, rx_d0, rx_d1);
+            config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en);
         }
+
+        #[cfg(any(eth_v1b, eth_v1c))]
+        config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
+
+        let dma = ETH.ethernet_dma();
+        let mac = ETH.ethernet_mac();
+
+        // Reset and wait
+        dma.dmabmr().modify(|w| w.set_sr(true));
+        while dma.dmabmr().read().sr() {}
+
+        mac.maccr().modify(|w| {
+            w.set_ifg(Ifg::IFG96); // inter frame gap 96 bit times
+            w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping
+            w.set_fes(Fes::FES100); // fast ethernet speed
+            w.set_dm(Dm::FULLDUPLEX); // full duplex
+                                      // TODO: Carrier sense ? ECRSFD
+        });
+
+        // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
+        // so the LR write must happen after the HR write.
+        mac.maca0hr()
+            .modify(|w| w.set_maca0h(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
+        mac.maca0lr().write(|w| {
+            w.set_maca0l(
+                u32::from(mac_addr[0])
+                    | (u32::from(mac_addr[1]) << 8)
+                    | (u32::from(mac_addr[2]) << 16)
+                    | (u32::from(mac_addr[3]) << 24),
+            )
+        });
+
+        // pause time
+        mac.macfcr().modify(|w| w.set_pt(0x100));
+
+        // Transfer and Forward, Receive and Forward
+        dma.dmaomr().modify(|w| {
+            w.set_tsf(Tsf::STOREFORWARD);
+            w.set_rsf(Rsf::STOREFORWARD);
+        });
+
+        dma.dmabmr().modify(|w| {
+            w.set_pbl(Pbl::PBL32) // programmable burst length - 32 ?
+        });
+
+        // TODO MTU size setting not found for v1 ethernet, check if correct
+
+        // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
+        let hclk = unsafe { crate::rcc::get_freqs() }.ahb1;
+        let hclk_mhz = hclk.0 / 1_000_000;
+
+        // Set the MDC clock frequency in the range 1MHz - 2.5MHz
+        let clock_range = match hclk_mhz {
+            0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."),
+            25..=34 => Cr::CR_20_35,     // Divide by 16
+            35..=59 => Cr::CR_35_60,     // Divide by 26
+            60..=99 => Cr::CR_60_100,    // Divide by 42
+            100..=149 => Cr::CR_100_150, // Divide by 62
+            150..=216 => Cr::CR_150_168, // Divide by 102
+            _ => {
+                panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
+            }
+        };
+
+        let pins = [
+            ref_clk.map_into(),
+            mdio.map_into(),
+            mdc.map_into(),
+            crs.map_into(),
+            rx_d0.map_into(),
+            rx_d1.map_into(),
+            tx_d0.map_into(),
+            tx_d1.map_into(),
+            tx_en.map_into(),
+        ];
+
+        let mut this = Self {
+            _peri: peri,
+            pins,
+            _phy: phy,
+            clock_range,
+            phy_addr,
+            mac_addr,
+            tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
+            rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
+        };
+
+        fence(Ordering::SeqCst);
+
+        let mac = ETH.ethernet_mac();
+        let dma = ETH.ethernet_dma();
+
+        mac.maccr().modify(|w| {
+            w.set_re(true);
+            w.set_te(true);
+        });
+        dma.dmaomr().modify(|w| {
+            w.set_ftf(Ftf::FLUSH); // flush transmit fifo (queue)
+            w.set_st(St::STARTED); // start transmitting channel
+            w.set_sr(DmaomrSr::STARTED); // start receiving channel
+        });
+
+        this.rx.demand_poll();
+
+        // Enable interrupts
+        dma.dmaier().modify(|w| {
+            w.set_nise(true);
+            w.set_rie(true);
+            w.set_tie(true);
+        });
+
+        P::phy_reset(&mut this);
+        P::phy_init(&mut this);
+
+        interrupt::ETH.unpend();
+        unsafe { interrupt::ETH.enable() };
+
+        this
     }
 }
 
 unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
     fn smi_read(&mut self, reg: u8) -> u16 {
-        // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
-        unsafe {
-            let mac = ETH.ethernet_mac();
+        let mac = ETH.ethernet_mac();
 
-            mac.macmiiar().modify(|w| {
-                w.set_pa(self.phy_addr);
-                w.set_mr(reg);
-                w.set_mw(Mw::READ); // read operation
-                w.set_cr(self.clock_range);
-                w.set_mb(MbProgress::BUSY); // indicate that operation is in progress
-            });
-            while mac.macmiiar().read().mb() == MbProgress::BUSY {}
-            mac.macmiidr().read().md()
-        }
+        mac.macmiiar().modify(|w| {
+            w.set_pa(self.phy_addr);
+            w.set_mr(reg);
+            w.set_mw(Mw::READ); // read operation
+            w.set_cr(self.clock_range);
+            w.set_mb(MbProgress::BUSY); // indicate that operation is in progress
+        });
+        while mac.macmiiar().read().mb() == MbProgress::BUSY {}
+        mac.macmiidr().read().md()
     }
 
     fn smi_write(&mut self, reg: u8, val: u16) {
-        // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
-        unsafe {
-            let mac = ETH.ethernet_mac();
+        let mac = ETH.ethernet_mac();
 
-            mac.macmiidr().write(|w| w.set_md(val));
-            mac.macmiiar().modify(|w| {
-                w.set_pa(self.phy_addr);
-                w.set_mr(reg);
-                w.set_mw(Mw::WRITE); // write
-                w.set_cr(self.clock_range);
-                w.set_mb(MbProgress::BUSY);
-            });
-            while mac.macmiiar().read().mb() == MbProgress::BUSY {}
-        }
+        mac.macmiidr().write(|w| w.set_md(val));
+        mac.macmiiar().modify(|w| {
+            w.set_pa(self.phy_addr);
+            w.set_mr(reg);
+            w.set_mw(Mw::WRITE); // write
+            w.set_cr(self.clock_range);
+            w.set_mb(MbProgress::BUSY);
+        });
+        while mac.macmiiar().read().mb() == MbProgress::BUSY {}
     }
 }
 
 impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> {
     fn drop(&mut self) {
-        // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers
-        unsafe {
-            let dma = ETH.ethernet_dma();
-            let mac = ETH.ethernet_mac();
+        let dma = ETH.ethernet_dma();
+        let mac = ETH.ethernet_mac();
 
-            // Disable the TX DMA and wait for any previous transmissions to be completed
-            dma.dmaomr().modify(|w| w.set_st(St::STOPPED));
+        // Disable the TX DMA and wait for any previous transmissions to be completed
+        dma.dmaomr().modify(|w| w.set_st(St::STOPPED));
 
-            // Disable MAC transmitter and receiver
-            mac.maccr().modify(|w| {
-                w.set_re(false);
-                w.set_te(false);
-            });
+        // Disable MAC transmitter and receiver
+        mac.maccr().modify(|w| {
+            w.set_re(false);
+            w.set_te(false);
+        });
 
-            dma.dmaomr().modify(|w| w.set_sr(DmaomrSr::STOPPED));
-        }
+        dma.dmaomr().modify(|w| w.set_sr(DmaomrSr::STOPPED));
 
-        // NOTE(unsafe) Exclusive access to the regs
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             for pin in self.pins.iter_mut() {
                 pin.set_as_disconnected();
             }
diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs
index 8b8566d92..01a073bb9 100644
--- a/embassy-stm32/src/eth/v1/rx_desc.rs
+++ b/embassy-stm32/src/eth/v1/rx_desc.rs
@@ -146,12 +146,9 @@ impl<'a> RDesRing<'a> {
         }
 
         // Register rx descriptor start
-        // NOTE (unsafe) Used for atomic writes
-        unsafe {
-            ETH.ethernet_dma()
-                .dmardlar()
-                .write(|w| w.0 = descriptors.as_ptr() as u32);
-        };
+        ETH.ethernet_dma()
+            .dmardlar()
+            .write(|w| w.0 = descriptors.as_ptr() as u32);
         // We already have fences in `set_owned`, which is called in `setup`
 
         Self {
@@ -162,12 +159,12 @@ impl<'a> RDesRing<'a> {
     }
 
     pub(crate) fn demand_poll(&self) {
-        unsafe { ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL)) };
+        ETH.ethernet_dma().dmarpdr().write(|w| w.set_rpd(Rpd::POLL));
     }
 
     /// Get current `RunningState`
     fn running_state(&self) -> RunningState {
-        match unsafe { ETH.ethernet_dma().dmasr().read().rps() } {
+        match ETH.ethernet_dma().dmasr().read().rps() {
             //  Reset or Stop Receive Command issued
             Rps::STOPPED => RunningState::Stopped,
             //  Fetching receive transfer descriptor
diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs
index 0e63c5443..1317d20f4 100644
--- a/embassy-stm32/src/eth/v1/tx_desc.rs
+++ b/embassy-stm32/src/eth/v1/tx_desc.rs
@@ -120,12 +120,9 @@ impl<'a> TDesRing<'a> {
         }
 
         // Register txdescriptor start
-        // NOTE (unsafe) Used for atomic writes
-        unsafe {
-            ETH.ethernet_dma()
-                .dmatdlar()
-                .write(|w| w.0 = descriptors.as_ptr() as u32);
-        }
+        ETH.ethernet_dma()
+            .dmatdlar()
+            .write(|w| w.0 = descriptors.as_ptr() as u32);
 
         Self {
             descriptors,
@@ -169,6 +166,6 @@ impl<'a> TDesRing<'a> {
             self.index = 0
         }
         // Request the DMA engine to poll the latest tx descriptor
-        unsafe { ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1) }
+        ETH.ethernet_dma().dmatpdr().modify(|w| w.0 = 1)
     }
 }
diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs
index 2426596fb..e9799adf1 100644
--- a/embassy-stm32/src/eth/v2/descriptors.rs
+++ b/embassy-stm32/src/eth/v2/descriptors.rs
@@ -73,14 +73,10 @@ impl<'a> TDesRing<'a> {
 
         // Initialize the pointers in the DMA engine. (There will be a memory barrier later
         // before the DMA engine is enabled.)
-        // NOTE (unsafe) Used for atomic writes
-        unsafe {
-            let dma = ETH.ethernet_dma();
-
-            dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);
-            dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1));
-            dma.dmactx_dtpr().write(|w| w.0 = 0);
-        }
+        let dma = ETH.ethernet_dma();
+        dma.dmactx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);
+        dma.dmactx_rlr().write(|w| w.set_tdrl((descriptors.len() as u16) - 1));
+        dma.dmactx_dtpr().write(|w| w.0 = 0);
 
         Self {
             descriptors,
@@ -129,8 +125,7 @@ impl<'a> TDesRing<'a> {
         }
 
         // signal DMA it can try again.
-        // NOTE(unsafe) Atomic write
-        unsafe { ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) }
+        ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0)
     }
 }
 
@@ -199,13 +194,10 @@ impl<'a> RDesRing<'a> {
             desc.set_ready(buffers[i].0.as_mut_ptr());
         }
 
-        unsafe {
-            let dma = ETH.ethernet_dma();
-
-            dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);
-            dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1));
-            dma.dmacrx_dtpr().write(|w| w.0 = 0);
-        }
+        let dma = ETH.ethernet_dma();
+        dma.dmacrx_dlar().write(|w| w.0 = descriptors.as_mut_ptr() as u32);
+        dma.dmacrx_rlr().write(|w| w.set_rdrl((descriptors.len() as u16) - 1));
+        dma.dmacrx_dtpr().write(|w| w.0 = 0);
 
         Self {
             descriptors,
@@ -254,8 +246,7 @@ impl<'a> RDesRing<'a> {
         fence(Ordering::Release);
 
         // signal DMA it can try again.
-        // NOTE(unsafe) Atomic write
-        unsafe { ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0) }
+        ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0);
 
         // Increment index.
         self.index += 1;
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs
index 3e45eafd5..600e1d3bc 100644
--- a/embassy-stm32/src/eth/v2/mod.rs
+++ b/embassy-stm32/src/eth/v2/mod.rs
@@ -20,18 +20,16 @@ impl interrupt::typelevel::Handler<interrupt::typelevel::ETH> for InterruptHandl
         WAKER.wake();
 
         // TODO: Check and clear more flags
-        unsafe {
-            let dma = ETH.ethernet_dma();
+        let dma = ETH.ethernet_dma();
 
-            dma.dmacsr().modify(|w| {
-                w.set_ti(true);
-                w.set_ri(true);
-                w.set_nis(true);
-            });
-            // Delay two peripheral's clock
-            dma.dmacsr().read();
-            dma.dmacsr().read();
-        }
+        dma.dmacsr().modify(|w| {
+            w.set_ti(true);
+            w.set_ri(true);
+            w.set_nis(true);
+        });
+        // Delay two peripheral's clock
+        dma.dmacsr().read();
+        dma.dmacsr().read();
     }
 }
 
@@ -50,7 +48,6 @@ pub struct Ethernet<'d, T: Instance, P: PHY> {
 
 macro_rules! config_pins {
     ($($pin:ident),*) => {
-        // NOTE(unsafe) Exclusive access to the registers
         critical_section::with(|_| {
             $(
                 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
@@ -80,239 +77,225 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> {
     ) -> Self {
         into_ref!(peri, ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
 
-        unsafe {
-            // Enable the necessary Clocks
-            // NOTE(unsafe) We have exclusive access to the registers
-            #[cfg(not(rcc_h5))]
-            critical_section::with(|_| {
-                crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true));
-                crate::pac::RCC.ahb1enr().modify(|w| {
-                    w.set_eth1macen(true);
-                    w.set_eth1txen(true);
-                    w.set_eth1rxen(true);
-                });
-
-                // RMII
-                crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100));
+        // Enable the necessary Clocks
+        #[cfg(not(rcc_h5))]
+        critical_section::with(|_| {
+            crate::pac::RCC.apb4enr().modify(|w| w.set_syscfgen(true));
+            crate::pac::RCC.ahb1enr().modify(|w| {
+                w.set_eth1macen(true);
+                w.set_eth1txen(true);
+                w.set_eth1rxen(true);
             });
 
-            #[cfg(rcc_h5)]
-            critical_section::with(|_| {
-                crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true));
+            // RMII
+            crate::pac::SYSCFG.pmcr().modify(|w| w.set_epis(0b100));
+        });
 
-                crate::pac::RCC.ahb1enr().modify(|w| {
-                    w.set_ethen(true);
-                    w.set_ethtxen(true);
-                    w.set_ethrxen(true);
-                });
+        #[cfg(rcc_h5)]
+        critical_section::with(|_| {
+            crate::pac::RCC.apb3enr().modify(|w| w.set_sbsen(true));
 
-                // RMII
-                crate::pac::SBS
-                    .pmcr()
-                    .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4));
+            crate::pac::RCC.ahb1enr().modify(|w| {
+                w.set_ethen(true);
+                w.set_ethtxen(true);
+                w.set_ethrxen(true);
             });
 
-            config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
+            // RMII
+            crate::pac::SBS
+                .pmcr()
+                .modify(|w| w.set_eth_sel_phy(crate::pac::sbs::vals::EthSelPhy::B_0X4));
+        });
 
-            // NOTE(unsafe) We have exclusive access to the registers
-            let dma = ETH.ethernet_dma();
-            let mac = ETH.ethernet_mac();
-            let mtl = ETH.ethernet_mtl();
+        config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en);
 
-            // Reset and wait
-            dma.dmamr().modify(|w| w.set_swr(true));
-            while dma.dmamr().read().swr() {}
+        let dma = ETH.ethernet_dma();
+        let mac = ETH.ethernet_mac();
+        let mtl = ETH.ethernet_mtl();
 
-            mac.maccr().modify(|w| {
-                w.set_ipg(0b000); // 96 bit times
-                w.set_acs(true);
-                w.set_fes(true);
-                w.set_dm(true);
-                // TODO: Carrier sense ? ECRSFD
-            });
+        // Reset and wait
+        dma.dmamr().modify(|w| w.set_swr(true));
+        while dma.dmamr().read().swr() {}
 
-            // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
-            // so the LR write must happen after the HR write.
-            mac.maca0hr()
-                .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
-            mac.maca0lr().write(|w| {
-                w.set_addrlo(
-                    u32::from(mac_addr[0])
-                        | (u32::from(mac_addr[1]) << 8)
-                        | (u32::from(mac_addr[2]) << 16)
-                        | (u32::from(mac_addr[3]) << 24),
-                )
-            });
+        mac.maccr().modify(|w| {
+            w.set_ipg(0b000); // 96 bit times
+            w.set_acs(true);
+            w.set_fes(true);
+            w.set_dm(true);
+            // TODO: Carrier sense ? ECRSFD
+        });
 
-            mac.macqtx_fcr().modify(|w| w.set_pt(0x100));
+        // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core,
+        // so the LR write must happen after the HR write.
+        mac.maca0hr()
+            .modify(|w| w.set_addrhi(u16::from(mac_addr[4]) | (u16::from(mac_addr[5]) << 8)));
+        mac.maca0lr().write(|w| {
+            w.set_addrlo(
+                u32::from(mac_addr[0])
+                    | (u32::from(mac_addr[1]) << 8)
+                    | (u32::from(mac_addr[2]) << 16)
+                    | (u32::from(mac_addr[3]) << 24),
+            )
+        });
 
-            // disable all MMC RX interrupts
-            mac.mmc_rx_interrupt_mask().write(|w| {
-                w.set_rxcrcerpim(true);
-                w.set_rxalgnerpim(true);
-                w.set_rxucgpim(true);
-                w.set_rxlpiuscim(true);
-                w.set_rxlpitrcim(true)
-            });
+        mac.macqtx_fcr().modify(|w| w.set_pt(0x100));
 
-            // disable all MMC TX interrupts
-            mac.mmc_tx_interrupt_mask().write(|w| {
-                w.set_txscolgpim(true);
-                w.set_txmcolgpim(true);
-                w.set_txgpktim(true);
-                w.set_txlpiuscim(true);
-                w.set_txlpitrcim(true);
-            });
+        // disable all MMC RX interrupts
+        mac.mmc_rx_interrupt_mask().write(|w| {
+            w.set_rxcrcerpim(true);
+            w.set_rxalgnerpim(true);
+            w.set_rxucgpim(true);
+            w.set_rxlpiuscim(true);
+            w.set_rxlpitrcim(true)
+        });
 
-            mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));
-            mtl.mtltx_qomr().modify(|w| w.set_tsf(true));
+        // disable all MMC TX interrupts
+        mac.mmc_tx_interrupt_mask().write(|w| {
+            w.set_txscolgpim(true);
+            w.set_txmcolgpim(true);
+            w.set_txgpktim(true);
+            w.set_txlpiuscim(true);
+            w.set_txlpitrcim(true);
+        });
 
-            dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ?
-            dma.dmacrx_cr().modify(|w| {
-                w.set_rxpbl(1); // 32 ?
-                w.set_rbsz(MTU as u16);
-            });
+        mtl.mtlrx_qomr().modify(|w| w.set_rsf(true));
+        mtl.mtltx_qomr().modify(|w| w.set_tsf(true));
 
-            // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
-            let hclk = crate::rcc::get_freqs().ahb1;
-            let hclk_mhz = hclk.0 / 1_000_000;
+        dma.dmactx_cr().modify(|w| w.set_txpbl(1)); // 32 ?
+        dma.dmacrx_cr().modify(|w| {
+            w.set_rxpbl(1); // 32 ?
+            w.set_rbsz(MTU as u16);
+        });
 
-            // Set the MDC clock frequency in the range 1MHz - 2.5MHz
-            let clock_range = match hclk_mhz {
-                0..=34 => 2,    // Divide by 16
-                35..=59 => 3,   // Divide by 26
-                60..=99 => 0,   // Divide by 42
-                100..=149 => 1, // Divide by 62
-                150..=249 => 4, // Divide by 102
-                250..=310 => 5, // Divide by 124
-                _ => {
-                    panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
-                }
-            };
+        // NOTE(unsafe) We got the peripheral singleton, which means that `rcc::init` was called
+        let hclk = unsafe { crate::rcc::get_freqs() }.ahb1;
+        let hclk_mhz = hclk.0 / 1_000_000;
 
-            let pins = [
-                ref_clk.map_into(),
-                mdio.map_into(),
-                mdc.map_into(),
-                crs.map_into(),
-                rx_d0.map_into(),
-                rx_d1.map_into(),
-                tx_d0.map_into(),
-                tx_d1.map_into(),
-                tx_en.map_into(),
-            ];
+        // Set the MDC clock frequency in the range 1MHz - 2.5MHz
+        let clock_range = match hclk_mhz {
+            0..=34 => 2,    // Divide by 16
+            35..=59 => 3,   // Divide by 26
+            60..=99 => 0,   // Divide by 42
+            100..=149 => 1, // Divide by 62
+            150..=249 => 4, // Divide by 102
+            250..=310 => 5, // Divide by 124
+            _ => {
+                panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider")
+            }
+        };
 
-            let mut this = Self {
-                _peri: peri,
-                tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
-                rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
-                pins,
-                _phy: phy,
-                clock_range,
-                phy_addr,
-                mac_addr,
-            };
+        let pins = [
+            ref_clk.map_into(),
+            mdio.map_into(),
+            mdc.map_into(),
+            crs.map_into(),
+            rx_d0.map_into(),
+            rx_d1.map_into(),
+            tx_d0.map_into(),
+            tx_d1.map_into(),
+            tx_en.map_into(),
+        ];
 
-            fence(Ordering::SeqCst);
+        let mut this = Self {
+            _peri: peri,
+            tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf),
+            rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf),
+            pins,
+            _phy: phy,
+            clock_range,
+            phy_addr,
+            mac_addr,
+        };
 
-            let mac = ETH.ethernet_mac();
-            let mtl = ETH.ethernet_mtl();
-            let dma = ETH.ethernet_dma();
+        fence(Ordering::SeqCst);
 
-            mac.maccr().modify(|w| {
-                w.set_re(true);
-                w.set_te(true);
-            });
-            mtl.mtltx_qomr().modify(|w| w.set_ftq(true));
+        let mac = ETH.ethernet_mac();
+        let mtl = ETH.ethernet_mtl();
+        let dma = ETH.ethernet_dma();
 
-            dma.dmactx_cr().modify(|w| w.set_st(true));
-            dma.dmacrx_cr().modify(|w| w.set_sr(true));
+        mac.maccr().modify(|w| {
+            w.set_re(true);
+            w.set_te(true);
+        });
+        mtl.mtltx_qomr().modify(|w| w.set_ftq(true));
 
-            // Enable interrupts
-            dma.dmacier().modify(|w| {
-                w.set_nie(true);
-                w.set_rie(true);
-                w.set_tie(true);
-            });
+        dma.dmactx_cr().modify(|w| w.set_st(true));
+        dma.dmacrx_cr().modify(|w| w.set_sr(true));
 
-            P::phy_reset(&mut this);
-            P::phy_init(&mut this);
+        // Enable interrupts
+        dma.dmacier().modify(|w| {
+            w.set_nie(true);
+            w.set_rie(true);
+            w.set_tie(true);
+        });
 
-            interrupt::ETH.unpend();
-            interrupt::ETH.enable();
+        P::phy_reset(&mut this);
+        P::phy_init(&mut this);
 
-            this
-        }
+        interrupt::ETH.unpend();
+        unsafe { interrupt::ETH.enable() };
+
+        this
     }
 }
 
 unsafe impl<'d, T: Instance, P: PHY> StationManagement for Ethernet<'d, T, P> {
     fn smi_read(&mut self, reg: u8) -> u16 {
-        // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
-        unsafe {
-            let mac = ETH.ethernet_mac();
+        let mac = ETH.ethernet_mac();
 
-            mac.macmdioar().modify(|w| {
-                w.set_pa(self.phy_addr);
-                w.set_rda(reg);
-                w.set_goc(0b11); // read
-                w.set_cr(self.clock_range);
-                w.set_mb(true);
-            });
-            while mac.macmdioar().read().mb() {}
-            mac.macmdiodr().read().md()
-        }
+        mac.macmdioar().modify(|w| {
+            w.set_pa(self.phy_addr);
+            w.set_rda(reg);
+            w.set_goc(0b11); // read
+            w.set_cr(self.clock_range);
+            w.set_mb(true);
+        });
+        while mac.macmdioar().read().mb() {}
+        mac.macmdiodr().read().md()
     }
 
     fn smi_write(&mut self, reg: u8, val: u16) {
-        // NOTE(unsafe) These registers aren't used in the interrupt and we have `&mut self`
-        unsafe {
-            let mac = ETH.ethernet_mac();
+        let mac = ETH.ethernet_mac();
 
-            mac.macmdiodr().write(|w| w.set_md(val));
-            mac.macmdioar().modify(|w| {
-                w.set_pa(self.phy_addr);
-                w.set_rda(reg);
-                w.set_goc(0b01); // write
-                w.set_cr(self.clock_range);
-                w.set_mb(true);
-            });
-            while mac.macmdioar().read().mb() {}
-        }
+        mac.macmdiodr().write(|w| w.set_md(val));
+        mac.macmdioar().modify(|w| {
+            w.set_pa(self.phy_addr);
+            w.set_rda(reg);
+            w.set_goc(0b01); // write
+            w.set_cr(self.clock_range);
+            w.set_mb(true);
+        });
+        while mac.macmdioar().read().mb() {}
     }
 }
 
 impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> {
     fn drop(&mut self) {
-        // NOTE(unsafe) We have `&mut self` and the interrupt doesn't use this registers
-        unsafe {
-            let dma = ETH.ethernet_dma();
-            let mac = ETH.ethernet_mac();
-            let mtl = ETH.ethernet_mtl();
+        let dma = ETH.ethernet_dma();
+        let mac = ETH.ethernet_mac();
+        let mtl = ETH.ethernet_mtl();
 
-            // Disable the TX DMA and wait for any previous transmissions to be completed
-            dma.dmactx_cr().modify(|w| w.set_st(false));
-            while {
-                let txqueue = mtl.mtltx_qdr().read();
-                txqueue.trcsts() == 0b01 || txqueue.txqsts()
-            } {}
+        // Disable the TX DMA and wait for any previous transmissions to be completed
+        dma.dmactx_cr().modify(|w| w.set_st(false));
+        while {
+            let txqueue = mtl.mtltx_qdr().read();
+            txqueue.trcsts() == 0b01 || txqueue.txqsts()
+        } {}
 
-            // Disable MAC transmitter and receiver
-            mac.maccr().modify(|w| {
-                w.set_re(false);
-                w.set_te(false);
-            });
+        // Disable MAC transmitter and receiver
+        mac.maccr().modify(|w| {
+            w.set_re(false);
+            w.set_te(false);
+        });
 
-            // Wait for previous receiver transfers to be completed and then disable the RX DMA
-            while {
-                let rxqueue = mtl.mtlrx_qdr().read();
-                rxqueue.rxqsts() != 0b00 || rxqueue.prxq() != 0
-            } {}
-            dma.dmacrx_cr().modify(|w| w.set_sr(false));
-        }
+        // Wait for previous receiver transfers to be completed and then disable the RX DMA
+        while {
+            let rxqueue = mtl.mtlrx_qdr().read();
+            rxqueue.rxqsts() != 0b00 || rxqueue.prxq() != 0
+        } {}
+        dma.dmacrx_cr().modify(|w| w.set_sr(false));
 
-        // NOTE(unsafe) Exclusive access to the regs
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             for pin in self.pins.iter_mut() {
                 pin.set_as_disconnected();
             }
diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs
index 0631ae473..3ff92c9e6 100644
--- a/embassy-stm32/src/exti.rs
+++ b/embassy-stm32/src/exti.rs
@@ -206,7 +206,7 @@ struct ExtiInputFuture<'a> {
 
 impl<'a> ExtiInputFuture<'a> {
     fn new(pin: u8, port: u8, rising: bool, falling: bool) -> Self {
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             let pin = pin as usize;
             exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port));
             EXTI.rtsr(0).modify(|w| w.set_line(pin, rising));
@@ -233,7 +233,7 @@ impl<'a> ExtiInputFuture<'a> {
 
 impl<'a> Drop for ExtiInputFuture<'a> {
     fn drop(&mut self) {
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             let pin = self.pin as _;
             cpu_regs().imr(0).modify(|w| w.set_line(pin, false));
         });
@@ -246,7 +246,7 @@ impl<'a> Future for ExtiInputFuture<'a> {
     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
         EXTI_WAKERS[self.pin as usize].register(cx.waker());
 
-        let imr = unsafe { cpu_regs().imr(0).read() };
+        let imr = cpu_regs().imr(0).read();
         if !imr.line(self.pin as _) {
             Poll::Ready(())
         } else {
diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs
index 5e1fc696f..242d99278 100644
--- a/embassy-stm32/src/flash/f4.rs
+++ b/embassy-stm32/src/flash/f4.rs
@@ -192,7 +192,7 @@ impl FlashSector {
 
 #[cfg(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479))]
 pub(crate) fn is_default_layout() -> bool {
-    unsafe { !pac::FLASH.optcr().read().db1m() }
+    !pac::FLASH.optcr().read().db1m()
 }
 
 #[cfg(not(any(stm32f427, stm32f429, stm32f437, stm32f439, stm32f469, stm32f479)))]
@@ -336,7 +336,7 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E
     ret
 }
 
-pub(crate) unsafe fn clear_all_err() {
+pub(crate) fn clear_all_err() {
     pac::FLASH.sr().write(|w| {
         w.set_pgserr(true);
         w.set_pgperr(true);
@@ -345,7 +345,7 @@ pub(crate) unsafe fn clear_all_err() {
     });
 }
 
-pub(crate) async unsafe fn wait_ready() -> Result<(), Error> {
+pub(crate) async fn wait_ready() -> Result<(), Error> {
     use core::task::Poll;
 
     use futures::future::poll_fn;
@@ -391,10 +391,10 @@ fn save_data_cache_state() {
     let dual_bank = get_flash_regions().last().unwrap().bank == FlashBank::Bank2;
     if dual_bank {
         // Disable data cache during write/erase if there are two banks, see errata 2.2.12
-        let dcen = unsafe { pac::FLASH.acr().read().dcen() };
+        let dcen = pac::FLASH.acr().read().dcen();
         DATA_CACHE_WAS_ENABLED.store(dcen, Ordering::Relaxed);
         if dcen {
-            unsafe { pac::FLASH.acr().modify(|w| w.set_dcen(false)) };
+            pac::FLASH.acr().modify(|w| w.set_dcen(false));
         }
     }
 }
@@ -405,12 +405,10 @@ fn restore_data_cache_state() {
         // Restore data cache if it was enabled
         let dcen = DATA_CACHE_WAS_ENABLED.load(Ordering::Relaxed);
         if dcen {
-            unsafe {
-                // Reset data cache before we enable it again
-                pac::FLASH.acr().modify(|w| w.set_dcrst(true));
-                pac::FLASH.acr().modify(|w| w.set_dcrst(false));
-                pac::FLASH.acr().modify(|w| w.set_dcen(true))
-            };
+            // Reset data cache before we enable it again
+            pac::FLASH.acr().modify(|w| w.set_dcrst(true));
+            pac::FLASH.acr().modify(|w| w.set_dcrst(false));
+            pac::FLASH.acr().modify(|w| w.set_dcen(true))
         }
     }
 }
@@ -445,7 +443,7 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) {
         feature = "stm32f439vi",
         feature = "stm32f439zi",
     ))]
-    if second_bank_read && unsafe { pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() } {
+    if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() {
         panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11");
     }
 
@@ -479,11 +477,9 @@ fn pa12_is_output_pull_low() -> bool {
     use pac::gpio::vals;
     use pac::GPIOA;
     const PIN: usize = 12;
-    unsafe {
-        GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT
-            && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULLDOWN
-            && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW
-    }
+    GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT
+        && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULLDOWN
+        && GPIOA.odr().read().odr(PIN) == vals::Odr::LOW
 }
 
 #[cfg(test)]
diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs
index b9129cb51..a4f3b9686 100644
--- a/embassy-stm32/src/fmc.rs
+++ b/embassy-stm32/src/fmc.rs
@@ -16,7 +16,7 @@ unsafe impl<'d, T> stm32_fmc::FmcPeripheral for Fmc<'d, T>
 where
     T: Instance,
 {
-    const REGISTERS: *const () = T::REGS.0 as *const _;
+    const REGISTERS: *const () = T::REGS.as_ptr() as *const _;
 
     fn enable(&mut self) {
         <T as crate::rcc::sealed::RccPeripheral>::enable();
@@ -28,9 +28,7 @@ where
         // fsmc v1, v2 and v3 does not have the fmcen bit
         // This is a "not" because it is expected that all future versions have this bit
         #[cfg(not(any(fmc_v1x3, fmc_v2x1, fsmc_v1x0, fsmc_v1x3, fsmc_v2x3, fsmc_v3x1)))]
-        unsafe {
-            T::REGS.bcr1().modify(|r| r.set_fmcen(true))
-        };
+        T::REGS.bcr1().modify(|r| r.set_fmcen(true));
     }
 
     fn source_clock_hz(&self) -> u32 {
@@ -67,7 +65,7 @@ macro_rules! fmc_sdram_constructor {
             chip: CHIP
         ) -> stm32_fmc::Sdram<Fmc<'d, T>, CHIP> {
 
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             config_pins!(
                 $($addr_pin_name),*,
                 $($ba_pin_name),*,
diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs
index 7a066a4ca..af3a8eaca 100644
--- a/embassy-stm32/src/gpio.rs
+++ b/embassy-stm32/src/gpio.rs
@@ -46,7 +46,7 @@ impl<'d, T: Pin> Flex<'d, T> {
     /// Put the pin into input mode.
     #[inline]
     pub fn set_as_input(&mut self, pull: Pull) {
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             let r = self.pin.block();
             let n = self.pin.pin() as usize;
             #[cfg(gpio_v1)]
@@ -84,7 +84,7 @@ impl<'d, T: Pin> Flex<'d, T> {
     /// at a specific level, call `set_high`/`set_low` on the pin first.
     #[inline]
     pub fn set_as_output(&mut self, speed: Speed) {
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             let r = self.pin.block();
             let n = self.pin.pin() as usize;
             #[cfg(gpio_v1)]
@@ -116,7 +116,7 @@ impl<'d, T: Pin> Flex<'d, T> {
     /// at a specific level, call `set_high`/`set_low` on the pin first.
     #[inline]
     pub fn set_as_input_output(&mut self, speed: Speed, pull: Pull) {
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             let r = self.pin.block();
             let n = self.pin.pin() as usize;
             #[cfg(gpio_v1)]
@@ -147,7 +147,7 @@ impl<'d, T: Pin> Flex<'d, T> {
 
     #[inline]
     pub fn is_low(&self) -> bool {
-        let state = unsafe { self.pin.block().idr().read().idr(self.pin.pin() as _) };
+        let state = self.pin.block().idr().read().idr(self.pin.pin() as _);
         state == vals::Idr::LOW
     }
 
@@ -164,7 +164,7 @@ impl<'d, T: Pin> Flex<'d, T> {
     /// Is the output pin set as low?
     #[inline]
     pub fn is_set_low(&self) -> bool {
-        let state = unsafe { self.pin.block().odr().read().odr(self.pin.pin() as _) };
+        let state = self.pin.block().odr().read().odr(self.pin.pin() as _);
         state == vals::Odr::LOW
     }
 
@@ -207,7 +207,7 @@ impl<'d, T: Pin> Flex<'d, T> {
 impl<'d, T: Pin> Drop for Flex<'d, T> {
     #[inline]
     fn drop(&mut self) {
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             let r = self.pin.block();
             let n = self.pin.pin() as usize;
             #[cfg(gpio_v1)]
@@ -534,29 +534,25 @@ pub(crate) mod sealed {
         /// Set the output as high.
         #[inline]
         fn set_high(&self) {
-            unsafe {
-                let n = self._pin() as _;
-                self.block().bsrr().write(|w| w.set_bs(n, true));
-            }
+            let n = self._pin() as _;
+            self.block().bsrr().write(|w| w.set_bs(n, true));
         }
 
         /// Set the output as low.
         #[inline]
         fn set_low(&self) {
-            unsafe {
-                let n = self._pin() as _;
-                self.block().bsrr().write(|w| w.set_br(n, true));
-            }
+            let n = self._pin() as _;
+            self.block().bsrr().write(|w| w.set_br(n, true));
         }
 
         #[inline]
-        unsafe fn set_as_af(&self, af_num: u8, af_type: AFType) {
+        fn set_as_af(&self, af_num: u8, af_type: AFType) {
             self.set_as_af_pull(af_num, af_type, Pull::None);
         }
 
         #[cfg(gpio_v1)]
         #[inline]
-        unsafe fn set_as_af_pull(&self, _af_num: u8, af_type: AFType, pull: Pull) {
+        fn set_as_af_pull(&self, _af_num: u8, af_type: AFType, pull: Pull) {
             // F1 uses the AFIO register for remapping.
             // For now, this is not implemented, so af_num is ignored
             // _af_num should be zero here, since it is not set by stm32-data
@@ -599,7 +595,7 @@ pub(crate) mod sealed {
 
         #[cfg(gpio_v2)]
         #[inline]
-        unsafe fn set_as_af_pull(&self, af_num: u8, af_type: AFType, pull: Pull) {
+        fn set_as_af_pull(&self, af_num: u8, af_type: AFType, pull: Pull) {
             let pin = self._pin() as usize;
             let block = self.block();
             block.afr(pin / 8).modify(|w| w.set_afr(pin % 8, af_num));
@@ -614,7 +610,7 @@ pub(crate) mod sealed {
         }
 
         #[inline]
-        unsafe fn set_as_analog(&self) {
+        fn set_as_analog(&self) {
             let pin = self._pin() as usize;
             let block = self.block();
             #[cfg(gpio_v1)]
@@ -635,12 +631,12 @@ pub(crate) mod sealed {
         /// This is currently the same as set_as_analog but is semantically different really.
         /// Drivers should set_as_disconnected pins when dropped.
         #[inline]
-        unsafe fn set_as_disconnected(&self) {
+        fn set_as_disconnected(&self) {
             self.set_as_analog();
         }
 
         #[inline]
-        unsafe fn set_speed(&self, speed: Speed) {
+        fn set_speed(&self, speed: Speed) {
             let pin = self._pin() as usize;
 
             #[cfg(gpio_v1)]
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs
index e04038886..aa485cd86 100644
--- a/embassy-stm32/src/i2c/v1.rs
+++ b/embassy-stm32/src/i2c/v1.rs
@@ -68,53 +68,45 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         T::enable();
         T::reset();
 
-        unsafe {
-            scl.set_as_af_pull(
-                scl.af_num(),
-                AFType::OutputOpenDrain,
-                match config.scl_pullup {
-                    true => Pull::Up,
-                    false => Pull::None,
-                },
-            );
-            sda.set_as_af_pull(
-                sda.af_num(),
-                AFType::OutputOpenDrain,
-                match config.sda_pullup {
-                    true => Pull::Up,
-                    false => Pull::None,
-                },
-            );
-        }
+        scl.set_as_af_pull(
+            scl.af_num(),
+            AFType::OutputOpenDrain,
+            match config.scl_pullup {
+                true => Pull::Up,
+                false => Pull::None,
+            },
+        );
+        sda.set_as_af_pull(
+            sda.af_num(),
+            AFType::OutputOpenDrain,
+            match config.sda_pullup {
+                true => Pull::Up,
+                false => Pull::None,
+            },
+        );
 
-        unsafe {
-            T::regs().cr1().modify(|reg| {
-                reg.set_pe(false);
-                //reg.set_anfoff(false);
-            });
-        }
+        T::regs().cr1().modify(|reg| {
+            reg.set_pe(false);
+            //reg.set_anfoff(false);
+        });
 
         let timings = Timings::new(T::frequency(), freq.into());
 
-        unsafe {
-            T::regs().cr2().modify(|reg| {
-                reg.set_freq(timings.freq);
-            });
-            T::regs().ccr().modify(|reg| {
-                reg.set_f_s(timings.mode.f_s());
-                reg.set_duty(timings.duty.duty());
-                reg.set_ccr(timings.ccr);
-            });
-            T::regs().trise().modify(|reg| {
-                reg.set_trise(timings.trise);
-            });
-        }
+        T::regs().cr2().modify(|reg| {
+            reg.set_freq(timings.freq);
+        });
+        T::regs().ccr().modify(|reg| {
+            reg.set_f_s(timings.mode.f_s());
+            reg.set_duty(timings.duty.duty());
+            reg.set_ccr(timings.ccr);
+        });
+        T::regs().trise().modify(|reg| {
+            reg.set_trise(timings.trise);
+        });
 
-        unsafe {
-            T::regs().cr1().modify(|reg| {
-                reg.set_pe(true);
-            });
-        }
+        T::regs().cr1().modify(|reg| {
+            reg.set_pe(true);
+        });
 
         Self {
             phantom: PhantomData,
@@ -123,7 +115,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         }
     }
 
-    unsafe fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
+    fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
         // Note that flags should only be cleared once they have been registered. If flags are
         // cleared otherwise, there may be an inherent race condition and flags may be missed.
         let sr1 = T::regs().sr1().read();
@@ -162,7 +154,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         Ok(sr1)
     }
 
-    unsafe fn write_bytes(
+    fn write_bytes(
         &mut self,
         addr: u8,
         bytes: &[u8],
@@ -211,7 +203,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         Ok(())
     }
 
-    unsafe fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
+    fn send_byte(&self, byte: u8, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
         // Wait until we're ready for sending
         while {
             // Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
@@ -234,7 +226,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         Ok(())
     }
 
-    unsafe fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
+    fn recv_byte(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<u8, Error> {
         while {
             // Check for any potential error conditions.
             self.check_and_clear_error_flags()?;
@@ -256,56 +248,52 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
     ) -> Result<(), Error> {
         if let Some((last, buffer)) = buffer.split_last_mut() {
             // Send a START condition and set ACK bit
-            unsafe {
-                T::regs().cr1().modify(|reg| {
-                    reg.set_start(true);
-                    reg.set_ack(true);
-                });
-            }
+            T::regs().cr1().modify(|reg| {
+                reg.set_start(true);
+                reg.set_ack(true);
+            });
 
             // Wait until START condition was generated
-            while unsafe { !self.check_and_clear_error_flags()?.start() } {
+            while !self.check_and_clear_error_flags()?.start() {
                 check_timeout()?;
             }
 
             // Also wait until signalled we're master and everything is waiting for us
             while {
-                let sr2 = unsafe { T::regs().sr2().read() };
+                let sr2 = T::regs().sr2().read();
                 !sr2.msl() && !sr2.busy()
             } {
                 check_timeout()?;
             }
 
             // Set up current address, we're trying to talk to
-            unsafe { T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1)) }
+            T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1));
 
             // Wait until address was sent
             // Wait for the address to be acknowledged
-            while unsafe { !self.check_and_clear_error_flags()?.addr() } {
+            while !self.check_and_clear_error_flags()?.addr() {
                 check_timeout()?;
             }
 
             // Clear condition by reading SR2
-            let _ = unsafe { T::regs().sr2().read() };
+            let _ = T::regs().sr2().read();
 
             // Receive bytes into buffer
             for c in buffer {
-                *c = unsafe { self.recv_byte(&check_timeout)? };
+                *c = self.recv_byte(&check_timeout)?;
             }
 
             // Prepare to send NACK then STOP after next byte
-            unsafe {
-                T::regs().cr1().modify(|reg| {
-                    reg.set_ack(false);
-                    reg.set_stop(true);
-                })
-            }
+            T::regs().cr1().modify(|reg| {
+                reg.set_ack(false);
+                reg.set_stop(true);
+            });
 
             // Receive last byte
-            *last = unsafe { self.recv_byte(&check_timeout)? };
+            *last = self.recv_byte(&check_timeout)?;
 
             // Wait for the STOP to be sent.
-            while unsafe { T::regs().cr1().read().stop() } {
+            while T::regs().cr1().read().stop() {
                 check_timeout()?;
             }
 
@@ -326,15 +314,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         write: &[u8],
         check_timeout: impl Fn() -> Result<(), Error>,
     ) -> Result<(), Error> {
-        unsafe {
-            self.write_bytes(addr, write, &check_timeout)?;
-            // Send a STOP condition
-            T::regs().cr1().modify(|reg| reg.set_stop(true));
-            // Wait for STOP condition to transmit.
-            while T::regs().cr1().read().stop() {
-                check_timeout()?;
-            }
-        };
+        self.write_bytes(addr, write, &check_timeout)?;
+        // Send a STOP condition
+        T::regs().cr1().modify(|reg| reg.set_stop(true));
+        // Wait for STOP condition to transmit.
+        while T::regs().cr1().read().stop() {
+            check_timeout()?;
+        }
 
         // Fallthrough is success
         Ok(())
@@ -351,7 +337,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         read: &mut [u8],
         check_timeout: impl Fn() -> Result<(), Error>,
     ) -> Result<(), Error> {
-        unsafe { self.write_bytes(addr, write, &check_timeout)? };
+        self.write_bytes(addr, write, &check_timeout)?;
         self.blocking_read_timeout(addr, read, &check_timeout)?;
 
         Ok(())
@@ -478,8 +464,6 @@ impl Timings {
         assert!(freq >= 2 && freq <= 50);
 
         // Configure bus frequency into I2C peripheral
-        //self.i2c.cr2.write(|w| unsafe { w.freq().bits(freq as u8) });
-
         let trise = if speed <= 100_000 {
             freq + 1
         } else {
@@ -539,18 +523,16 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> {
     type Config = Hertz;
     fn set_config(&mut self, config: &Self::Config) {
         let timings = Timings::new(T::frequency(), *config);
-        unsafe {
-            T::regs().cr2().modify(|reg| {
-                reg.set_freq(timings.freq);
-            });
-            T::regs().ccr().modify(|reg| {
-                reg.set_f_s(timings.mode.f_s());
-                reg.set_duty(timings.duty.duty());
-                reg.set_ccr(timings.ccr);
-            });
-            T::regs().trise().modify(|reg| {
-                reg.set_trise(timings.trise);
-            });
-        }
+        T::regs().cr2().modify(|reg| {
+            reg.set_freq(timings.freq);
+        });
+        T::regs().ccr().modify(|reg| {
+            reg.set_f_s(timings.mode.f_s());
+            reg.set_duty(timings.duty.duty());
+            reg.set_ccr(timings.ccr);
+        });
+        T::regs().trise().modify(|reg| {
+            reg.set_trise(timings.trise);
+        });
     }
 }
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 1aaf2b46b..1f036d55c 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -89,49 +89,41 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         T::enable();
         T::reset();
 
-        unsafe {
-            scl.set_as_af_pull(
-                scl.af_num(),
-                AFType::OutputOpenDrain,
-                match config.scl_pullup {
-                    true => Pull::Up,
-                    false => Pull::None,
-                },
-            );
-            sda.set_as_af_pull(
-                sda.af_num(),
-                AFType::OutputOpenDrain,
-                match config.sda_pullup {
-                    true => Pull::Up,
-                    false => Pull::None,
-                },
-            );
-        }
+        scl.set_as_af_pull(
+            scl.af_num(),
+            AFType::OutputOpenDrain,
+            match config.scl_pullup {
+                true => Pull::Up,
+                false => Pull::None,
+            },
+        );
+        sda.set_as_af_pull(
+            sda.af_num(),
+            AFType::OutputOpenDrain,
+            match config.sda_pullup {
+                true => Pull::Up,
+                false => Pull::None,
+            },
+        );
 
-        unsafe {
-            T::regs().cr1().modify(|reg| {
-                reg.set_pe(false);
-                reg.set_anfoff(false);
-            });
-        }
+        T::regs().cr1().modify(|reg| {
+            reg.set_pe(false);
+            reg.set_anfoff(false);
+        });
 
         let timings = Timings::new(T::frequency(), freq.into());
 
-        unsafe {
-            T::regs().timingr().write(|reg| {
-                reg.set_presc(timings.prescale);
-                reg.set_scll(timings.scll);
-                reg.set_sclh(timings.sclh);
-                reg.set_sdadel(timings.sdadel);
-                reg.set_scldel(timings.scldel);
-            });
-        }
+        T::regs().timingr().write(|reg| {
+            reg.set_presc(timings.prescale);
+            reg.set_scll(timings.scll);
+            reg.set_sclh(timings.sclh);
+            reg.set_sdadel(timings.sdadel);
+            reg.set_scldel(timings.scldel);
+        });
 
-        unsafe {
-            T::regs().cr1().modify(|reg| {
-                reg.set_pe(true);
-            });
-        }
+        T::regs().cr1().modify(|reg| {
+            reg.set_pe(true);
+        });
 
         T::Interrupt::unpend();
         unsafe { T::Interrupt::enable() };
@@ -144,12 +136,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
     }
 
     fn master_stop(&mut self) {
-        unsafe {
-            T::regs().cr2().write(|w| w.set_stop(true));
-        }
+        T::regs().cr2().write(|w| w.set_stop(true));
     }
 
-    unsafe fn master_read(
+    fn master_read(
         address: u8,
         length: usize,
         stop: Stop,
@@ -191,7 +181,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         Ok(())
     }
 
-    unsafe fn master_write(
+    fn master_write(
         address: u8,
         length: usize,
         stop: Stop,
@@ -229,7 +219,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         Ok(())
     }
 
-    unsafe fn master_continue(
+    fn master_continue(
         length: usize,
         reload: bool,
         check_timeout: impl Fn() -> Result<(), Error>,
@@ -259,13 +249,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         //$i2c.txdr.write(|w| w.txdata().bits(0));
         //}
 
-        unsafe {
-            if T::regs().isr().read().txis() {
-                T::regs().txdr().write(|w| w.set_txdata(0));
-            }
-            if !T::regs().isr().read().txe() {
-                T::regs().isr().modify(|w| w.set_txe(true))
-            }
+        if T::regs().isr().read().txis() {
+            T::regs().txdr().write(|w| w.set_txdata(0));
+        }
+        if !T::regs().isr().read().txe() {
+            T::regs().isr().modify(|w| w.set_txe(true))
         }
 
         // If TXDR is not flagged as empty, write 1 to flush it
@@ -276,21 +264,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 
     fn wait_txe(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
         loop {
-            unsafe {
-                let isr = T::regs().isr().read();
-                if isr.txe() {
-                    return Ok(());
-                } else if isr.berr() {
-                    T::regs().icr().write(|reg| reg.set_berrcf(true));
-                    return Err(Error::Bus);
-                } else if isr.arlo() {
-                    T::regs().icr().write(|reg| reg.set_arlocf(true));
-                    return Err(Error::Arbitration);
-                } else if isr.nackf() {
-                    T::regs().icr().write(|reg| reg.set_nackcf(true));
-                    self.flush_txdr();
-                    return Err(Error::Nack);
-                }
+            let isr = T::regs().isr().read();
+            if isr.txe() {
+                return Ok(());
+            } else if isr.berr() {
+                T::regs().icr().write(|reg| reg.set_berrcf(true));
+                return Err(Error::Bus);
+            } else if isr.arlo() {
+                T::regs().icr().write(|reg| reg.set_arlocf(true));
+                return Err(Error::Arbitration);
+            } else if isr.nackf() {
+                T::regs().icr().write(|reg| reg.set_nackcf(true));
+                self.flush_txdr();
+                return Err(Error::Nack);
             }
 
             check_timeout()?;
@@ -299,21 +285,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 
     fn wait_rxne(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
         loop {
-            unsafe {
-                let isr = T::regs().isr().read();
-                if isr.rxne() {
-                    return Ok(());
-                } else if isr.berr() {
-                    T::regs().icr().write(|reg| reg.set_berrcf(true));
-                    return Err(Error::Bus);
-                } else if isr.arlo() {
-                    T::regs().icr().write(|reg| reg.set_arlocf(true));
-                    return Err(Error::Arbitration);
-                } else if isr.nackf() {
-                    T::regs().icr().write(|reg| reg.set_nackcf(true));
-                    self.flush_txdr();
-                    return Err(Error::Nack);
-                }
+            let isr = T::regs().isr().read();
+            if isr.rxne() {
+                return Ok(());
+            } else if isr.berr() {
+                T::regs().icr().write(|reg| reg.set_berrcf(true));
+                return Err(Error::Bus);
+            } else if isr.arlo() {
+                T::regs().icr().write(|reg| reg.set_arlocf(true));
+                return Err(Error::Arbitration);
+            } else if isr.nackf() {
+                T::regs().icr().write(|reg| reg.set_nackcf(true));
+                self.flush_txdr();
+                return Err(Error::Nack);
             }
 
             check_timeout()?;
@@ -322,21 +306,19 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 
     fn wait_tc(&self, check_timeout: impl Fn() -> Result<(), Error>) -> Result<(), Error> {
         loop {
-            unsafe {
-                let isr = T::regs().isr().read();
-                if isr.tc() {
-                    return Ok(());
-                } else if isr.berr() {
-                    T::regs().icr().write(|reg| reg.set_berrcf(true));
-                    return Err(Error::Bus);
-                } else if isr.arlo() {
-                    T::regs().icr().write(|reg| reg.set_arlocf(true));
-                    return Err(Error::Arbitration);
-                } else if isr.nackf() {
-                    T::regs().icr().write(|reg| reg.set_nackcf(true));
-                    self.flush_txdr();
-                    return Err(Error::Nack);
-                }
+            let isr = T::regs().isr().read();
+            if isr.tc() {
+                return Ok(());
+            } else if isr.berr() {
+                T::regs().icr().write(|reg| reg.set_berrcf(true));
+                return Err(Error::Bus);
+            } else if isr.arlo() {
+                T::regs().icr().write(|reg| reg.set_arlocf(true));
+                return Err(Error::Arbitration);
+            } else if isr.nackf() {
+                T::regs().icr().write(|reg| reg.set_nackcf(true));
+                self.flush_txdr();
+                return Err(Error::Nack);
             }
 
             check_timeout()?;
@@ -358,32 +340,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         };
         let last_chunk_idx = total_chunks.saturating_sub(1);
 
-        unsafe {
-            Self::master_read(
-                address,
-                read.len().min(255),
-                Stop::Automatic,
-                last_chunk_idx != 0,
-                restart,
-                &check_timeout,
-            )?;
-        }
+        Self::master_read(
+            address,
+            read.len().min(255),
+            Stop::Automatic,
+            last_chunk_idx != 0,
+            restart,
+            &check_timeout,
+        )?;
 
         for (number, chunk) in read.chunks_mut(255).enumerate() {
             if number != 0 {
-                // NOTE(unsafe) We have &mut self
-                unsafe {
-                    Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
-                }
+                Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
             }
 
             for byte in chunk {
                 // Wait until we have received something
                 self.wait_rxne(&check_timeout)?;
 
-                unsafe {
-                    *byte = T::regs().rxdr().read().rxdata();
-                }
+                *byte = T::regs().rxdr().read().rxdata();
             }
         }
         Ok(())
@@ -407,23 +382,17 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         // I2C start
         //
         // ST SAD+W
-        // NOTE(unsafe) We have &mut self
-        unsafe {
-            Self::master_write(
-                address,
-                write.len().min(255),
-                Stop::Software,
-                last_chunk_idx != 0,
-                &check_timeout,
-            )?;
-        }
+        Self::master_write(
+            address,
+            write.len().min(255),
+            Stop::Software,
+            last_chunk_idx != 0,
+            &check_timeout,
+        )?;
 
         for (number, chunk) in write.chunks(255).enumerate() {
             if number != 0 {
-                // NOTE(unsafe) We have &mut self
-                unsafe {
-                    Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
-                }
+                Self::master_continue(chunk.len(), number != last_chunk_idx, &check_timeout)?;
             }
 
             for byte in chunk {
@@ -432,9 +401,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
                 // through)
                 self.wait_txe(&check_timeout)?;
 
-                unsafe {
-                    T::regs().txdr().write(|w| w.set_txdata(*byte));
-                }
+                T::regs().txdr().write(|w| w.set_txdata(*byte));
             }
         }
         // Wait until the write finishes
@@ -467,7 +434,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
                     w.set_tcie(true);
                 }
             });
-            let dst = regs.txdr().ptr() as *mut u8;
+            let dst = regs.txdr().as_ptr() as *mut u8;
 
             let ch = &mut self.tx_dma;
             let request = ch.request();
@@ -479,37 +446,30 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 
         let on_drop = OnDrop::new(|| {
             let regs = T::regs();
-            unsafe {
-                regs.cr1().modify(|w| {
-                    if last_slice {
-                        w.set_txdmaen(false);
-                    }
-                    w.set_tcie(false);
-                })
-            }
+            regs.cr1().modify(|w| {
+                if last_slice {
+                    w.set_txdmaen(false);
+                }
+                w.set_tcie(false);
+            })
         });
 
         poll_fn(|cx| {
             state.waker.register(cx.waker());
 
-            let isr = unsafe { T::regs().isr().read() };
+            let isr = T::regs().isr().read();
             if remaining_len == total_len {
-                // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
                 if first_slice {
-                    unsafe {
-                        Self::master_write(
-                            address,
-                            total_len.min(255),
-                            Stop::Software,
-                            (total_len > 255) || !last_slice,
-                            &check_timeout,
-                        )?;
-                    }
+                    Self::master_write(
+                        address,
+                        total_len.min(255),
+                        Stop::Software,
+                        (total_len > 255) || !last_slice,
+                        &check_timeout,
+                    )?;
                 } else {
-                    unsafe {
-                        Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?;
-                        T::regs().cr1().modify(|w| w.set_tcie(true));
-                    }
+                    Self::master_continue(total_len.min(255), (total_len > 255) || !last_slice, &check_timeout)?;
+                    T::regs().cr1().modify(|w| w.set_tcie(true));
                 }
             } else if !(isr.tcr() || isr.tc()) {
                 // poll_fn was woken without an interrupt present
@@ -519,13 +479,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
             } else {
                 let last_piece = (remaining_len <= 255) && last_slice;
 
-                // NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
-                unsafe {
-                    if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
-                        return Poll::Ready(Err(e));
-                    }
-                    T::regs().cr1().modify(|w| w.set_tcie(true));
+                if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
+                    return Poll::Ready(Err(e));
                 }
+                T::regs().cr1().modify(|w| w.set_tcie(true));
             }
 
             remaining_len = remaining_len.saturating_sub(255);
@@ -564,7 +521,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
                 w.set_rxdmaen(true);
                 w.set_tcie(true);
             });
-            let src = regs.rxdr().ptr() as *mut u8;
+            let src = regs.rxdr().as_ptr() as *mut u8;
 
             let ch = &mut self.rx_dma;
             let request = ch.request();
@@ -576,30 +533,25 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 
         let on_drop = OnDrop::new(|| {
             let regs = T::regs();
-            unsafe {
-                regs.cr1().modify(|w| {
-                    w.set_rxdmaen(false);
-                    w.set_tcie(false);
-                })
-            }
+            regs.cr1().modify(|w| {
+                w.set_rxdmaen(false);
+                w.set_tcie(false);
+            })
         });
 
         poll_fn(|cx| {
             state.waker.register(cx.waker());
 
-            let isr = unsafe { T::regs().isr().read() };
+            let isr = T::regs().isr().read();
             if remaining_len == total_len {
-                // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
-                unsafe {
-                    Self::master_read(
-                        address,
-                        total_len.min(255),
-                        Stop::Software,
-                        total_len > 255,
-                        restart,
-                        &check_timeout,
-                    )?;
-                }
+                Self::master_read(
+                    address,
+                    total_len.min(255),
+                    Stop::Software,
+                    total_len > 255,
+                    restart,
+                    &check_timeout,
+                )?;
             } else if !(isr.tcr() || isr.tc()) {
                 // poll_fn was woken without an interrupt present
                 return Poll::Pending;
@@ -608,13 +560,10 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
             } else {
                 let last_piece = remaining_len <= 255;
 
-                // NOTE(unsafe) self.rx_dma does not fiddle with the i2c registers
-                unsafe {
-                    if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
-                        return Poll::Ready(Err(e));
-                    }
-                    T::regs().cr1().modify(|w| w.set_tcie(true));
+                if let Err(e) = Self::master_continue(remaining_len.min(255), !last_piece, &check_timeout) {
+                    return Poll::Ready(Err(e));
                 }
+                T::regs().cr1().modify(|w| w.set_tcie(true));
             }
 
             remaining_len = remaining_len.saturating_sub(255);
@@ -758,16 +707,13 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
         let first_length = write[0].len();
         let last_slice_index = write.len() - 1;
 
-        // NOTE(unsafe) We have &mut self
-        unsafe {
-            Self::master_write(
-                address,
-                first_length.min(255),
-                Stop::Software,
-                (first_length > 255) || (last_slice_index != 0),
-                &check_timeout,
-            )?;
-        }
+        Self::master_write(
+            address,
+            first_length.min(255),
+            Stop::Software,
+            (first_length > 255) || (last_slice_index != 0),
+            &check_timeout,
+        )?;
 
         for (idx, slice) in write.iter().enumerate() {
             let slice_len = slice.len();
@@ -780,26 +726,20 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
             let last_chunk_idx = total_chunks.saturating_sub(1);
 
             if idx != 0 {
-                // NOTE(unsafe) We have &mut self
-                unsafe {
-                    Self::master_continue(
-                        slice_len.min(255),
-                        (idx != last_slice_index) || (slice_len > 255),
-                        &check_timeout,
-                    )?;
-                }
+                Self::master_continue(
+                    slice_len.min(255),
+                    (idx != last_slice_index) || (slice_len > 255),
+                    &check_timeout,
+                )?;
             }
 
             for (number, chunk) in slice.chunks(255).enumerate() {
                 if number != 0 {
-                    // NOTE(unsafe) We have &mut self
-                    unsafe {
-                        Self::master_continue(
-                            chunk.len(),
-                            (number != last_chunk_idx) || (idx != last_slice_index),
-                            &check_timeout,
-                        )?;
-                    }
+                    Self::master_continue(
+                        chunk.len(),
+                        (number != last_chunk_idx) || (idx != last_slice_index),
+                        &check_timeout,
+                    )?;
                 }
 
                 for byte in chunk {
@@ -810,9 +750,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
 
                     // Put byte on the wire
                     //self.i2c.txdr.write(|w| w.txdata().bits(*byte));
-                    unsafe {
-                        T::regs().txdr().write(|w| w.set_txdata(*byte));
-                    }
+                    T::regs().txdr().write(|w| w.set_txdata(*byte));
                 }
             }
         }
@@ -1061,14 +999,12 @@ impl<'d, T: Instance> SetConfig for I2c<'d, T> {
     type Config = Hertz;
     fn set_config(&mut self, config: &Self::Config) {
         let timings = Timings::new(T::frequency(), *config);
-        unsafe {
-            T::regs().timingr().write(|reg| {
-                reg.set_presc(timings.prescale);
-                reg.set_scll(timings.scll);
-                reg.set_sclh(timings.sclh);
-                reg.set_sdadel(timings.sdadel);
-                reg.set_scldel(timings.scldel);
-            });
-        }
+        T::regs().timingr().write(|reg| {
+            reg.set_presc(timings.prescale);
+            reg.set_scll(timings.scll);
+            reg.set_sclh(timings.sclh);
+            reg.set_sdadel(timings.sdadel);
+            reg.set_scldel(timings.scldel);
+        });
     }
 }
diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs
index 2bb199f68..62dda69b4 100644
--- a/embassy-stm32/src/i2s.rs
+++ b/embassy-stm32/src/i2s.rs
@@ -153,19 +153,17 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
     ) -> Self {
         into_ref!(sd, ws, ck, mck);
 
-        unsafe {
-            sd.set_as_af(sd.af_num(), AFType::OutputPushPull);
-            sd.set_speed(crate::gpio::Speed::VeryHigh);
+        sd.set_as_af(sd.af_num(), AFType::OutputPushPull);
+        sd.set_speed(crate::gpio::Speed::VeryHigh);
 
-            ws.set_as_af(ws.af_num(), AFType::OutputPushPull);
-            ws.set_speed(crate::gpio::Speed::VeryHigh);
+        ws.set_as_af(ws.af_num(), AFType::OutputPushPull);
+        ws.set_speed(crate::gpio::Speed::VeryHigh);
 
-            ck.set_as_af(ck.af_num(), AFType::OutputPushPull);
-            ck.set_speed(crate::gpio::Speed::VeryHigh);
+        ck.set_as_af(ck.af_num(), AFType::OutputPushPull);
+        ck.set_speed(crate::gpio::Speed::VeryHigh);
 
-            mck.set_as_af(mck.af_num(), AFType::OutputPushPull);
-            mck.set_speed(crate::gpio::Speed::VeryHigh);
-        }
+        mck.set_as_af(mck.af_num(), AFType::OutputPushPull);
+        mck.set_speed(crate::gpio::Speed::VeryHigh);
 
         let spi = Spi::new_internal(peri, txdma, rxdma, freq, SpiConfig::default());
 
@@ -178,7 +176,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
         let (odd, div) = compute_baud_rate(pclk, freq, config.master_clock, config.format);
 
         #[cfg(any(spi_v1, spi_f1))]
-        unsafe {
+        {
             use stm32_metapac::spi::vals::{I2scfg, Odd};
 
             // 1. Select the I2SDIV[7:0] bits in the SPI_I2SPR register to define the serial clock baud
@@ -232,10 +230,6 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
                 w.set_i2se(true)
             });
         }
-        #[cfg(spi_v2)]
-        unsafe {}
-        #[cfg(any(spi_v3, spi_v4))]
-        unsafe {}
 
         Self {
             _peri: spi,
@@ -264,12 +258,10 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> {
 
 impl<'d, T: Instance, Tx, Rx> Drop for I2S<'d, T, Tx, Rx> {
     fn drop(&mut self) {
-        unsafe {
-            self.sd.as_ref().map(|x| x.set_as_disconnected());
-            self.ws.as_ref().map(|x| x.set_as_disconnected());
-            self.ck.as_ref().map(|x| x.set_as_disconnected());
-            self.mck.as_ref().map(|x| x.set_as_disconnected());
-        }
+        self.sd.as_ref().map(|x| x.set_as_disconnected());
+        self.ws.as_ref().map(|x| x.set_as_disconnected());
+        self.ck.as_ref().map(|x| x.set_as_disconnected());
+        self.mck.as_ref().map(|x| x.set_as_disconnected());
     }
 }
 
diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs
index 8bb0774b8..3062226c7 100644
--- a/embassy-stm32/src/ipcc.rs
+++ b/embassy-stm32/src/ipcc.rs
@@ -1,7 +1,77 @@
+use core::future::poll_fn;
+use core::task::Poll;
+
+use atomic_polyfill::{compiler_fence, Ordering};
+
 use self::sealed::Instance;
+use crate::interrupt;
+use crate::interrupt::typelevel::Interrupt;
 use crate::peripherals::IPCC;
 use crate::rcc::sealed::RccPeripheral;
 
+/// Interrupt handler.
+pub struct ReceiveInterruptHandler {}
+
+impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_RX> for ReceiveInterruptHandler {
+    unsafe fn on_interrupt() {
+        let regs = IPCC::regs();
+
+        let channels = [
+            IpccChannel::Channel1,
+            IpccChannel::Channel2,
+            IpccChannel::Channel3,
+            IpccChannel::Channel4,
+            IpccChannel::Channel5,
+            IpccChannel::Channel6,
+        ];
+
+        // Status register gives channel occupied status. For rx, use cpu1.
+        let sr = regs.cpu(1).sr().read();
+        regs.cpu(0).mr().modify(|w| {
+            for channel in channels {
+                if sr.chf(channel as usize) {
+                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
+                    w.set_chom(channel as usize, true);
+
+                    // There shouldn't be a race because the channel is masked only if the interrupt has fired
+                    IPCC::state().rx_waker_for(channel).wake();
+                }
+            }
+        })
+    }
+}
+
+pub struct TransmitInterruptHandler {}
+
+impl interrupt::typelevel::Handler<interrupt::typelevel::IPCC_C1_TX> for TransmitInterruptHandler {
+    unsafe fn on_interrupt() {
+        let regs = IPCC::regs();
+
+        let channels = [
+            IpccChannel::Channel1,
+            IpccChannel::Channel2,
+            IpccChannel::Channel3,
+            IpccChannel::Channel4,
+            IpccChannel::Channel5,
+            IpccChannel::Channel6,
+        ];
+
+        // Status register gives channel occupied status. For tx, use cpu0.
+        let sr = regs.cpu(0).sr().read();
+        regs.cpu(0).mr().modify(|w| {
+            for channel in channels {
+                if !sr.chf(channel as usize) {
+                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
+                    w.set_chfm(channel as usize, true);
+
+                    // There shouldn't be a race because the channel is masked only if the interrupt has fired
+                    IPCC::state().tx_waker_for(channel).wake();
+                }
+            }
+        });
+    }
+}
+
 #[non_exhaustive]
 #[derive(Clone, Copy, Default)]
 pub struct Config {
@@ -20,13 +90,6 @@ pub enum IpccChannel {
     Channel6 = 5,
 }
 
-pub mod sealed {
-    pub trait Instance: crate::rcc::RccPeripheral {
-        fn regs() -> crate::pac::ipcc::Ipcc;
-        fn set_cpu2(enabled: bool);
-    }
-}
-
 pub struct Ipcc;
 
 impl Ipcc {
@@ -35,124 +98,107 @@ impl Ipcc {
         IPCC::reset();
         IPCC::set_cpu2(true);
 
-        unsafe { _configure_pwr() };
+        _configure_pwr();
 
         let regs = IPCC::regs();
 
-        unsafe {
-            regs.cpu(0).cr().modify(|w| {
-                w.set_rxoie(true);
-                w.set_txfie(true);
-            })
+        regs.cpu(0).cr().modify(|w| {
+            w.set_rxoie(true);
+            w.set_txfie(true);
+        });
+
+        // enable interrupts
+        crate::interrupt::typelevel::IPCC_C1_RX::unpend();
+        crate::interrupt::typelevel::IPCC_C1_TX::unpend();
+
+        unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() };
+        unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() };
+    }
+
+    /// Send data to an IPCC channel. The closure is called to write the data when appropriate.
+    pub async fn send(channel: IpccChannel, f: impl FnOnce()) {
+        let regs = IPCC::regs();
+
+        Self::flush(channel).await;
+        compiler_fence(Ordering::SeqCst);
+
+        f();
+
+        compiler_fence(Ordering::SeqCst);
+
+        trace!("ipcc: ch {}: send data", channel as u8);
+        regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true));
+    }
+
+    /// Wait for the tx channel to become clear
+    pub async fn flush(channel: IpccChannel) {
+        let regs = IPCC::regs();
+
+        // This is a race, but is nice for debugging
+        if regs.cpu(0).sr().read().chf(channel as usize) {
+            trace!("ipcc: ch {}:  wait for tx free", channel as u8);
         }
+
+        poll_fn(|cx| {
+            IPCC::state().tx_waker_for(channel).register(cx.waker());
+            // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt
+            regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false));
+
+            compiler_fence(Ordering::SeqCst);
+
+            if !regs.cpu(0).sr().read().chf(channel as usize) {
+                // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
+                regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true));
+
+                Poll::Ready(())
+            } else {
+                Poll::Pending
+            }
+        })
+        .await;
     }
 
-    pub fn c1_set_rx_channel(channel: IpccChannel, enabled: bool) {
+    /// Receive data from an IPCC channel. The closure is called to read the data when appropriate.
+    pub async fn receive<R>(channel: IpccChannel, mut f: impl FnMut() -> Option<R>) -> R {
         let regs = IPCC::regs();
 
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, !enabled)) }
-    }
+        loop {
+            // This is a race, but is nice for debugging
+            if !regs.cpu(1).sr().read().chf(channel as usize) {
+                trace!("ipcc: ch {}:  wait for rx occupied", channel as u8);
+            }
 
-    pub fn c1_get_rx_channel(channel: IpccChannel) -> bool {
-        let regs = IPCC::regs();
+            poll_fn(|cx| {
+                IPCC::state().rx_waker_for(channel).register(cx.waker());
+                // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt
+                regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false));
 
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { !regs.cpu(0).mr().read().chom(channel as usize) }
-    }
+                compiler_fence(Ordering::SeqCst);
 
-    #[allow(dead_code)]
-    pub fn c2_set_rx_channel(channel: IpccChannel, enabled: bool) {
-        let regs = IPCC::regs();
+                if regs.cpu(1).sr().read().chf(channel as usize) {
+                    // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt
+                    regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true));
 
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { regs.cpu(1).mr().modify(|w| w.set_chom(channel as usize, !enabled)) }
-    }
+                    Poll::Ready(())
+                } else {
+                    Poll::Pending
+                }
+            })
+            .await;
 
-    #[allow(dead_code)]
-    pub fn c2_get_rx_channel(channel: IpccChannel) -> bool {
-        let regs = IPCC::regs();
+            trace!("ipcc: ch {}:  read data", channel as u8);
+            compiler_fence(Ordering::SeqCst);
 
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { !regs.cpu(1).mr().read().chom(channel as usize) }
-    }
+            match f() {
+                Some(ret) => return ret,
+                None => {}
+            }
 
-    pub fn c1_set_tx_channel(channel: IpccChannel, enabled: bool) {
-        let regs = IPCC::regs();
-
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) }
-    }
-
-    pub fn c1_get_tx_channel(channel: IpccChannel) -> bool {
-        let regs = IPCC::regs();
-
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { !regs.cpu(0).mr().read().chfm(channel as usize) }
-    }
-
-    #[allow(dead_code)]
-    pub fn c2_set_tx_channel(channel: IpccChannel, enabled: bool) {
-        let regs = IPCC::regs();
-
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { regs.cpu(1).mr().modify(|w| w.set_chfm(channel as usize, !enabled)) }
-    }
-
-    #[allow(dead_code)]
-    pub fn c2_get_tx_channel(channel: IpccChannel) -> bool {
-        let regs = IPCC::regs();
-
-        // If bit is set to 1 then interrupt is disabled
-        unsafe { !regs.cpu(1).mr().read().chfm(channel as usize) }
-    }
-
-    /// clears IPCC receive channel status for CPU1
-    pub fn c1_clear_flag_channel(channel: IpccChannel) {
-        let regs = IPCC::regs();
-
-        unsafe { regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)) }
-    }
-
-    #[allow(dead_code)]
-    /// clears IPCC receive channel status for CPU2
-    pub fn c2_clear_flag_channel(channel: IpccChannel) {
-        let regs = IPCC::regs();
-
-        unsafe { regs.cpu(1).scr().write(|w| w.set_chc(channel as usize, true)) }
-    }
-
-    pub fn c1_set_flag_channel(channel: IpccChannel) {
-        let regs = IPCC::regs();
-
-        unsafe { regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)) }
-    }
-
-    #[allow(dead_code)]
-    pub fn c2_set_flag_channel(channel: IpccChannel) {
-        let regs = IPCC::regs();
-
-        unsafe { regs.cpu(1).scr().write(|w| w.set_chs(channel as usize, true)) }
-    }
-
-    pub fn c1_is_active_flag(channel: IpccChannel) -> bool {
-        let regs = IPCC::regs();
-
-        unsafe { regs.cpu(0).sr().read().chf(channel as usize) }
-    }
-
-    pub fn c2_is_active_flag(channel: IpccChannel) -> bool {
-        let regs = IPCC::regs();
-
-        unsafe { regs.cpu(1).sr().read().chf(channel as usize) }
-    }
-
-    pub fn is_tx_pending(channel: IpccChannel) -> bool {
-        !Self::c1_is_active_flag(channel) && Self::c1_get_tx_channel(channel)
-    }
-
-    pub fn is_rx_pending(channel: IpccChannel) -> bool {
-        Self::c2_is_active_flag(channel) && Self::c1_get_rx_channel(channel)
+            trace!("ipcc: ch {}: clear rx", channel as u8);
+            compiler_fence(Ordering::SeqCst);
+            // If the channel is clear and the read function returns none, fetch more data
+            regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true));
+        }
     }
 }
 
@@ -162,11 +208,68 @@ impl sealed::Instance for crate::peripherals::IPCC {
     }
 
     fn set_cpu2(enabled: bool) {
-        unsafe { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)) }
+        crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled));
+    }
+
+    fn state() -> &'static self::sealed::State {
+        static STATE: self::sealed::State = self::sealed::State::new();
+        &STATE
     }
 }
 
-unsafe fn _configure_pwr() {
+pub(crate) mod sealed {
+    use embassy_sync::waitqueue::AtomicWaker;
+
+    use super::*;
+
+    pub struct State {
+        rx_wakers: [AtomicWaker; 6],
+        tx_wakers: [AtomicWaker; 6],
+    }
+
+    impl State {
+        pub const fn new() -> Self {
+            const WAKER: AtomicWaker = AtomicWaker::new();
+
+            Self {
+                rx_wakers: [WAKER; 6],
+                tx_wakers: [WAKER; 6],
+            }
+        }
+
+        pub fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker {
+            match channel {
+                IpccChannel::Channel1 => &self.rx_wakers[0],
+                IpccChannel::Channel2 => &self.rx_wakers[1],
+                IpccChannel::Channel3 => &self.rx_wakers[2],
+                IpccChannel::Channel4 => &self.rx_wakers[3],
+                IpccChannel::Channel5 => &self.rx_wakers[4],
+                IpccChannel::Channel6 => &self.rx_wakers[5],
+            }
+        }
+
+        pub fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker {
+            match channel {
+                IpccChannel::Channel1 => &self.tx_wakers[0],
+                IpccChannel::Channel2 => &self.tx_wakers[1],
+                IpccChannel::Channel3 => &self.tx_wakers[2],
+                IpccChannel::Channel4 => &self.tx_wakers[3],
+                IpccChannel::Channel5 => &self.tx_wakers[4],
+                IpccChannel::Channel6 => &self.tx_wakers[5],
+            }
+        }
+    }
+
+    pub trait Instance: crate::rcc::RccPeripheral {
+        fn regs() -> crate::pac::ipcc::Ipcc;
+        fn set_cpu2(enabled: bool);
+        fn state() -> &'static State;
+    }
+}
+
+fn _configure_pwr() {
+    // TODO: move this to RCC
+
     let pwr = crate::pac::PWR;
     let rcc = crate::pac::RCC;
 
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 6fde61c06..45a7b5476 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -146,35 +146,35 @@ impl Default for Config {
 pub fn init(config: Config) -> Peripherals {
     let p = Peripherals::take();
 
-    unsafe {
-        #[cfg(dbgmcu)]
-        if config.enable_debug_during_sleep {
-            crate::pac::DBGMCU.cr().modify(|cr| {
-                #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))]
-                {
-                    cr.set_dbg_stop(true);
-                    cr.set_dbg_standby(true);
-                }
-                #[cfg(any(
-                    dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1,
-                    dbgmcu_l4, dbgmcu_wb, dbgmcu_wl
-                ))]
-                {
-                    cr.set_dbg_sleep(true);
-                    cr.set_dbg_stop(true);
-                    cr.set_dbg_standby(true);
-                }
-                #[cfg(dbgmcu_h7)]
-                {
-                    cr.set_d1dbgcken(true);
-                    cr.set_d3dbgcken(true);
-                    cr.set_dbgsleep_d1(true);
-                    cr.set_dbgstby_d1(true);
-                    cr.set_dbgstop_d1(true);
-                }
-            });
-        }
+    #[cfg(dbgmcu)]
+    if config.enable_debug_during_sleep {
+        crate::pac::DBGMCU.cr().modify(|cr| {
+            #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5))]
+            {
+                cr.set_dbg_stop(true);
+                cr.set_dbg_standby(true);
+            }
+            #[cfg(any(
+                dbgmcu_f1, dbgmcu_f2, dbgmcu_f3, dbgmcu_f4, dbgmcu_f7, dbgmcu_g4, dbgmcu_f7, dbgmcu_l0, dbgmcu_l1,
+                dbgmcu_l4, dbgmcu_wb, dbgmcu_wl
+            ))]
+            {
+                cr.set_dbg_sleep(true);
+                cr.set_dbg_stop(true);
+                cr.set_dbg_standby(true);
+            }
+            #[cfg(dbgmcu_h7)]
+            {
+                cr.set_d1dbgcken(true);
+                cr.set_d3dbgcken(true);
+                cr.set_dbgsleep_d1(true);
+                cr.set_dbgstby_d1(true);
+                cr.set_dbgstop_d1(true);
+            }
+        });
+    }
 
+    unsafe {
         gpio::init();
         dma::init(
             #[cfg(bdma)]
diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs
index cfb79947c..0e153202e 100644
--- a/embassy-stm32/src/pwm/complementary_pwm.rs
+++ b/embassy-stm32/src/pwm/complementary_pwm.rs
@@ -21,7 +21,7 @@ macro_rules! complementary_channel_impl {
         impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> {
             pub fn $new_chx(pin: impl Peripheral<P = impl $complementary_pin_trait<Perip>> + 'd) -> Self {
                 into_ref!(pin);
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     pin.set_low();
                     pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
                     #[cfg(gpio_v2)]
@@ -72,33 +72,27 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
         this.inner.set_frequency(freq);
         this.inner.start();
 
-        unsafe {
-            this.inner.enable_outputs(true);
+        this.inner.enable_outputs(true);
 
-            this.inner
-                .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
-            this.inner
-                .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
-            this.inner
-                .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
-            this.inner
-                .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
-        }
+        this.inner
+            .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
+        this.inner
+            .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
+        this.inner
+            .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
+        this.inner
+            .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
         this
     }
 
     pub fn enable(&mut self, channel: Channel) {
-        unsafe {
-            self.inner.enable_channel(channel, true);
-            self.inner.enable_complementary_channel(channel, true);
-        }
+        self.inner.enable_channel(channel, true);
+        self.inner.enable_complementary_channel(channel, true);
     }
 
     pub fn disable(&mut self, channel: Channel) {
-        unsafe {
-            self.inner.enable_complementary_channel(channel, false);
-            self.inner.enable_channel(channel, false);
-        }
+        self.inner.enable_complementary_channel(channel, false);
+        self.inner.enable_channel(channel, false);
     }
 
     pub fn set_freq(&mut self, freq: Hertz) {
@@ -106,22 +100,20 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> {
     }
 
     pub fn get_max_duty(&self) -> u16 {
-        unsafe { self.inner.get_max_compare_value() }
+        self.inner.get_max_compare_value()
     }
 
     pub fn set_duty(&mut self, channel: Channel, duty: u16) {
         assert!(duty < self.get_max_duty());
-        unsafe { self.inner.set_compare_value(channel, duty) }
+        self.inner.set_compare_value(channel, duty)
     }
 
     /// Set the dead time as a proportion of max_duty
     pub fn set_dead_time(&mut self, value: u16) {
         let (ckd, value) = compute_dead_time_value(value);
 
-        unsafe {
-            self.inner.set_dead_time_clock_division(ckd);
-            self.inner.set_dead_time_value(value);
-        }
+        self.inner.set_dead_time_clock_division(ckd);
+        self.inner.set_dead_time_value(value);
     }
 }
 
diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs
index 0bef07089..5aba2663e 100644
--- a/embassy-stm32/src/pwm/mod.rs
+++ b/embassy-stm32/src/pwm/mod.rs
@@ -59,33 +59,33 @@ pub(crate) mod sealed {
 
     pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance {
         /// Global output enable. Does not do anything on non-advanced timers.
-        unsafe fn enable_outputs(&mut self, enable: bool);
+        fn enable_outputs(&mut self, enable: bool);
 
-        unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
+        fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
 
-        unsafe fn enable_channel(&mut self, channel: Channel, enable: bool);
+        fn enable_channel(&mut self, channel: Channel, enable: bool);
 
-        unsafe fn set_compare_value(&mut self, channel: Channel, value: u16);
+        fn set_compare_value(&mut self, channel: Channel, value: u16);
 
-        unsafe fn get_max_compare_value(&self) -> u16;
+        fn get_max_compare_value(&self) -> u16;
     }
 
     pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance {
-        unsafe fn set_dead_time_clock_division(&mut self, value: Ckd);
+        fn set_dead_time_clock_division(&mut self, value: Ckd);
 
-        unsafe fn set_dead_time_value(&mut self, value: u8);
+        fn set_dead_time_value(&mut self, value: u8);
 
-        unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool);
+        fn enable_complementary_channel(&mut self, channel: Channel, enable: bool);
     }
 
     pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance {
-        unsafe fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
+        fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode);
 
-        unsafe fn enable_channel(&mut self, channel: Channel, enable: bool);
+        fn enable_channel(&mut self, channel: Channel, enable: bool);
 
-        unsafe fn set_compare_value(&mut self, channel: Channel, value: u32);
+        fn set_compare_value(&mut self, channel: Channel, value: u32);
 
-        unsafe fn get_max_compare_value(&self) -> u32;
+        fn get_max_compare_value(&self) -> u32;
     }
 }
 
@@ -108,9 +108,9 @@ pub trait CaptureCompare32bitInstance:
 macro_rules! impl_compare_capable_16bit {
     ($inst:ident) => {
         impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst {
-            unsafe fn enable_outputs(&mut self, _enable: bool) {}
+            fn enable_outputs(&mut self, _enable: bool) {}
 
-            unsafe fn set_output_compare_mode(&mut self, channel: crate::pwm::Channel, mode: OutputCompareMode) {
+            fn set_output_compare_mode(&mut self, channel: crate::pwm::Channel, mode: OutputCompareMode) {
                 use crate::timer::sealed::GeneralPurpose16bitInstance;
                 let r = Self::regs_gp16();
                 let raw_channel: usize = channel.raw();
@@ -118,19 +118,19 @@ macro_rules! impl_compare_capable_16bit {
                     .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
             }
 
-            unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) {
+            fn enable_channel(&mut self, channel: Channel, enable: bool) {
                 use crate::timer::sealed::GeneralPurpose16bitInstance;
                 Self::regs_gp16()
                     .ccer()
                     .modify(|w| w.set_cce(channel.raw(), enable));
             }
 
-            unsafe fn set_compare_value(&mut self, channel: Channel, value: u16) {
+            fn set_compare_value(&mut self, channel: Channel, value: u16) {
                 use crate::timer::sealed::GeneralPurpose16bitInstance;
                 Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value));
             }
 
-            unsafe fn get_max_compare_value(&self) -> u16 {
+            fn get_max_compare_value(&self) -> u16 {
                 use crate::timer::sealed::GeneralPurpose16bitInstance;
                 Self::regs_gp16().arr().read().arr()
             }
@@ -150,7 +150,7 @@ foreach_interrupt! {
     ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => {
         impl_compare_capable_16bit!($inst);
         impl crate::pwm::sealed::CaptureCompare32bitInstance for crate::peripherals::$inst {
-            unsafe fn set_output_compare_mode(
+            fn set_output_compare_mode(
                 &mut self,
                 channel: crate::pwm::Channel,
                 mode: OutputCompareMode,
@@ -160,17 +160,17 @@ foreach_interrupt! {
                 Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
             }
 
-            unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) {
+            fn enable_channel(&mut self, channel: Channel, enable: bool) {
                 use crate::timer::sealed::GeneralPurpose32bitInstance;
                 Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable));
             }
 
-            unsafe fn set_compare_value(&mut self, channel: Channel, value: u32) {
+            fn set_compare_value(&mut self, channel: Channel, value: u32) {
                 use crate::timer::sealed::GeneralPurpose32bitInstance;
                 Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value));
             }
 
-            unsafe fn get_max_compare_value(&self) -> u32 {
+            fn get_max_compare_value(&self) -> u32 {
                 use crate::timer::sealed::GeneralPurpose32bitInstance;
                 Self::regs_gp32().arr().read().arr() as u32
             }
@@ -185,13 +185,13 @@ foreach_interrupt! {
 
     ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => {
         impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst {
-            unsafe fn enable_outputs(&mut self, enable: bool) {
+            fn enable_outputs(&mut self, enable: bool) {
                 use crate::timer::sealed::AdvancedControlInstance;
                 let r = Self::regs_advanced();
                 r.bdtr().modify(|w| w.set_moe(enable));
             }
 
-            unsafe fn set_output_compare_mode(
+            fn set_output_compare_mode(
                 &mut self,
                 channel: crate::pwm::Channel,
                 mode: OutputCompareMode,
@@ -203,21 +203,21 @@ foreach_interrupt! {
                     .modify(|w| w.set_ocm(raw_channel % 2, mode.into()));
             }
 
-            unsafe fn enable_channel(&mut self, channel: Channel, enable: bool) {
+            fn enable_channel(&mut self, channel: Channel, enable: bool) {
                 use crate::timer::sealed::AdvancedControlInstance;
                 Self::regs_advanced()
                     .ccer()
                     .modify(|w| w.set_cce(channel.raw(), enable));
             }
 
-            unsafe fn set_compare_value(&mut self, channel: Channel, value: u16) {
+            fn set_compare_value(&mut self, channel: Channel, value: u16) {
                 use crate::timer::sealed::AdvancedControlInstance;
                 Self::regs_advanced()
                     .ccr(channel.raw())
                     .modify(|w| w.set_ccr(value));
             }
 
-            unsafe fn get_max_compare_value(&self) -> u16 {
+            fn get_max_compare_value(&self) -> u16 {
                 use crate::timer::sealed::AdvancedControlInstance;
                 Self::regs_advanced().arr().read().arr()
             }
@@ -228,17 +228,17 @@ foreach_interrupt! {
         }
 
         impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {
-            unsafe fn set_dead_time_clock_division(&mut self, value: Ckd) {
+            fn set_dead_time_clock_division(&mut self, value: Ckd) {
                 use crate::timer::sealed::AdvancedControlInstance;
                 Self::regs_advanced().cr1().modify(|w| w.set_ckd(value));
             }
 
-            unsafe fn set_dead_time_value(&mut self, value: u8) {
+            fn set_dead_time_value(&mut self, value: u8) {
                 use crate::timer::sealed::AdvancedControlInstance;
                 Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value));
             }
 
-            unsafe fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
+            fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) {
                 use crate::timer::sealed::AdvancedControlInstance;
                 Self::regs_advanced()
                     .ccer()
diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/pwm/simple_pwm.rs
index b045a2d78..995f59c23 100644
--- a/embassy-stm32/src/pwm/simple_pwm.rs
+++ b/embassy-stm32/src/pwm/simple_pwm.rs
@@ -24,7 +24,7 @@ macro_rules! channel_impl {
         impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> {
             pub fn $new_chx(pin: impl Peripheral<P = impl $pin_trait<Perip>> + 'd) -> Self {
                 into_ref!(pin);
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     pin.set_low();
                     pin.set_as_af(pin.af_num(), AFType::OutputPushPull);
                     #[cfg(gpio_v2)]
@@ -71,31 +71,25 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
         this.inner.set_frequency(freq);
         this.inner.start();
 
-        unsafe {
-            this.inner.enable_outputs(true);
+        this.inner.enable_outputs(true);
 
-            this.inner
-                .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
-            this.inner
-                .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
-            this.inner
-                .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
-            this.inner
-                .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
-        }
+        this.inner
+            .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1);
+        this.inner
+            .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1);
+        this.inner
+            .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1);
+        this.inner
+            .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1);
         this
     }
 
     pub fn enable(&mut self, channel: Channel) {
-        unsafe {
-            self.inner.enable_channel(channel, true);
-        }
+        self.inner.enable_channel(channel, true);
     }
 
     pub fn disable(&mut self, channel: Channel) {
-        unsafe {
-            self.inner.enable_channel(channel, false);
-        }
+        self.inner.enable_channel(channel, false);
     }
 
     pub fn set_freq(&mut self, freq: Hertz) {
@@ -103,11 +97,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> {
     }
 
     pub fn get_max_duty(&self) -> u16 {
-        unsafe { self.inner.get_max_compare_value() }
+        self.inner.get_max_compare_value()
     }
 
     pub fn set_duty(&mut self, channel: Channel, duty: u16) {
         assert!(duty < self.get_max_duty());
-        unsafe { self.inner.set_compare_value(channel, duty) }
+        self.inner.set_compare_value(channel, duty)
     }
 }
diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs
index c3126b37f..e9db934bf 100644
--- a/embassy-stm32/src/qspi/mod.rs
+++ b/embassy-stm32/src/qspi/mod.rs
@@ -96,20 +96,18 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
     ) -> Self {
         into_ref!(peri, d0, d1, d2, d3, sck, nss);
 
-        unsafe {
-            sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
-            sck.set_speed(crate::gpio::Speed::VeryHigh);
-            nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
-            nss.set_speed(crate::gpio::Speed::VeryHigh);
-            d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
-            d0.set_speed(crate::gpio::Speed::VeryHigh);
-            d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
-            d1.set_speed(crate::gpio::Speed::VeryHigh);
-            d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
-            d2.set_speed(crate::gpio::Speed::VeryHigh);
-            d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
-            d3.set_speed(crate::gpio::Speed::VeryHigh);
-        }
+        sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
+        sck.set_speed(crate::gpio::Speed::VeryHigh);
+        nss.set_as_af(nss.af_num(), AFType::OutputPushPull);
+        nss.set_speed(crate::gpio::Speed::VeryHigh);
+        d0.set_as_af(d0.af_num(), AFType::OutputPushPull);
+        d0.set_speed(crate::gpio::Speed::VeryHigh);
+        d1.set_as_af(d1.af_num(), AFType::OutputPushPull);
+        d1.set_speed(crate::gpio::Speed::VeryHigh);
+        d2.set_as_af(d2.af_num(), AFType::OutputPushPull);
+        d2.set_speed(crate::gpio::Speed::VeryHigh);
+        d3.set_as_af(d3.af_num(), AFType::OutputPushPull);
+        d3.set_speed(crate::gpio::Speed::VeryHigh);
 
         Self::new_inner(
             peri,
@@ -138,21 +136,19 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
         into_ref!(peri, dma);
 
         T::enable();
-        unsafe {
-            T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
+        T::REGS.cr().write(|w| w.set_fthres(config.fifo_threshold.into()));
 
-            while T::REGS.sr().read().busy() {}
+        while T::REGS.sr().read().busy() {}
 
-            T::REGS.cr().write(|w| {
-                w.set_prescaler(config.prescaler);
-                w.set_en(true);
-            });
-            T::REGS.dcr().write(|w| {
-                w.set_fsize(config.memory_size.into());
-                w.set_csht(config.cs_high_time.into());
-                w.set_ckmode(false);
-            });
-        }
+        T::REGS.cr().write(|w| {
+            w.set_prescaler(config.prescaler);
+            w.set_en(true);
+        });
+        T::REGS.dcr().write(|w| {
+            w.set_fsize(config.memory_size.into());
+            w.set_csht(config.cs_high_time.into());
+            w.set_ckmode(false);
+        });
 
         Self {
             _peri: peri,
@@ -168,148 +164,140 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> {
     }
 
     pub fn command(&mut self, transaction: TransferConfig) {
-        unsafe {
-            T::REGS.cr().modify(|v| v.set_dmaen(false));
-            self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+        T::REGS.cr().modify(|v| v.set_dmaen(false));
+        self.setup_transaction(QspiMode::IndirectWrite, &transaction);
 
-            while !T::REGS.sr().read().tcf() {}
-            T::REGS.fcr().modify(|v| v.set_ctcf(true));
-        }
+        while !T::REGS.sr().read().tcf() {}
+        T::REGS.fcr().modify(|v| v.set_ctcf(true));
     }
 
     pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
-        unsafe {
-            T::REGS.cr().modify(|v| v.set_dmaen(false));
-            self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+        T::REGS.cr().modify(|v| v.set_dmaen(false));
+        self.setup_transaction(QspiMode::IndirectWrite, &transaction);
 
-            if let Some(len) = transaction.data_len {
-                let current_ar = T::REGS.ar().read().address();
-                T::REGS.ccr().modify(|v| {
-                    v.set_fmode(QspiMode::IndirectRead.into());
-                });
-                T::REGS.ar().write(|v| {
-                    v.set_address(current_ar);
-                });
+        if let Some(len) = transaction.data_len {
+            let current_ar = T::REGS.ar().read().address();
+            T::REGS.ccr().modify(|v| {
+                v.set_fmode(QspiMode::IndirectRead.into());
+            });
+            T::REGS.ar().write(|v| {
+                v.set_address(current_ar);
+            });
 
-                for idx in 0..len {
-                    while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
-                    buf[idx] = *(T::REGS.dr().ptr() as *mut u8);
-                }
+            for idx in 0..len {
+                while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
+                buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
             }
-
-            while !T::REGS.sr().read().tcf() {}
-            T::REGS.fcr().modify(|v| v.set_ctcf(true));
         }
+
+        while !T::REGS.sr().read().tcf() {}
+        T::REGS.fcr().modify(|v| v.set_ctcf(true));
     }
 
     pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
-        unsafe {
-            T::REGS.cr().modify(|v| v.set_dmaen(false));
-            self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+        T::REGS.cr().modify(|v| v.set_dmaen(false));
+        self.setup_transaction(QspiMode::IndirectWrite, &transaction);
 
-            if let Some(len) = transaction.data_len {
-                T::REGS.ccr().modify(|v| {
-                    v.set_fmode(QspiMode::IndirectWrite.into());
-                });
+        if let Some(len) = transaction.data_len {
+            T::REGS.ccr().modify(|v| {
+                v.set_fmode(QspiMode::IndirectWrite.into());
+            });
 
-                for idx in 0..len {
-                    while !T::REGS.sr().read().ftf() {}
-                    *(T::REGS.dr().ptr() as *mut u8) = buf[idx];
-                }
+            for idx in 0..len {
+                while !T::REGS.sr().read().ftf() {}
+                unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) };
             }
-
-            while !T::REGS.sr().read().tcf() {}
-            T::REGS.fcr().modify(|v| v.set_ctcf(true));
         }
+
+        while !T::REGS.sr().read().tcf() {}
+        T::REGS.fcr().modify(|v| v.set_ctcf(true));
     }
 
     pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig)
     where
         Dma: QuadDma<T>,
     {
-        unsafe {
-            self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+        self.setup_transaction(QspiMode::IndirectWrite, &transaction);
 
-            T::REGS.ccr().modify(|v| {
-                v.set_fmode(QspiMode::IndirectRead.into());
-            });
-            let current_ar = T::REGS.ar().read().address();
-            T::REGS.ar().write(|v| {
-                v.set_address(current_ar);
-            });
+        T::REGS.ccr().modify(|v| {
+            v.set_fmode(QspiMode::IndirectRead.into());
+        });
+        let current_ar = T::REGS.ar().read().address();
+        T::REGS.ar().write(|v| {
+            v.set_address(current_ar);
+        });
 
-            let request = self.dma.request();
-            let transfer = Transfer::new_read(
+        let request = self.dma.request();
+        let transfer = unsafe {
+            Transfer::new_read(
                 &mut self.dma,
                 request,
-                T::REGS.dr().ptr() as *mut u8,
+                T::REGS.dr().as_ptr() as *mut u8,
                 buf,
                 Default::default(),
-            );
+            )
+        };
 
-            T::REGS.cr().modify(|v| v.set_dmaen(true));
+        T::REGS.cr().modify(|v| v.set_dmaen(true));
 
-            transfer.blocking_wait();
-        }
+        transfer.blocking_wait();
     }
 
     pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig)
     where
         Dma: QuadDma<T>,
     {
-        unsafe {
-            self.setup_transaction(QspiMode::IndirectWrite, &transaction);
+        self.setup_transaction(QspiMode::IndirectWrite, &transaction);
 
-            T::REGS.ccr().modify(|v| {
-                v.set_fmode(QspiMode::IndirectWrite.into());
-            });
+        T::REGS.ccr().modify(|v| {
+            v.set_fmode(QspiMode::IndirectWrite.into());
+        });
 
-            let request = self.dma.request();
-            let transfer = Transfer::new_write(
+        let request = self.dma.request();
+        let transfer = unsafe {
+            Transfer::new_write(
                 &mut self.dma,
                 request,
                 buf,
-                T::REGS.dr().ptr() as *mut u8,
+                T::REGS.dr().as_ptr() as *mut u8,
                 Default::default(),
-            );
+            )
+        };
 
-            T::REGS.cr().modify(|v| v.set_dmaen(true));
+        T::REGS.cr().modify(|v| v.set_dmaen(true));
 
-            transfer.blocking_wait();
-        }
+        transfer.blocking_wait();
     }
 
     fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig) {
-        unsafe {
-            T::REGS.fcr().modify(|v| {
-                v.set_csmf(true);
-                v.set_ctcf(true);
-                v.set_ctef(true);
-                v.set_ctof(true);
+        T::REGS.fcr().modify(|v| {
+            v.set_csmf(true);
+            v.set_ctcf(true);
+            v.set_ctef(true);
+            v.set_ctof(true);
+        });
+
+        while T::REGS.sr().read().busy() {}
+
+        if let Some(len) = transaction.data_len {
+            T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
+        }
+
+        T::REGS.ccr().write(|v| {
+            v.set_fmode(fmode.into());
+            v.set_imode(transaction.iwidth.into());
+            v.set_instruction(transaction.instruction);
+            v.set_admode(transaction.awidth.into());
+            v.set_adsize(self.config.address_size.into());
+            v.set_dmode(transaction.dwidth.into());
+            v.set_abmode(QspiWidth::NONE.into());
+            v.set_dcyc(transaction.dummy.into());
+        });
+
+        if let Some(addr) = transaction.address {
+            T::REGS.ar().write(|v| {
+                v.set_address(addr);
             });
-
-            while T::REGS.sr().read().busy() {}
-
-            if let Some(len) = transaction.data_len {
-                T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
-            }
-
-            T::REGS.ccr().write(|v| {
-                v.set_fmode(fmode.into());
-                v.set_imode(transaction.iwidth.into());
-                v.set_instruction(transaction.instruction);
-                v.set_admode(transaction.awidth.into());
-                v.set_adsize(self.config.address_size.into());
-                v.set_dmode(transaction.dwidth.into());
-                v.set_abmode(QspiWidth::NONE.into());
-                v.set_dcyc(transaction.dummy.into());
-            });
-
-            if let Some(addr) = transaction.address {
-                T::REGS.ar().write(|v| {
-                    v.set_address(addr);
-                });
-            }
         }
     }
 }
diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs
index e0929ca49..bc430afb2 100644
--- a/embassy-stm32/src/rcc/f4.rs
+++ b/embassy-stm32/src/rcc/f4.rs
@@ -36,18 +36,18 @@ pub struct Config {
 }
 
 #[cfg(stm32f410)]
-unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
+fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
     None
 }
 
 // Not currently implemented, but will be in the future
 #[cfg(any(stm32f411, stm32f412, stm32f413, stm32f423, stm32f446))]
-unsafe fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
+fn setup_i2s_pll(_vco_in: u32, _plli2s: Option<u32>) -> Option<u32> {
     None
 }
 
 #[cfg(not(any(stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f446)))]
-unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
+fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
     let min_div = 2;
     let max_div = 7;
     let target = match plli2s {
@@ -82,13 +82,7 @@ unsafe fn setup_i2s_pll(vco_in: u32, plli2s: Option<u32>) -> Option<u32> {
     Some(output)
 }
 
-unsafe fn setup_pll(
-    pllsrcclk: u32,
-    use_hse: bool,
-    pllsysclk: Option<u32>,
-    plli2s: Option<u32>,
-    pll48clk: bool,
-) -> PllResults {
+fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, plli2s: Option<u32>, pll48clk: bool) -> PllResults {
     use crate::pac::rcc::vals::{Pllp, Pllsrc};
 
     let sysclk = pllsysclk.unwrap_or(pllsrcclk);
@@ -320,7 +314,7 @@ impl<'d, T: McoInstance> Mco<'d, T> {
     }
 }
 
-unsafe fn flash_setup(sysclk: u32) {
+fn flash_setup(sysclk: u32) {
     use crate::pac::flash::vals::Latency;
 
     // Be conservative with voltage ranges
diff --git a/embassy-stm32/src/rcc/f7.rs b/embassy-stm32/src/rcc/f7.rs
index 2d21326a3..71215cac5 100644
--- a/embassy-stm32/src/rcc/f7.rs
+++ b/embassy-stm32/src/rcc/f7.rs
@@ -25,7 +25,7 @@ pub struct Config {
     pub pll48: bool,
 }
 
-unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
+fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48clk: bool) -> PllResults {
     use crate::pac::rcc::vals::{Pllp, Pllsrc};
 
     let sysclk = pllsysclk.unwrap_or(pllsrcclk);
@@ -97,7 +97,7 @@ unsafe fn setup_pll(pllsrcclk: u32, use_hse: bool, pllsysclk: Option<u32>, pll48
     }
 }
 
-unsafe fn flash_setup(sysclk: u32) {
+fn flash_setup(sysclk: u32) {
     use crate::pac::flash::vals::Latency;
 
     // Be conservative with voltage ranges
diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs
index 3e138c7ab..17c73c36b 100644
--- a/embassy-stm32/src/rcc/g0.rs
+++ b/embassy-stm32/src/rcc/g0.rs
@@ -245,7 +245,7 @@ impl Default for Config {
 }
 
 impl PllConfig {
-    pub(crate) unsafe fn init(self) -> u32 {
+    pub(crate) fn init(self) -> u32 {
         assert!(self.n >= 8 && self.n <= 86);
         let (src, input_freq) = match self.source {
             PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ.0),
diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs
index 17fbc6056..4025a4e05 100644
--- a/embassy-stm32/src/rcc/h5.rs
+++ b/embassy-stm32/src/rcc/h5.rs
@@ -462,7 +462,7 @@ struct PllOutput {
     r: Option<Hertz>,
 }
 
-unsafe fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
+fn init_pll(num: usize, config: Option<Pll>, input: &PllInput) -> PllOutput {
     let Some(config) = config else {
         // Stop PLL
         RCC.cr().modify(|w| w.set_pllon(num, false));
@@ -595,12 +595,9 @@ fn flash_setup(clk: Hertz, vos: VoltageScale) {
 
     defmt::debug!("flash: latency={} wrhighfreq={}", latency, wrhighfreq);
 
-    // NOTE(unsafe) Atomic write
-    unsafe {
-        FLASH.acr().write(|w| {
-            w.set_wrhighfreq(wrhighfreq);
-            w.set_latency(latency);
-        });
-        while FLASH.acr().read().latency() != latency {}
-    }
+    FLASH.acr().write(|w| {
+        w.set_wrhighfreq(wrhighfreq);
+        w.set_latency(latency);
+    });
+    while FLASH.acr().read().latency() != latency {}
 }
diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs
index 0185f7ae8..daa1cd61f 100644
--- a/embassy-stm32/src/rcc/h7.rs
+++ b/embassy-stm32/src/rcc/h7.rs
@@ -253,14 +253,11 @@ fn flash_setup(rcc_aclk: u32, vos: VoltageScale) {
         },
     };
 
-    // NOTE(unsafe) Atomic write
-    unsafe {
-        FLASH.acr().write(|w| {
-            w.set_wrhighfreq(progr_delay);
-            w.set_latency(wait_states)
-        });
-        while FLASH.acr().read().latency() != wait_states {}
-    }
+    FLASH.acr().write(|w| {
+        w.set_wrhighfreq(progr_delay);
+        w.set_latency(wait_states)
+    });
+    while FLASH.acr().read().latency() != wait_states {}
 }
 
 pub enum McoClock {
@@ -474,7 +471,6 @@ pub(crate) unsafe fn init(mut config: Config) {
     // Configure traceclk from PLL if needed
     traceclk_setup(&mut config, sys_use_pll1_p);
 
-    // NOTE(unsafe) We have exclusive access to the RCC
     let (pll1_p_ck, pll1_q_ck, pll1_r_ck) = pll::pll_setup(srcclk.0, &config.pll1, 0);
     let (pll2_p_ck, pll2_q_ck, pll2_r_ck) = pll::pll_setup(srcclk.0, &config.pll2, 1);
     let (pll3_p_ck, pll3_q_ck, pll3_r_ck) = pll::pll_setup(srcclk.0, &config.pll3, 2);
@@ -756,7 +752,7 @@ mod pll {
     /// # Safety
     ///
     /// Must have exclusive access to the RCC register block
-    unsafe fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults {
+    fn vco_setup(pll_src: u32, requested_output: u32, plln: usize) -> PllConfigResults {
         use crate::pac::rcc::vals::{Pllrge, Pllvcosel};
 
         let (vco_ck_target, pll_x_p) = vco_output_divider_setup(requested_output, plln);
@@ -785,11 +781,7 @@ mod pll {
     /// # Safety
     ///
     /// Must have exclusive access to the RCC register block
-    pub(super) unsafe fn pll_setup(
-        pll_src: u32,
-        config: &PllConfig,
-        plln: usize,
-    ) -> (Option<u32>, Option<u32>, Option<u32>) {
+    pub(super) fn pll_setup(pll_src: u32, config: &PllConfig, plln: usize) -> (Option<u32>, Option<u32>, Option<u32>) {
         use crate::pac::rcc::vals::Divp;
 
         match config.p_ck {
diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs
index c657bf70e..b2faec53d 100644
--- a/embassy-stm32/src/rng.rs
+++ b/embassy-stm32/src/rng.rs
@@ -34,40 +34,34 @@ impl<'d, T: Instance> Rng<'d, T> {
     pub fn reset(&mut self) {
         // rng_v2 locks up on seed error, needs reset
         #[cfg(rng_v2)]
-        if unsafe { T::regs().sr().read().seis() } {
+        if T::regs().sr().read().seis() {
             T::reset();
         }
-        unsafe {
-            T::regs().cr().modify(|reg| {
-                reg.set_rngen(true);
-                reg.set_ie(true);
-            });
-            T::regs().sr().modify(|reg| {
-                reg.set_seis(false);
-                reg.set_ceis(false);
-            });
-        }
+        T::regs().cr().modify(|reg| {
+            reg.set_rngen(true);
+            reg.set_ie(true);
+        });
+        T::regs().sr().modify(|reg| {
+            reg.set_seis(false);
+            reg.set_ceis(false);
+        });
         // Reference manual says to discard the first.
         let _ = self.next_u32();
     }
 
     pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
-        unsafe {
-            T::regs().cr().modify(|reg| {
-                reg.set_rngen(true);
-            })
-        }
+        T::regs().cr().modify(|reg| {
+            reg.set_rngen(true);
+        });
 
         for chunk in dest.chunks_mut(4) {
             poll_fn(|cx| {
                 RNG_WAKER.register(cx.waker());
-                unsafe {
-                    T::regs().cr().modify(|reg| {
-                        reg.set_ie(true);
-                    });
-                }
+                T::regs().cr().modify(|reg| {
+                    reg.set_ie(true);
+                });
 
-                let bits = unsafe { T::regs().sr().read() };
+                let bits = T::regs().sr().read();
 
                 if bits.drdy() {
                     Poll::Ready(Ok(()))
@@ -82,7 +76,7 @@ impl<'d, T: Instance> Rng<'d, T> {
                 }
             })
             .await?;
-            let random_bytes = unsafe { T::regs().dr().read() }.to_be_bytes();
+            let random_bytes = T::regs().dr().read().to_be_bytes();
             for (dest, src) in chunk.iter_mut().zip(random_bytes.iter()) {
                 *dest = *src
             }
@@ -95,11 +89,11 @@ impl<'d, T: Instance> Rng<'d, T> {
 impl<'d, T: Instance> RngCore for Rng<'d, T> {
     fn next_u32(&mut self) -> u32 {
         loop {
-            let sr = unsafe { T::regs().sr().read() };
+            let sr = T::regs().sr().read();
             if sr.seis() | sr.ceis() {
                 self.reset();
             } else if sr.drdy() {
-                return unsafe { T::regs().dr().read() };
+                return T::regs().dr().read();
             }
         }
     }
diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs
index 0a590c1bb..a9c48d88d 100644
--- a/embassy-stm32/src/rtc/datetime.rs
+++ b/embassy-stm32/src/rtc/datetime.rs
@@ -154,29 +154,27 @@ pub(super) fn write_date_time(rtc: &Rtc, t: DateTime) {
     let yr_offset = (yr - 1970_u16) as u8;
     let (yt, yu) = byte_to_bcd2(yr_offset);
 
-    unsafe {
-        use crate::pac::rtc::vals::Ampm;
+    use crate::pac::rtc::vals::Ampm;
 
-        rtc.tr().write(|w| {
-            w.set_ht(ht);
-            w.set_hu(hu);
-            w.set_mnt(mnt);
-            w.set_mnu(mnu);
-            w.set_st(st);
-            w.set_su(su);
-            w.set_pm(Ampm::AM);
-        });
+    rtc.tr().write(|w| {
+        w.set_ht(ht);
+        w.set_hu(hu);
+        w.set_mnt(mnt);
+        w.set_mnu(mnu);
+        w.set_st(st);
+        w.set_su(su);
+        w.set_pm(Ampm::AM);
+    });
 
-        rtc.dr().write(|w| {
-            w.set_dt(dt);
-            w.set_du(du);
-            w.set_mt(mt > 0);
-            w.set_mu(mu);
-            w.set_yt(yt);
-            w.set_yu(yu);
-            w.set_wdu(day_of_week_to_u8(t.day_of_week));
-        });
-    }
+    rtc.dr().write(|w| {
+        w.set_dt(dt);
+        w.set_du(du);
+        w.set_mt(mt > 0);
+        w.set_mu(mu);
+        w.set_yt(yt);
+        w.set_yu(yu);
+        w.set_wdu(day_of_week_to_u8(t.day_of_week));
+    });
 }
 
 pub(super) fn datetime(
diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs
index 962927fb1..12a2ac795 100644
--- a/embassy-stm32/src/rtc/mod.rs
+++ b/embassy-stm32/src/rtc/mod.rs
@@ -113,7 +113,7 @@ impl Default for RtcCalibrationCyclePeriod {
 
 impl<'d, T: Instance> Rtc<'d, T> {
     pub fn new(_rtc: impl Peripheral<P = T> + 'd, rtc_config: RtcConfig) -> Self {
-        unsafe { T::enable_peripheral_clk() };
+        T::enable_peripheral_clk();
 
         let mut rtc_struct = Self {
             phantom: PhantomData,
@@ -144,34 +144,32 @@ impl<'d, T: Instance> Rtc<'d, T> {
     /// Will return an `RtcError::InvalidDateTime` if the stored value in the system is not a valid [`DayOfWeek`].
     pub fn now(&self) -> Result<DateTime, RtcError> {
         let r = T::regs();
-        unsafe {
-            let tr = r.tr().read();
-            let second = bcd2_to_byte((tr.st(), tr.su()));
-            let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));
-            let hour = bcd2_to_byte((tr.ht(), tr.hu()));
-            // Reading either RTC_SSR or RTC_TR locks the values in the higher-order
-            // calendar shadow registers until RTC_DR is read.
-            let dr = r.dr().read();
+        let tr = r.tr().read();
+        let second = bcd2_to_byte((tr.st(), tr.su()));
+        let minute = bcd2_to_byte((tr.mnt(), tr.mnu()));
+        let hour = bcd2_to_byte((tr.ht(), tr.hu()));
+        // Reading either RTC_SSR or RTC_TR locks the values in the higher-order
+        // calendar shadow registers until RTC_DR is read.
+        let dr = r.dr().read();
 
-            let weekday = dr.wdu();
-            let day = bcd2_to_byte((dr.dt(), dr.du()));
-            let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
-            let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
+        let weekday = dr.wdu();
+        let day = bcd2_to_byte((dr.dt(), dr.du()));
+        let month = bcd2_to_byte((dr.mt() as u8, dr.mu()));
+        let year = bcd2_to_byte((dr.yt(), dr.yu())) as u16 + 1970_u16;
 
-            self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
-        }
+        self::datetime::datetime(year, month, day, weekday, hour, minute, second).map_err(RtcError::InvalidDateTime)
     }
 
     /// Check if daylight savings time is active.
     pub fn get_daylight_savings(&self) -> bool {
-        let cr = unsafe { T::regs().cr().read() };
+        let cr = T::regs().cr().read();
         cr.bkp()
     }
 
     /// Enable/disable daylight savings time.
     pub fn set_daylight_savings(&mut self, daylight_savings: bool) {
         self.write(true, |rtc| {
-            unsafe { rtc.cr().modify(|w| w.set_bkp(daylight_savings)) };
+            rtc.cr().modify(|w| w.set_bkp(daylight_savings));
         })
     }
 
@@ -228,7 +226,7 @@ pub(crate) mod sealed {
             crate::pac::RTC
         }
 
-        unsafe fn enable_peripheral_clk() {}
+        fn enable_peripheral_clk() {}
 
         /// Read content of the backup register.
         ///
diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs
index adaafe67a..e1615b34c 100644
--- a/embassy-stm32/src/rtc/v2.rs
+++ b/embassy-stm32/src/rtc/v2.rs
@@ -8,74 +8,72 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
     /// It this changes the RTC clock source the time will be reset
     pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
         // Unlock the backup domain
-        unsafe {
-            let clock_config = rtc_config.clock_config as u8;
+        let clock_config = rtc_config.clock_config as u8;
 
-            #[cfg(not(rtc_v2wb))]
-            use stm32_metapac::rcc::vals::Rtcsel;
+        #[cfg(not(rtc_v2wb))]
+        use stm32_metapac::rcc::vals::Rtcsel;
 
-            #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
-            let cr = crate::pac::PWR.cr();
-            #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
-            let cr = crate::pac::PWR.cr1();
+        #[cfg(any(rtc_v2f2, rtc_v2f3, rtc_v2l1))]
+        let cr = crate::pac::PWR.cr();
+        #[cfg(any(rtc_v2f4, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
+        let cr = crate::pac::PWR.cr1();
 
-            // TODO: Missing from PAC for l0 and f0?
-            #[cfg(not(any(rtc_v2f0, rtc_v2l0)))]
-            {
-                cr.modify(|w| w.set_dbp(true));
-                while !cr.read().dbp() {}
-            }
-
-            #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
-            let reg = crate::pac::RCC.bdcr().read();
-            #[cfg(any(rtc_v2l0, rtc_v2l1))]
-            let reg = crate::pac::RCC.csr().read();
-
-            #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))]
-            assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
-
-            #[cfg(rtc_v2wb)]
-            let rtcsel = reg.rtcsel();
-            #[cfg(not(rtc_v2wb))]
-            let rtcsel = reg.rtcsel().0;
-
-            if !reg.rtcen() || rtcsel != clock_config {
-                #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
-                crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
-
-                #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
-                let cr = crate::pac::RCC.bdcr();
-                #[cfg(any(rtc_v2l0, rtc_v2l1))]
-                let cr = crate::pac::RCC.csr();
-
-                cr.modify(|w| {
-                    // Reset
-                    #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
-                    w.set_bdrst(false);
-
-                    // Select RTC source
-                    #[cfg(not(rtc_v2wb))]
-                    w.set_rtcsel(Rtcsel(clock_config));
-                    #[cfg(rtc_v2wb)]
-                    w.set_rtcsel(clock_config);
-                    w.set_rtcen(true);
-
-                    // Restore bcdr
-                    #[cfg(any(rtc_v2l4, rtc_v2wb))]
-                    w.set_lscosel(reg.lscosel());
-                    #[cfg(any(rtc_v2l4, rtc_v2wb))]
-                    w.set_lscoen(reg.lscoen());
-
-                    w.set_lseon(reg.lseon());
-
-                    #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
-                    w.set_lsedrv(reg.lsedrv());
-                    w.set_lsebyp(reg.lsebyp());
-                });
-            }
+        // TODO: Missing from PAC for l0 and f0?
+        #[cfg(not(any(rtc_v2f0, rtc_v2l0)))]
+        {
+            cr.modify(|w| w.set_dbp(true));
+            while !cr.read().dbp() {}
         }
 
-        self.write(true, |rtc| unsafe {
+        #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
+        let reg = crate::pac::RCC.bdcr().read();
+        #[cfg(any(rtc_v2l0, rtc_v2l1))]
+        let reg = crate::pac::RCC.csr().read();
+
+        #[cfg(any(rtc_v2h7, rtc_v2l4, rtc_v2wb))]
+        assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+
+        #[cfg(rtc_v2wb)]
+        let rtcsel = reg.rtcsel();
+        #[cfg(not(rtc_v2wb))]
+        let rtcsel = reg.rtcsel().0;
+
+        if !reg.rtcen() || rtcsel != clock_config {
+            #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
+            crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+
+            #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
+            let cr = crate::pac::RCC.bdcr();
+            #[cfg(any(rtc_v2l0, rtc_v2l1))]
+            let cr = crate::pac::RCC.csr();
+
+            cr.modify(|w| {
+                // Reset
+                #[cfg(not(any(rtc_v2l0, rtc_v2l1)))]
+                w.set_bdrst(false);
+
+                // Select RTC source
+                #[cfg(not(rtc_v2wb))]
+                w.set_rtcsel(Rtcsel(clock_config));
+                #[cfg(rtc_v2wb)]
+                w.set_rtcsel(clock_config);
+                w.set_rtcen(true);
+
+                // Restore bcdr
+                #[cfg(any(rtc_v2l4, rtc_v2wb))]
+                w.set_lscosel(reg.lscosel());
+                #[cfg(any(rtc_v2l4, rtc_v2wb))]
+                w.set_lscoen(reg.lscoen());
+
+                w.set_lseon(reg.lseon());
+
+                #[cfg(any(rtc_v2f0, rtc_v2f7, rtc_v2h7, rtc_v2l4, rtc_v2wb))]
+                w.set_lsedrv(reg.lsedrv());
+                w.set_lsebyp(reg.lsebyp());
+            });
+        }
+
+        self.write(true, |rtc| {
             rtc.cr().modify(|w| {
                 #[cfg(rtc_v2f2)]
                 w.set_fmt(false);
@@ -117,47 +115,45 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
         clock_drift = clock_drift / RTC_CALR_RESOLUTION_PPM;
 
         self.write(false, |rtc| {
-            unsafe {
-                rtc.calr().write(|w| {
-                    match period {
-                        super::RtcCalibrationCyclePeriod::Seconds8 => {
-                            w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
-                        }
-                        super::RtcCalibrationCyclePeriod::Seconds16 => {
-                            w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
-                        }
-                        super::RtcCalibrationCyclePeriod::Seconds32 => {
-                            // Set neither `calw8` nor `calw16` to use 32 seconds
-                        }
+            rtc.calr().write(|w| {
+                match period {
+                    super::RtcCalibrationCyclePeriod::Seconds8 => {
+                        w.set_calw8(stm32_metapac::rtc::vals::Calw8::EIGHT_SECOND);
                     }
-
-                    // Extra pulses during calibration cycle period: CALP * 512 - CALM
-                    //
-                    // CALP sets whether pulses are added or omitted.
-                    //
-                    // CALM contains how many pulses (out of 512) are masked in a
-                    // given calibration cycle period.
-                    if clock_drift > 0.0 {
-                        // Maximum (about 512.2) rounds to 512.
-                        clock_drift += 0.5;
-
-                        // When the offset is positive (0 to 512), the opposite of
-                        // the offset (512 - offset) is masked, i.e. for the
-                        // maximum offset (512), 0 pulses are masked.
-                        w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
-                        w.set_calm(512 - clock_drift as u16);
-                    } else {
-                        // Minimum (about -510.7) rounds to -511.
-                        clock_drift -= 0.5;
-
-                        // When the offset is negative or zero (-511 to 0),
-                        // the absolute offset is masked, i.e. for the minimum
-                        // offset (-511), 511 pulses are masked.
-                        w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
-                        w.set_calm((clock_drift * -1.0) as u16);
+                    super::RtcCalibrationCyclePeriod::Seconds16 => {
+                        w.set_calw16(stm32_metapac::rtc::vals::Calw16::SIXTEEN_SECOND);
                     }
-                });
-            }
+                    super::RtcCalibrationCyclePeriod::Seconds32 => {
+                        // Set neither `calw8` nor `calw16` to use 32 seconds
+                    }
+                }
+
+                // Extra pulses during calibration cycle period: CALP * 512 - CALM
+                //
+                // CALP sets whether pulses are added or omitted.
+                //
+                // CALM contains how many pulses (out of 512) are masked in a
+                // given calibration cycle period.
+                if clock_drift > 0.0 {
+                    // Maximum (about 512.2) rounds to 512.
+                    clock_drift += 0.5;
+
+                    // When the offset is positive (0 to 512), the opposite of
+                    // the offset (512 - offset) is masked, i.e. for the
+                    // maximum offset (512), 0 pulses are masked.
+                    w.set_calp(stm32_metapac::rtc::vals::Calp::INCREASEFREQ);
+                    w.set_calm(512 - clock_drift as u16);
+                } else {
+                    // Minimum (about -510.7) rounds to -511.
+                    clock_drift -= 0.5;
+
+                    // When the offset is negative or zero (-511 to 0),
+                    // the absolute offset is masked, i.e. for the minimum
+                    // offset (-511), 511 pulses are masked.
+                    w.set_calp(stm32_metapac::rtc::vals::Calp::NOCHANGE);
+                    w.set_calm((clock_drift * -1.0) as u16);
+                }
+            });
         })
     }
 
@@ -168,31 +164,27 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
         let r = T::regs();
         // Disable write protection.
         // This is safe, as we're only writin the correct and expected values.
-        unsafe {
-            r.wpr().write(|w| w.set_key(0xca));
-            r.wpr().write(|w| w.set_key(0x53));
+        r.wpr().write(|w| w.set_key(0xca));
+        r.wpr().write(|w| w.set_key(0x53));
 
-            // true if initf bit indicates RTC peripheral is in init mode
-            if init_mode && !r.isr().read().initf() {
-                // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
-                r.isr().modify(|w| w.set_init(Init::INITMODE));
-                // wait till init state entered
-                // ~2 RTCCLK cycles
-                while !r.isr().read().initf() {}
-            }
+        // true if initf bit indicates RTC peripheral is in init mode
+        if init_mode && !r.isr().read().initf() {
+            // to update calendar date/time, time format, and prescaler configuration, RTC must be in init mode
+            r.isr().modify(|w| w.set_init(Init::INITMODE));
+            // wait till init state entered
+            // ~2 RTCCLK cycles
+            while !r.isr().read().initf() {}
         }
 
         let result = f(&r);
 
-        unsafe {
-            if init_mode {
-                r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
-            }
-
-            // Re-enable write protection.
-            // This is safe, as the field accepts the full range of 8-bit values.
-            r.wpr().write(|w| w.set_key(0xff));
+        if init_mode {
+            r.isr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
         }
+
+        // Re-enable write protection.
+        // This is safe, as the field accepts the full range of 8-bit values.
+        r.wpr().write(|w| w.set_key(0xff));
         result
     }
 }
@@ -200,7 +192,7 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
 impl sealed::Instance for crate::peripherals::RTC {
     const BACKUP_REGISTER_COUNT: usize = 20;
 
-    unsafe fn enable_peripheral_clk() {
+    fn enable_peripheral_clk() {
         #[cfg(any(rtc_v2l4, rtc_v2wb))]
         {
             // enable peripheral clock for communication
@@ -213,7 +205,7 @@ impl sealed::Instance for crate::peripherals::RTC {
 
     fn read_backup_register(rtc: &Rtc, register: usize) -> Option<u32> {
         if register < Self::BACKUP_REGISTER_COUNT {
-            Some(unsafe { rtc.bkpr(register).read().bkp() })
+            Some(rtc.bkpr(register).read().bkp())
         } else {
             None
         }
@@ -221,7 +213,7 @@ impl sealed::Instance for crate::peripherals::RTC {
 
     fn write_backup_register(rtc: &Rtc, register: usize, value: u32) {
         if register < Self::BACKUP_REGISTER_COUNT {
-            unsafe { rtc.bkpr(register).write(|w| w.set_bkp(value)) }
+            rtc.bkpr(register).write(|w| w.set_bkp(value));
         }
     }
 }
diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs
index 24f6496a6..7c91046a2 100644
--- a/embassy-stm32/src/rtc/v3.rs
+++ b/embassy-stm32/src/rtc/v3.rs
@@ -8,70 +8,66 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
     /// It this changes the RTC clock source the time will be reset
     pub(super) fn apply_config(&mut self, rtc_config: RtcConfig) {
         // Unlock the backup domain
-        unsafe {
-            #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))]
-            {
-                crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
-                while !crate::pac::PWR.cr1().read().dbp() {}
-            }
-            #[cfg(any(rcc_wl5, rcc_wle))]
-            {
-                use crate::pac::pwr::vals::Dbp;
+        #[cfg(not(any(rtc_v3u5, rcc_wl5, rcc_wle)))]
+        {
+            crate::pac::PWR.cr1().modify(|w| w.set_dbp(true));
+            while !crate::pac::PWR.cr1().read().dbp() {}
+        }
+        #[cfg(any(rcc_wl5, rcc_wle))]
+        {
+            use crate::pac::pwr::vals::Dbp;
 
-                crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED));
-                while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {}
-            }
+            crate::pac::PWR.cr1().modify(|w| w.set_dbp(Dbp::ENABLED));
+            while crate::pac::PWR.cr1().read().dbp() != Dbp::ENABLED {}
+        }
 
-            let reg = crate::pac::RCC.bdcr().read();
-            assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
+        let reg = crate::pac::RCC.bdcr().read();
+        assert!(!reg.lsecsson(), "RTC is not compatible with LSE CSS, yet.");
 
-            let config_rtcsel = rtc_config.clock_config as u8;
-            #[cfg(not(any(rcc_wl5, rcc_wle)))]
-            let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel);
+        let config_rtcsel = rtc_config.clock_config as u8;
+        #[cfg(not(any(rcc_wl5, rcc_wle)))]
+        let config_rtcsel = crate::pac::rcc::vals::Rtcsel(config_rtcsel);
 
-            if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
-                crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
+        if !reg.rtcen() || reg.rtcsel() != config_rtcsel {
+            crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true));
 
-                crate::pac::RCC.bdcr().modify(|w| {
-                    // Reset
-                    w.set_bdrst(false);
+            crate::pac::RCC.bdcr().modify(|w| {
+                // Reset
+                w.set_bdrst(false);
 
-                    // Select RTC source
-                    w.set_rtcsel(config_rtcsel);
+                // Select RTC source
+                w.set_rtcsel(config_rtcsel);
 
-                    w.set_rtcen(true);
+                w.set_rtcen(true);
 
-                    // Restore bcdr
-                    w.set_lscosel(reg.lscosel());
-                    w.set_lscoen(reg.lscoen());
+                // Restore bcdr
+                w.set_lscosel(reg.lscosel());
+                w.set_lscoen(reg.lscoen());
 
-                    w.set_lseon(reg.lseon());
-                    w.set_lsedrv(reg.lsedrv());
-                    w.set_lsebyp(reg.lsebyp());
-                });
-            }
+                w.set_lseon(reg.lseon());
+                w.set_lsedrv(reg.lsedrv());
+                w.set_lsebyp(reg.lsebyp());
+            });
         }
 
         self.write(true, |rtc| {
-            unsafe {
-                rtc.cr().modify(|w| {
-                    w.set_fmt(Fmt::TWENTYFOURHOUR);
-                    w.set_osel(Osel::DISABLED);
-                    w.set_pol(Pol::HIGH);
-                });
+            rtc.cr().modify(|w| {
+                w.set_fmt(Fmt::TWENTYFOURHOUR);
+                w.set_osel(Osel::DISABLED);
+                w.set_pol(Pol::HIGH);
+            });
 
-                rtc.prer().modify(|w| {
-                    w.set_prediv_s(rtc_config.sync_prescaler);
-                    w.set_prediv_a(rtc_config.async_prescaler);
-                });
+            rtc.prer().modify(|w| {
+                w.set_prediv_s(rtc_config.sync_prescaler);
+                w.set_prediv_a(rtc_config.async_prescaler);
+            });
 
-                // TODO: configuration for output pins
-                rtc.cr().modify(|w| {
-                    w.set_out2en(false);
-                    w.set_tampalrm_type(TampalrmType::PUSHPULL);
-                    w.set_tampalrm_pu(TampalrmPu::NOPULLUP);
-                });
-            }
+            // TODO: configuration for output pins
+            rtc.cr().modify(|w| {
+                w.set_out2en(false);
+                w.set_tampalrm_type(TampalrmType::PUSHPULL);
+                w.set_tampalrm_pu(TampalrmPu::NOPULLUP);
+            });
         });
 
         self.rtc_config = rtc_config;
@@ -99,47 +95,45 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
         clock_drift = clock_drift / Self::RTC_CALR_RESOLUTION_PPM;
 
         self.write(false, |rtc| {
-            unsafe {
-                rtc.calr().write(|w| {
-                    match period {
-                        RtcCalibrationCyclePeriod::Seconds8 => {
-                            w.set_calw8(Calw8::EIGHTSECONDS);
-                        }
-                        RtcCalibrationCyclePeriod::Seconds16 => {
-                            w.set_calw16(Calw16::SIXTEENSECONDS);
-                        }
-                        RtcCalibrationCyclePeriod::Seconds32 => {
-                            // Set neither `calw8` nor `calw16` to use 32 seconds
-                        }
+            rtc.calr().write(|w| {
+                match period {
+                    RtcCalibrationCyclePeriod::Seconds8 => {
+                        w.set_calw8(Calw8::EIGHTSECONDS);
                     }
-
-                    // Extra pulses during calibration cycle period: CALP * 512 - CALM
-                    //
-                    // CALP sets whether pulses are added or omitted.
-                    //
-                    // CALM contains how many pulses (out of 512) are masked in a
-                    // given calibration cycle period.
-                    if clock_drift > 0.0 {
-                        // Maximum (about 512.2) rounds to 512.
-                        clock_drift += 0.5;
-
-                        // When the offset is positive (0 to 512), the opposite of
-                        // the offset (512 - offset) is masked, i.e. for the
-                        // maximum offset (512), 0 pulses are masked.
-                        w.set_calp(Calp::INCREASEFREQ);
-                        w.set_calm(512 - clock_drift as u16);
-                    } else {
-                        // Minimum (about -510.7) rounds to -511.
-                        clock_drift -= 0.5;
-
-                        // When the offset is negative or zero (-511 to 0),
-                        // the absolute offset is masked, i.e. for the minimum
-                        // offset (-511), 511 pulses are masked.
-                        w.set_calp(Calp::NOCHANGE);
-                        w.set_calm((clock_drift * -1.0) as u16);
+                    RtcCalibrationCyclePeriod::Seconds16 => {
+                        w.set_calw16(Calw16::SIXTEENSECONDS);
                     }
-                });
-            }
+                    RtcCalibrationCyclePeriod::Seconds32 => {
+                        // Set neither `calw8` nor `calw16` to use 32 seconds
+                    }
+                }
+
+                // Extra pulses during calibration cycle period: CALP * 512 - CALM
+                //
+                // CALP sets whether pulses are added or omitted.
+                //
+                // CALM contains how many pulses (out of 512) are masked in a
+                // given calibration cycle period.
+                if clock_drift > 0.0 {
+                    // Maximum (about 512.2) rounds to 512.
+                    clock_drift += 0.5;
+
+                    // When the offset is positive (0 to 512), the opposite of
+                    // the offset (512 - offset) is masked, i.e. for the
+                    // maximum offset (512), 0 pulses are masked.
+                    w.set_calp(Calp::INCREASEFREQ);
+                    w.set_calm(512 - clock_drift as u16);
+                } else {
+                    // Minimum (about -510.7) rounds to -511.
+                    clock_drift -= 0.5;
+
+                    // When the offset is negative or zero (-511 to 0),
+                    // the absolute offset is masked, i.e. for the minimum
+                    // offset (-511), 511 pulses are masked.
+                    w.set_calp(Calp::NOCHANGE);
+                    w.set_calm((clock_drift * -1.0) as u16);
+                }
+            });
         })
     }
 
@@ -150,29 +144,26 @@ impl<'d, T: Instance> super::Rtc<'d, T> {
         let r = T::regs();
         // Disable write protection.
         // This is safe, as we're only writin the correct and expected values.
-        unsafe {
-            r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));
-            r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));
+        r.wpr().write(|w| w.set_key(Key::DEACTIVATE1));
+        r.wpr().write(|w| w.set_key(Key::DEACTIVATE2));
 
-            if init_mode && !r.icsr().read().initf() {
-                r.icsr().modify(|w| w.set_init(Init::INITMODE));
-                // wait till init state entered
-                // ~2 RTCCLK cycles
-                while !r.icsr().read().initf() {}
-            }
+        if init_mode && !r.icsr().read().initf() {
+            r.icsr().modify(|w| w.set_init(Init::INITMODE));
+            // wait till init state entered
+            // ~2 RTCCLK cycles
+            while !r.icsr().read().initf() {}
         }
 
         let result = f(&r);
 
-        unsafe {
-            if init_mode {
-                r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
-            }
-
-            // Re-enable write protection.
-            // This is safe, as the field accepts the full range of 8-bit values.
-            r.wpr().write(|w| w.set_key(Key::ACTIVATE));
+        if init_mode {
+            r.icsr().modify(|w| w.set_init(Init::FREERUNNINGMODE)); // Exits init mode
         }
+
+        // Re-enable write protection.
+        // This is safe, as the field accepts the full range of 8-bit values.
+        r.wpr().write(|w| w.set_key(Key::ACTIVATE));
+
         result
     }
 }
@@ -192,7 +183,7 @@ impl sealed::Instance for crate::peripherals::RTC {
     fn write_backup_register(_rtc: &Rtc, register: usize, _value: u32) {
         if register < Self::BACKUP_REGISTER_COUNT {
             // RTC3 backup registers come from the TAMP peripe=heral, not RTC. Not() even in the L412 PAC
-            //unsafe { self.rtc.bkpr()[register].write(|w| w.bits(value)) }
+            //self.rtc.bkpr()[register].write(|w| w.bits(value))
         }
     }
 }
diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs
index 28eb49ab6..80a336a48 100644
--- a/embassy-stm32/src/sdmmc/mod.rs
+++ b/embassy-stm32/src/sdmmc/mod.rs
@@ -28,17 +28,14 @@ pub struct InterruptHandler<T: Instance> {
 impl<T: Instance> InterruptHandler<T> {
     fn data_interrupts(enable: bool) {
         let regs = T::regs();
-        // NOTE(unsafe) Atomic write
-        unsafe {
-            regs.maskr().write(|w| {
-                w.set_dcrcfailie(enable);
-                w.set_dtimeoutie(enable);
-                w.set_dataendie(enable);
+        regs.maskr().write(|w| {
+            w.set_dcrcfailie(enable);
+            w.set_dtimeoutie(enable);
+            w.set_dataendie(enable);
 
-                #[cfg(sdmmc_v2)]
-                w.set_dabortie(enable);
-            });
-        }
+            #[cfg(sdmmc_v2)]
+            w.set_dabortie(enable);
+        });
     }
 }
 
@@ -285,7 +282,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
     ) -> Self {
         into_ref!(clk, cmd, d0);
 
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
             cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
             d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
@@ -322,7 +319,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T>> Sdmmc<'d, T, Dma> {
     ) -> Self {
         into_ref!(clk, cmd, d0, d1, d2, d3);
 
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
             cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
             d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
@@ -364,7 +361,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
     ) -> Self {
         into_ref!(clk, cmd, d0);
 
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
             cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
             d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
@@ -400,7 +397,7 @@ impl<'d, T: Instance> Sdmmc<'d, T, NoDma> {
     ) -> Self {
         into_ref!(clk, cmd, d0, d1, d2, d3);
 
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             clk.set_as_af_pull(clk.af_num(), AFType::OutputPushPull, Pull::None);
             cmd.set_as_af_pull(cmd.af_num(), AFType::OutputPushPull, Pull::Up);
             d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::Up);
@@ -451,26 +448,24 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
         unsafe { T::Interrupt::enable() };
 
         let regs = T::regs();
-        unsafe {
-            regs.clkcr().write(|w| {
-                w.set_pwrsav(false);
-                w.set_negedge(false);
+        regs.clkcr().write(|w| {
+            w.set_pwrsav(false);
+            w.set_negedge(false);
 
-                // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors.
-                // See chip erratas for more details.
-                #[cfg(sdmmc_v1)]
-                w.set_hwfc_en(false);
-                #[cfg(sdmmc_v2)]
-                w.set_hwfc_en(true);
+            // Hardware flow control is broken on SDIOv1 and causes clock glitches, which result in CRC errors.
+            // See chip erratas for more details.
+            #[cfg(sdmmc_v1)]
+            w.set_hwfc_en(false);
+            #[cfg(sdmmc_v2)]
+            w.set_hwfc_en(true);
 
-                #[cfg(sdmmc_v1)]
-                w.set_clken(true);
-            });
+            #[cfg(sdmmc_v1)]
+            w.set_clken(true);
+        });
 
-            // Power off, writen 00: Clock to the card is stopped;
-            // D[7:0], CMD, and CK are driven high.
-            regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8));
-        }
+        // Power off, writen 00: Clock to the card is stopped;
+        // D[7:0], CMD, and CK are driven high.
+        regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::Off as u8));
 
         Self {
             _peri: sdmmc,
@@ -495,14 +490,11 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
     fn data_active() -> bool {
         let regs = T::regs();
 
-        // NOTE(unsafe) Atomic read with no side-effects
-        unsafe {
-            let status = regs.star().read();
-            #[cfg(sdmmc_v1)]
-            return status.rxact() || status.txact();
-            #[cfg(sdmmc_v2)]
-            return status.dpsmact();
-        }
+        let status = regs.star().read();
+        #[cfg(sdmmc_v1)]
+        return status.rxact() || status.txact();
+        #[cfg(sdmmc_v2)]
+        return status.dpsmact();
     }
 
     /// Coammand transfer is in progress
@@ -510,14 +502,11 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
     fn cmd_active() -> bool {
         let regs = T::regs();
 
-        // NOTE(unsafe) Atomic read with no side-effects
-        unsafe {
-            let status = regs.star().read();
-            #[cfg(sdmmc_v1)]
-            return status.cmdact();
-            #[cfg(sdmmc_v2)]
-            return status.cpsmact();
-        }
+        let status = regs.star().read();
+        #[cfg(sdmmc_v1)]
+        return status.cmdact();
+        #[cfg(sdmmc_v2)]
+        return status.cpsmact();
     }
 
     /// Wait idle on CMDACT, RXACT and TXACT (v1) or DOSNACT and CPSMACT (v2)
@@ -542,44 +531,41 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
         Self::wait_idle();
         Self::clear_interrupt_flags();
 
-        // NOTE(unsafe) We have exclusive access to the regisers
-        unsafe {
-            regs.dtimer()
-                .write(|w| w.set_datatime(self.config.data_transfer_timeout));
-            regs.dlenr().write(|w| w.set_datalength(length_bytes));
+        regs.dtimer()
+            .write(|w| w.set_datatime(self.config.data_transfer_timeout));
+        regs.dlenr().write(|w| w.set_datalength(length_bytes));
 
+        #[cfg(sdmmc_v1)]
+        let transfer = unsafe {
+            let request = self.dma.request();
+            Transfer::new_read(
+                &mut self.dma,
+                request,
+                regs.fifor().as_ptr() as *mut u32,
+                buffer,
+                DMA_TRANSFER_OPTIONS,
+            )
+        };
+        #[cfg(sdmmc_v2)]
+        let transfer = {
+            regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32));
+            regs.idmactrlr().modify(|w| w.set_idmaen(true));
+            Transfer {
+                _dummy: core::marker::PhantomData,
+            }
+        };
+
+        regs.dctrl().modify(|w| {
+            w.set_dblocksize(block_size);
+            w.set_dtdir(true);
             #[cfg(sdmmc_v1)]
-            let transfer = {
-                let request = self.dma.request();
-                Transfer::new_read(
-                    &mut self.dma,
-                    request,
-                    regs.fifor().ptr() as *mut u32,
-                    buffer,
-                    DMA_TRANSFER_OPTIONS,
-                )
-            };
-            #[cfg(sdmmc_v2)]
-            let transfer = {
-                regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_mut_ptr() as u32));
-                regs.idmactrlr().modify(|w| w.set_idmaen(true));
-                Transfer {
-                    _dummy: core::marker::PhantomData,
-                }
-            };
+            {
+                w.set_dmaen(true);
+                w.set_dten(true);
+            }
+        });
 
-            regs.dctrl().modify(|w| {
-                w.set_dblocksize(block_size);
-                w.set_dtdir(true);
-                #[cfg(sdmmc_v1)]
-                {
-                    w.set_dmaen(true);
-                    w.set_dten(true);
-                }
-            });
-
-            transfer
-        }
+        transfer
     }
 
     /// # Safety
@@ -598,59 +584,54 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
         Self::wait_idle();
         Self::clear_interrupt_flags();
 
-        // NOTE(unsafe) We have exclusive access to the regisers
-        unsafe {
-            regs.dtimer()
-                .write(|w| w.set_datatime(self.config.data_transfer_timeout));
-            regs.dlenr().write(|w| w.set_datalength(length_bytes));
+        regs.dtimer()
+            .write(|w| w.set_datatime(self.config.data_transfer_timeout));
+        regs.dlenr().write(|w| w.set_datalength(length_bytes));
 
+        #[cfg(sdmmc_v1)]
+        let transfer = unsafe {
+            let request = self.dma.request();
+            Transfer::new_write(
+                &mut self.dma,
+                request,
+                buffer,
+                regs.fifor().as_ptr() as *mut u32,
+                DMA_TRANSFER_OPTIONS,
+            )
+        };
+        #[cfg(sdmmc_v2)]
+        let transfer = {
+            regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32));
+            regs.idmactrlr().modify(|w| w.set_idmaen(true));
+            Transfer {
+                _dummy: core::marker::PhantomData,
+            }
+        };
+
+        regs.dctrl().modify(|w| {
+            w.set_dblocksize(block_size);
+            w.set_dtdir(false);
             #[cfg(sdmmc_v1)]
-            let transfer = {
-                let request = self.dma.request();
-                Transfer::new_write(
-                    &mut self.dma,
-                    request,
-                    buffer,
-                    regs.fifor().ptr() as *mut u32,
-                    DMA_TRANSFER_OPTIONS,
-                )
-            };
-            #[cfg(sdmmc_v2)]
-            let transfer = {
-                regs.idmabase0r().write(|w| w.set_idmabase0(buffer.as_ptr() as u32));
-                regs.idmactrlr().modify(|w| w.set_idmaen(true));
-                Transfer {
-                    _dummy: core::marker::PhantomData,
-                }
-            };
+            {
+                w.set_dmaen(true);
+                w.set_dten(true);
+            }
+        });
 
-            regs.dctrl().modify(|w| {
-                w.set_dblocksize(block_size);
-                w.set_dtdir(false);
-                #[cfg(sdmmc_v1)]
-                {
-                    w.set_dmaen(true);
-                    w.set_dten(true);
-                }
-            });
-
-            transfer
-        }
+        transfer
     }
 
     /// Stops the DMA datapath
     fn stop_datapath() {
         let regs = T::regs();
 
-        unsafe {
-            #[cfg(sdmmc_v1)]
-            regs.dctrl().modify(|w| {
-                w.set_dmaen(false);
-                w.set_dten(false);
-            });
-            #[cfg(sdmmc_v2)]
-            regs.idmactrlr().modify(|w| w.set_idmaen(false));
-        }
+        #[cfg(sdmmc_v1)]
+        regs.dctrl().modify(|w| {
+            w.set_dmaen(false);
+            w.set_dten(false);
+        });
+        #[cfg(sdmmc_v2)]
+        regs.idmactrlr().modify(|w| w.set_idmaen(false));
     }
 
     /// Sets the CLKDIV field in CLKCR. Updates clock field in self
@@ -673,16 +654,13 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
         assert!(ker_ck.0 > 3 * sdmmc_bus_bandwidth / 32);
         self.clock = new_clock;
 
-        // NOTE(unsafe) We have exclusive access to the regblock
-        unsafe {
-            // CPSMACT and DPSMACT must be 0 to set CLKDIV
-            Self::wait_idle();
-            regs.clkcr().modify(|w| {
-                w.set_clkdiv(clkdiv);
-                #[cfg(sdmmc_v1)]
-                w.set_bypass(_bypass);
-            });
-        }
+        // CPSMACT and DPSMACT must be 0 to set CLKDIV
+        Self::wait_idle();
+        regs.clkcr().modify(|w| {
+            w.set_clkdiv(clkdiv);
+            #[cfg(sdmmc_v1)]
+            w.set_bypass(_bypass);
+        });
 
         Ok(())
     }
@@ -710,7 +688,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         // Arm `OnDrop` after the buffer, so it will be dropped first
         let regs = T::regs();
-        let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
+        let on_drop = OnDrop::new(|| Self::on_drop());
 
         let transfer = self.prepare_datapath_read(&mut status, 64, 6);
         InterruptHandler::<T>::data_interrupts(true);
@@ -718,7 +696,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         let res = poll_fn(|cx| {
             T::state().register(cx.waker());
-            let status = unsafe { regs.star().read() };
+            let status = regs.star().read();
 
             if status.dcrcfail() {
                 return Poll::Ready(Err(Error::Crc));
@@ -769,8 +747,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         Self::cmd(Cmd::card_status(rca << 16), false)?; // CMD13
 
-        // NOTE(unsafe) Atomic read with no side-effects
-        let r1 = unsafe { regs.respr(0).read().cardstatus() };
+        let r1 = regs.respr(0).read().cardstatus();
         Ok(r1.into())
     }
 
@@ -786,7 +763,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         // Arm `OnDrop` after the buffer, so it will be dropped first
         let regs = T::regs();
-        let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
+        let on_drop = OnDrop::new(|| Self::on_drop());
 
         let transfer = self.prepare_datapath_read(&mut status, 64, 6);
         InterruptHandler::<T>::data_interrupts(true);
@@ -794,7 +771,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         let res = poll_fn(|cx| {
             T::state().register(cx.waker());
-            let status = unsafe { regs.star().read() };
+            let status = regs.star().read();
 
             if status.dcrcfail() {
                 return Poll::Ready(Err(Error::Crc));
@@ -840,35 +817,32 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
     #[inline(always)]
     fn clear_interrupt_flags() {
         let regs = T::regs();
-        // NOTE(unsafe) Atomic write
-        unsafe {
-            regs.icr().write(|w| {
-                w.set_ccrcfailc(true);
-                w.set_dcrcfailc(true);
-                w.set_ctimeoutc(true);
-                w.set_dtimeoutc(true);
-                w.set_txunderrc(true);
-                w.set_rxoverrc(true);
-                w.set_cmdrendc(true);
-                w.set_cmdsentc(true);
-                w.set_dataendc(true);
-                w.set_dbckendc(true);
-                w.set_sdioitc(true);
+        regs.icr().write(|w| {
+            w.set_ccrcfailc(true);
+            w.set_dcrcfailc(true);
+            w.set_ctimeoutc(true);
+            w.set_dtimeoutc(true);
+            w.set_txunderrc(true);
+            w.set_rxoverrc(true);
+            w.set_cmdrendc(true);
+            w.set_cmdsentc(true);
+            w.set_dataendc(true);
+            w.set_dbckendc(true);
+            w.set_sdioitc(true);
 
-                #[cfg(sdmmc_v2)]
-                {
-                    w.set_dholdc(true);
-                    w.set_dabortc(true);
-                    w.set_busyd0endc(true);
-                    w.set_ackfailc(true);
-                    w.set_acktimeoutc(true);
-                    w.set_vswendc(true);
-                    w.set_ckstopc(true);
-                    w.set_idmatec(true);
-                    w.set_idmabtcc(true);
-                }
-            });
-        }
+            #[cfg(sdmmc_v2)]
+            {
+                w.set_dholdc(true);
+                w.set_dabortc(true);
+                w.set_busyd0endc(true);
+                w.set_ackfailc(true);
+                w.set_acktimeoutc(true);
+                w.set_vswendc(true);
+                w.set_ckstopc(true);
+                w.set_idmatec(true);
+                w.set_idmabtcc(true);
+            }
+        });
     }
 
     async fn get_scr(&mut self, card: &mut Card) -> Result<(), Error> {
@@ -880,7 +854,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         // Arm `OnDrop` after the buffer, so it will be dropped first
         let regs = T::regs();
-        let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
+        let on_drop = OnDrop::new(|| Self::on_drop());
 
         let transfer = self.prepare_datapath_read(&mut scr[..], 8, 3);
         InterruptHandler::<T>::data_interrupts(true);
@@ -888,7 +862,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         let res = poll_fn(|cx| {
             T::state().register(cx.waker());
-            let status = unsafe { regs.star().read() };
+            let status = regs.star().read();
 
             if status.dcrcfail() {
                 return Poll::Ready(Err(Error::Crc));
@@ -921,59 +895,53 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
         let regs = T::regs();
 
         Self::clear_interrupt_flags();
-        // NOTE(safety) Atomic operations
-        unsafe {
-            // CP state machine must be idle
-            while Self::cmd_active() {}
+        // CP state machine must be idle
+        while Self::cmd_active() {}
 
-            // Command arg
-            regs.argr().write(|w| w.set_cmdarg(cmd.arg));
+        // Command arg
+        regs.argr().write(|w| w.set_cmdarg(cmd.arg));
 
-            // Command index and start CP State Machine
-            regs.cmdr().write(|w| {
-                w.set_waitint(false);
-                w.set_waitresp(cmd.resp as u8);
-                w.set_cmdindex(cmd.cmd);
-                w.set_cpsmen(true);
+        // Command index and start CP State Machine
+        regs.cmdr().write(|w| {
+            w.set_waitint(false);
+            w.set_waitresp(cmd.resp as u8);
+            w.set_cmdindex(cmd.cmd);
+            w.set_cpsmen(true);
 
-                #[cfg(sdmmc_v2)]
-                {
-                    // Special mode in CP State Machine
-                    // CMD12: Stop Transmission
-                    let cpsm_stop_transmission = cmd.cmd == 12;
-                    w.set_cmdstop(cpsm_stop_transmission);
-                    w.set_cmdtrans(data);
-                }
-            });
-
-            let mut status;
-            if cmd.resp == Response::None {
-                // Wait for CMDSENT or a timeout
-                while {
-                    status = regs.star().read();
-                    !(status.ctimeout() || status.cmdsent())
-                } {}
-            } else {
-                // Wait for CMDREND or CCRCFAIL or a timeout
-                while {
-                    status = regs.star().read();
-                    !(status.ctimeout() || status.cmdrend() || status.ccrcfail())
-                } {}
+            #[cfg(sdmmc_v2)]
+            {
+                // Special mode in CP State Machine
+                // CMD12: Stop Transmission
+                let cpsm_stop_transmission = cmd.cmd == 12;
+                w.set_cmdstop(cpsm_stop_transmission);
+                w.set_cmdtrans(data);
             }
+        });
 
-            if status.ctimeout() {
-                return Err(Error::Timeout);
-            } else if status.ccrcfail() {
-                return Err(Error::Crc);
-            }
-            Ok(())
+        let mut status;
+        if cmd.resp == Response::None {
+            // Wait for CMDSENT or a timeout
+            while {
+                status = regs.star().read();
+                !(status.ctimeout() || status.cmdsent())
+            } {}
+        } else {
+            // Wait for CMDREND or CCRCFAIL or a timeout
+            while {
+                status = regs.star().read();
+                !(status.ctimeout() || status.cmdrend() || status.ccrcfail())
+            } {}
         }
+
+        if status.ctimeout() {
+            return Err(Error::Timeout);
+        } else if status.ccrcfail() {
+            return Err(Error::Crc);
+        }
+        Ok(())
     }
 
-    /// # Safety
-    ///
-    /// Ensure that `regs` has exclusive access to the regblocks
-    unsafe fn on_drop() {
+    fn on_drop() {
         let regs = T::regs();
         if Self::data_active() {
             Self::clear_interrupt_flags();
@@ -1017,141 +985,138 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
             false => BusWidth::One,
         };
 
-        // NOTE(unsafe) We have exclusive access to the peripheral
-        unsafe {
-            // While the SD/SDIO card or eMMC is in identification mode,
-            // the SDMMC_CK frequency must be no more than 400 kHz.
-            let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0));
-            self.clock = init_clock;
+        // While the SD/SDIO card or eMMC is in identification mode,
+        // the SDMMC_CK frequency must be no more than 400 kHz.
+        let (_bypass, clkdiv, init_clock) = unwrap!(clk_div(ker_ck, SD_INIT_FREQ.0));
+        self.clock = init_clock;
 
-            // CPSMACT and DPSMACT must be 0 to set WIDBUS
-            Self::wait_idle();
+        // CPSMACT and DPSMACT must be 0 to set WIDBUS
+        Self::wait_idle();
 
-            regs.clkcr().modify(|w| {
-                w.set_widbus(0);
-                w.set_clkdiv(clkdiv);
-                #[cfg(sdmmc_v1)]
-                w.set_bypass(_bypass);
-            });
+        regs.clkcr().modify(|w| {
+            w.set_widbus(0);
+            w.set_clkdiv(clkdiv);
+            #[cfg(sdmmc_v1)]
+            w.set_bypass(_bypass);
+        });
 
-            regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
-            Self::cmd(Cmd::idle(), false)?;
+        regs.power().modify(|w| w.set_pwrctrl(PowerCtrl::On as u8));
+        Self::cmd(Cmd::idle(), false)?;
 
-            // Check if cards supports CMD8 (with pattern)
-            Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?;
-            let r1 = regs.respr(0).read().cardstatus();
+        // Check if cards supports CMD8 (with pattern)
+        Self::cmd(Cmd::hs_send_ext_csd(0x1AA), false)?;
+        let r1 = regs.respr(0).read().cardstatus();
 
-            let mut card = if r1 == 0x1AA {
-                // Card echoed back the pattern. Must be at least v2
-                Card::default()
-            } else {
-                return Err(Error::UnsupportedCardVersion);
-            };
+        let mut card = if r1 == 0x1AA {
+            // Card echoed back the pattern. Must be at least v2
+            Card::default()
+        } else {
+            return Err(Error::UnsupportedCardVersion);
+        };
 
-            let ocr = loop {
-                // Signal that next command is a app command
-                Self::cmd(Cmd::app_cmd(0), false)?; // CMD55
+        let ocr = loop {
+            // Signal that next command is a app command
+            Self::cmd(Cmd::app_cmd(0), false)?; // CMD55
 
-                let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32
-                    | CmdAppOper::HIGH_CAPACITY as u32
-                    | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32;
+            let arg = CmdAppOper::VOLTAGE_WINDOW_SD as u32
+                | CmdAppOper::HIGH_CAPACITY as u32
+                | CmdAppOper::SD_SWITCH_1_8V_CAPACITY as u32;
 
-                // Initialize card
-                match Self::cmd(Cmd::app_op_cmd(arg), false) {
-                    // ACMD41
-                    Ok(_) => (),
-                    Err(Error::Crc) => (),
-                    Err(err) => return Err(err),
-                }
-                let ocr: OCR = regs.respr(0).read().cardstatus().into();
-                if !ocr.is_busy() {
-                    // Power up done
-                    break ocr;
-                }
-            };
-
-            if ocr.high_capacity() {
-                // Card is SDHC or SDXC or SDUC
-                card.card_type = CardCapacity::SDHC;
-            } else {
-                card.card_type = CardCapacity::SDSC;
+            // Initialize card
+            match Self::cmd(Cmd::app_op_cmd(arg), false) {
+                // ACMD41
+                Ok(_) => (),
+                Err(Error::Crc) => (),
+                Err(err) => return Err(err),
             }
-            card.ocr = ocr;
-
-            Self::cmd(Cmd::all_send_cid(), false)?; // CMD2
-            let cid0 = regs.respr(0).read().cardstatus() as u128;
-            let cid1 = regs.respr(1).read().cardstatus() as u128;
-            let cid2 = regs.respr(2).read().cardstatus() as u128;
-            let cid3 = regs.respr(3).read().cardstatus() as u128;
-            let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3);
-            card.cid = cid.into();
-
-            Self::cmd(Cmd::send_rel_addr(), false)?;
-            card.rca = regs.respr(0).read().cardstatus() >> 16;
-
-            Self::cmd(Cmd::send_csd(card.rca << 16), false)?;
-            let csd0 = regs.respr(0).read().cardstatus() as u128;
-            let csd1 = regs.respr(1).read().cardstatus() as u128;
-            let csd2 = regs.respr(2).read().cardstatus() as u128;
-            let csd3 = regs.respr(3).read().cardstatus() as u128;
-            let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3);
-            card.csd = csd.into();
-
-            self.select_card(Some(&card))?;
-
-            self.get_scr(&mut card).await?;
-
-            // Set bus width
-            let (width, acmd_arg) = match bus_width {
-                BusWidth::Eight => unimplemented!(),
-                BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2),
-                _ => (BusWidth::One, 0),
-            };
-            Self::cmd(Cmd::app_cmd(card.rca << 16), false)?;
-            Self::cmd(Cmd::cmd6(acmd_arg), false)?;
-
-            // CPSMACT and DPSMACT must be 0 to set WIDBUS
-            Self::wait_idle();
-
-            regs.clkcr().modify(|w| {
-                w.set_widbus(match width {
-                    BusWidth::One => 0,
-                    BusWidth::Four => 1,
-                    BusWidth::Eight => 2,
-                    _ => panic!("Invalid Bus Width"),
-                })
-            });
-
-            // Set Clock
-            if freq.0 <= 25_000_000 {
-                // Final clock frequency
-                self.clkcr_set_clkdiv(freq.0, width)?;
-            } else {
-                // Switch to max clock for SDR12
-                self.clkcr_set_clkdiv(25_000_000, width)?;
+            let ocr: OCR = regs.respr(0).read().cardstatus().into();
+            if !ocr.is_busy() {
+                // Power up done
+                break ocr;
             }
+        };
 
-            self.card = Some(card);
-
-            // Read status
-            self.read_sd_status().await?;
-
-            if freq.0 > 25_000_000 {
-                // Switch to SDR25
-                self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?;
-
-                if self.signalling == Signalling::SDR25 {
-                    // Set final clock frequency
-                    self.clkcr_set_clkdiv(freq.0, width)?;
-
-                    if self.read_status(&card)?.state() != CurrentState::Transfer {
-                        return Err(Error::SignalingSwitchFailed);
-                    }
-                }
-            }
-            // Read status after signalling change
-            self.read_sd_status().await?;
+        if ocr.high_capacity() {
+            // Card is SDHC or SDXC or SDUC
+            card.card_type = CardCapacity::SDHC;
+        } else {
+            card.card_type = CardCapacity::SDSC;
         }
+        card.ocr = ocr;
+
+        Self::cmd(Cmd::all_send_cid(), false)?; // CMD2
+        let cid0 = regs.respr(0).read().cardstatus() as u128;
+        let cid1 = regs.respr(1).read().cardstatus() as u128;
+        let cid2 = regs.respr(2).read().cardstatus() as u128;
+        let cid3 = regs.respr(3).read().cardstatus() as u128;
+        let cid = (cid0 << 96) | (cid1 << 64) | (cid2 << 32) | (cid3);
+        card.cid = cid.into();
+
+        Self::cmd(Cmd::send_rel_addr(), false)?;
+        card.rca = regs.respr(0).read().cardstatus() >> 16;
+
+        Self::cmd(Cmd::send_csd(card.rca << 16), false)?;
+        let csd0 = regs.respr(0).read().cardstatus() as u128;
+        let csd1 = regs.respr(1).read().cardstatus() as u128;
+        let csd2 = regs.respr(2).read().cardstatus() as u128;
+        let csd3 = regs.respr(3).read().cardstatus() as u128;
+        let csd = (csd0 << 96) | (csd1 << 64) | (csd2 << 32) | (csd3);
+        card.csd = csd.into();
+
+        self.select_card(Some(&card))?;
+
+        self.get_scr(&mut card).await?;
+
+        // Set bus width
+        let (width, acmd_arg) = match bus_width {
+            BusWidth::Eight => unimplemented!(),
+            BusWidth::Four if card.scr.bus_width_four() => (BusWidth::Four, 2),
+            _ => (BusWidth::One, 0),
+        };
+        Self::cmd(Cmd::app_cmd(card.rca << 16), false)?;
+        Self::cmd(Cmd::cmd6(acmd_arg), false)?;
+
+        // CPSMACT and DPSMACT must be 0 to set WIDBUS
+        Self::wait_idle();
+
+        regs.clkcr().modify(|w| {
+            w.set_widbus(match width {
+                BusWidth::One => 0,
+                BusWidth::Four => 1,
+                BusWidth::Eight => 2,
+                _ => panic!("Invalid Bus Width"),
+            })
+        });
+
+        // Set Clock
+        if freq.0 <= 25_000_000 {
+            // Final clock frequency
+            self.clkcr_set_clkdiv(freq.0, width)?;
+        } else {
+            // Switch to max clock for SDR12
+            self.clkcr_set_clkdiv(25_000_000, width)?;
+        }
+
+        self.card = Some(card);
+
+        // Read status
+        self.read_sd_status().await?;
+
+        if freq.0 > 25_000_000 {
+            // Switch to SDR25
+            self.signalling = self.switch_signalling_mode(Signalling::SDR25).await?;
+
+            if self.signalling == Signalling::SDR25 {
+                // Set final clock frequency
+                self.clkcr_set_clkdiv(freq.0, width)?;
+
+                if self.read_status(&card)?.state() != CurrentState::Transfer {
+                    return Err(Error::SignalingSwitchFailed);
+                }
+            }
+        }
+        // Read status after signalling change
+        self.read_sd_status().await?;
 
         Ok(())
     }
@@ -1172,7 +1137,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
         Self::cmd(Cmd::set_block_length(512), false)?; // CMD16
 
         let regs = T::regs();
-        let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
+        let on_drop = OnDrop::new(|| Self::on_drop());
 
         let transfer = self.prepare_datapath_read(buffer, 512, 9);
         InterruptHandler::<T>::data_interrupts(true);
@@ -1180,7 +1145,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         let res = poll_fn(|cx| {
             T::state().register(cx.waker());
-            let status = unsafe { regs.star().read() };
+            let status = regs.star().read();
 
             if status.dcrcfail() {
                 return Poll::Ready(Err(Error::Crc));
@@ -1217,7 +1182,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
         Self::cmd(Cmd::set_block_length(512), false)?; // CMD16
 
         let regs = T::regs();
-        let on_drop = OnDrop::new(|| unsafe { Self::on_drop() });
+        let on_drop = OnDrop::new(|| Self::on_drop());
 
         // sdmmc_v1 uses different cmd/dma order than v2, but only for writes
         #[cfg(sdmmc_v1)]
@@ -1231,7 +1196,7 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 
         let res = poll_fn(|cx| {
             T::state().register(cx.waker());
-            let status = unsafe { regs.star().read() };
+            let status = regs.star().read();
 
             if status.dcrcfail() {
                 return Poll::Ready(Err(Error::Crc));
@@ -1289,9 +1254,9 @@ impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Sdmmc<'d, T, Dma> {
 impl<'d, T: Instance, Dma: SdmmcDma<T> + 'd> Drop for Sdmmc<'d, T, Dma> {
     fn drop(&mut self) {
         T::Interrupt::disable();
-        unsafe { Self::on_drop() };
+        Self::on_drop();
 
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             self.clk.set_as_disconnected();
             self.cmd.set_as_disconnected();
             self.d0.set_as_disconnected();
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index 580971e45..1cddac992 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -98,14 +98,12 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
             Polarity::IdleHigh => Pull::Up,
         };
 
-        unsafe {
-            sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode);
-            sck.set_speed(crate::gpio::Speed::VeryHigh);
-            mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
-            mosi.set_speed(crate::gpio::Speed::VeryHigh);
-            miso.set_as_af(miso.af_num(), AFType::Input);
-            miso.set_speed(crate::gpio::Speed::VeryHigh);
-        }
+        sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, sck_pull_mode);
+        sck.set_speed(crate::gpio::Speed::VeryHigh);
+        mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
+        mosi.set_speed(crate::gpio::Speed::VeryHigh);
+        miso.set_as_af(miso.af_num(), AFType::Input);
+        miso.set_speed(crate::gpio::Speed::VeryHigh);
 
         Self::new_inner(
             peri,
@@ -129,12 +127,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         config: Config,
     ) -> Self {
         into_ref!(sck, miso);
-        unsafe {
-            sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
-            sck.set_speed(crate::gpio::Speed::VeryHigh);
-            miso.set_as_af(miso.af_num(), AFType::Input);
-            miso.set_speed(crate::gpio::Speed::VeryHigh);
-        }
+        sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
+        sck.set_speed(crate::gpio::Speed::VeryHigh);
+        miso.set_as_af(miso.af_num(), AFType::Input);
+        miso.set_speed(crate::gpio::Speed::VeryHigh);
 
         Self::new_inner(
             peri,
@@ -158,12 +154,10 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         config: Config,
     ) -> Self {
         into_ref!(sck, mosi);
-        unsafe {
-            sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
-            sck.set_speed(crate::gpio::Speed::VeryHigh);
-            mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
-            mosi.set_speed(crate::gpio::Speed::VeryHigh);
-        }
+        sck.set_as_af(sck.af_num(), AFType::OutputPushPull);
+        sck.set_speed(crate::gpio::Speed::VeryHigh);
+        mosi.set_as_af(mosi.af_num(), AFType::OutputPushPull);
+        mosi.set_speed(crate::gpio::Speed::VeryHigh);
 
         Self::new_inner(
             peri,
@@ -186,10 +180,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         config: Config,
     ) -> Self {
         into_ref!(mosi);
-        unsafe {
-            mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down);
-            mosi.set_speed(crate::gpio::Speed::Medium);
-        }
+        mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down);
+        mosi.set_speed(crate::gpio::Speed::Medium);
 
         Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config)
     }
@@ -247,7 +239,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         T::reset();
 
         #[cfg(any(spi_v1, spi_f1))]
-        unsafe {
+        {
             T::REGS.cr2().modify(|w| {
                 w.set_ssoe(false);
             });
@@ -270,7 +262,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
             });
         }
         #[cfg(spi_v2)]
-        unsafe {
+        {
             T::REGS.cr2().modify(|w| {
                 let (ds, frxth) = <u8 as sealed::Word>::CONFIG;
                 w.set_frxth(frxth);
@@ -292,7 +284,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
             });
         }
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        unsafe {
+        {
             T::REGS.ifcr().write(|w| w.0 = 0xffff_ffff);
             T::REGS.cfg2().modify(|w| {
                 //w.set_ssoe(true);
@@ -343,29 +335,25 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         let lsbfirst = config.raw_byte_order();
 
         #[cfg(any(spi_v1, spi_f1, spi_v2))]
-        unsafe {
-            T::REGS.cr1().modify(|w| {
-                w.set_cpha(cpha);
-                w.set_cpol(cpol);
-                w.set_lsbfirst(lsbfirst);
-            });
-        }
+        T::REGS.cr1().modify(|w| {
+            w.set_cpha(cpha);
+            w.set_cpol(cpol);
+            w.set_lsbfirst(lsbfirst);
+        });
 
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        unsafe {
-            T::REGS.cfg2().modify(|w| {
-                w.set_cpha(cpha);
-                w.set_cpol(cpol);
-                w.set_lsbfirst(lsbfirst);
-            });
-        }
+        T::REGS.cfg2().modify(|w| {
+            w.set_cpha(cpha);
+            w.set_cpol(cpol);
+            w.set_lsbfirst(lsbfirst);
+        });
     }
 
     pub fn get_current_config(&self) -> Config {
         #[cfg(any(spi_v1, spi_f1, spi_v2))]
-        let cfg = unsafe { T::REGS.cr1().read() };
+        let cfg = T::REGS.cr1().read();
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        let cfg = unsafe { T::REGS.cfg2().read() };
+        let cfg = T::REGS.cfg2().read();
         let polarity = if cfg.cpol() == vals::Cpol::IDLELOW {
             Polarity::IdleLow
         } else {
@@ -395,7 +383,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         }
 
         #[cfg(any(spi_v1, spi_f1))]
-        unsafe {
+        {
             T::REGS.cr1().modify(|reg| {
                 reg.set_spe(false);
                 reg.set_dff(word_size)
@@ -405,7 +393,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
             });
         }
         #[cfg(spi_v2)]
-        unsafe {
+        {
             T::REGS.cr1().modify(|w| {
                 w.set_spe(false);
             });
@@ -418,7 +406,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
             });
         }
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        unsafe {
+        {
             T::REGS.cr1().modify(|w| {
                 w.set_csusp(true);
             });
@@ -447,26 +435,22 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         }
 
         self.set_word_size(W::CONFIG);
-        unsafe {
-            T::REGS.cr1().modify(|w| {
-                w.set_spe(false);
-            });
-        }
+        T::REGS.cr1().modify(|w| {
+            w.set_spe(false);
+        });
 
         let tx_request = self.txdma.request();
         let tx_dst = T::REGS.tx_ptr();
         let tx_f = unsafe { Transfer::new_write(&mut self.txdma, tx_request, data, tx_dst, Default::default()) };
 
-        unsafe {
-            set_txdmaen(T::REGS, true);
-            T::REGS.cr1().modify(|w| {
-                w.set_spe(true);
-            });
-            #[cfg(any(spi_v3, spi_v4, spi_v5))]
-            T::REGS.cr1().modify(|w| {
-                w.set_cstart(true);
-            });
-        }
+        set_txdmaen(T::REGS, true);
+        T::REGS.cr1().modify(|w| {
+            w.set_spe(true);
+        });
+        #[cfg(any(spi_v3, spi_v4, spi_v5))]
+        T::REGS.cr1().modify(|w| {
+            w.set_cstart(true);
+        });
 
         tx_f.await;
 
@@ -485,11 +469,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         }
 
         self.set_word_size(W::CONFIG);
-        unsafe {
-            T::REGS.cr1().modify(|w| {
-                w.set_spe(false);
-            });
-        }
+        T::REGS.cr1().modify(|w| {
+            w.set_spe(false);
+        });
 
         // SPIv3 clears rxfifo on SPE=0
         #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
@@ -517,16 +499,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
             )
         };
 
-        unsafe {
-            set_txdmaen(T::REGS, true);
-            T::REGS.cr1().modify(|w| {
-                w.set_spe(true);
-            });
-            #[cfg(any(spi_v3, spi_v4, spi_v5))]
-            T::REGS.cr1().modify(|w| {
-                w.set_cstart(true);
-            });
-        }
+        set_txdmaen(T::REGS, true);
+        T::REGS.cr1().modify(|w| {
+            w.set_spe(true);
+        });
+        #[cfg(any(spi_v3, spi_v4, spi_v5))]
+        T::REGS.cr1().modify(|w| {
+            w.set_cstart(true);
+        });
 
         join(tx_f, rx_f).await;
 
@@ -548,11 +528,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         }
 
         self.set_word_size(W::CONFIG);
-        unsafe {
-            T::REGS.cr1().modify(|w| {
-                w.set_spe(false);
-            });
-        }
+        T::REGS.cr1().modify(|w| {
+            w.set_spe(false);
+        });
 
         // SPIv3 clears rxfifo on SPE=0
         #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
@@ -568,16 +546,14 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
         let tx_dst = T::REGS.tx_ptr();
         let tx_f = unsafe { Transfer::new_write_raw(&mut self.txdma, tx_request, write, tx_dst, Default::default()) };
 
-        unsafe {
-            set_txdmaen(T::REGS, true);
-            T::REGS.cr1().modify(|w| {
-                w.set_spe(true);
-            });
-            #[cfg(any(spi_v3, spi_v4, spi_v5))]
-            T::REGS.cr1().modify(|w| {
-                w.set_cstart(true);
-            });
-        }
+        set_txdmaen(T::REGS, true);
+        T::REGS.cr1().modify(|w| {
+            w.set_spe(true);
+        });
+        #[cfg(any(spi_v3, spi_v4, spi_v5))]
+        T::REGS.cr1().modify(|w| {
+            w.set_cstart(true);
+        });
 
         join(tx_f, rx_f).await;
 
@@ -603,7 +579,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
     }
 
     pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> {
-        unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
+        T::REGS.cr1().modify(|w| w.set_spe(true));
         flush_rx_fifo(T::REGS);
         self.set_word_size(W::CONFIG);
         for word in words.iter() {
@@ -613,7 +589,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
     }
 
     pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
-        unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
+        T::REGS.cr1().modify(|w| w.set_spe(true));
         flush_rx_fifo(T::REGS);
         self.set_word_size(W::CONFIG);
         for word in words.iter_mut() {
@@ -623,7 +599,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
     }
 
     pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> {
-        unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
+        T::REGS.cr1().modify(|w| w.set_spe(true));
         flush_rx_fifo(T::REGS);
         self.set_word_size(W::CONFIG);
         for word in words.iter_mut() {
@@ -633,7 +609,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
     }
 
     pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
-        unsafe { T::REGS.cr1().modify(|w| w.set_spe(true)) }
+        T::REGS.cr1().modify(|w| w.set_spe(true));
         flush_rx_fifo(T::REGS);
         self.set_word_size(W::CONFIG);
         let len = read.len().max(write.len());
@@ -650,11 +626,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> {
 
 impl<'d, T: Instance, Tx, Rx> Drop for Spi<'d, T, Tx, Rx> {
     fn drop(&mut self) {
-        unsafe {
-            self.sck.as_ref().map(|x| x.set_as_disconnected());
-            self.mosi.as_ref().map(|x| x.set_as_disconnected());
-            self.miso.as_ref().map(|x| x.set_as_disconnected());
-        }
+        self.sck.as_ref().map(|x| x.set_as_disconnected());
+        self.mosi.as_ref().map(|x| x.set_as_disconnected());
+        self.miso.as_ref().map(|x| x.set_as_disconnected());
     }
 }
 
@@ -690,7 +664,7 @@ impl RegsExt for Regs {
         let dr = self.dr();
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
         let dr = self.txdr();
-        dr.ptr() as *mut W
+        dr.as_ptr() as *mut W
     }
 
     fn rx_ptr<W>(&self) -> *mut W {
@@ -698,7 +672,7 @@ impl RegsExt for Regs {
         let dr = self.dr();
         #[cfg(any(spi_v3, spi_v4, spi_v5))]
         let dr = self.rxdr();
-        dr.ptr() as *mut W
+        dr.as_ptr() as *mut W
     }
 }
 
@@ -731,7 +705,7 @@ fn check_error_flags(sr: regs::Sr) -> Result<(), Error> {
 
 fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> {
     loop {
-        let sr = unsafe { regs.sr().read() };
+        let sr = regs.sr().read();
 
         check_error_flags(sr)?;
 
@@ -748,7 +722,7 @@ fn spin_until_tx_ready(regs: Regs) -> Result<(), Error> {
 
 fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
     loop {
-        let sr = unsafe { regs.sr().read() };
+        let sr = regs.sr().read();
 
         check_error_flags(sr)?;
 
@@ -764,72 +738,64 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> {
 }
 
 fn flush_rx_fifo(regs: Regs) {
-    unsafe {
-        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
-        while regs.sr().read().rxne() {
-            let _ = regs.dr().read();
-        }
-        #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        while regs.sr().read().rxp() {
-            let _ = regs.rxdr().read();
-        }
+    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+    while regs.sr().read().rxne() {
+        let _ = regs.dr().read();
+    }
+    #[cfg(any(spi_v3, spi_v4, spi_v5))]
+    while regs.sr().read().rxp() {
+        let _ = regs.rxdr().read();
     }
 }
 
 fn set_txdmaen(regs: Regs, val: bool) {
-    unsafe {
-        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
-        regs.cr2().modify(|reg| {
-            reg.set_txdmaen(val);
-        });
-        #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        regs.cfg1().modify(|reg| {
-            reg.set_txdmaen(val);
-        });
-    }
+    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+    regs.cr2().modify(|reg| {
+        reg.set_txdmaen(val);
+    });
+    #[cfg(any(spi_v3, spi_v4, spi_v5))]
+    regs.cfg1().modify(|reg| {
+        reg.set_txdmaen(val);
+    });
 }
 
 fn set_rxdmaen(regs: Regs, val: bool) {
-    unsafe {
-        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
-        regs.cr2().modify(|reg| {
-            reg.set_rxdmaen(val);
-        });
-        #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        regs.cfg1().modify(|reg| {
-            reg.set_rxdmaen(val);
-        });
-    }
+    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+    regs.cr2().modify(|reg| {
+        reg.set_rxdmaen(val);
+    });
+    #[cfg(any(spi_v3, spi_v4, spi_v5))]
+    regs.cfg1().modify(|reg| {
+        reg.set_rxdmaen(val);
+    });
 }
 
 fn finish_dma(regs: Regs) {
-    unsafe {
-        #[cfg(spi_v2)]
-        while regs.sr().read().ftlvl() > 0 {}
+    #[cfg(spi_v2)]
+    while regs.sr().read().ftlvl() > 0 {}
 
-        #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        while !regs.sr().read().txc() {}
-        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
-        while regs.sr().read().bsy() {}
+    #[cfg(any(spi_v3, spi_v4, spi_v5))]
+    while !regs.sr().read().txc() {}
+    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+    while regs.sr().read().bsy() {}
 
-        // Disable the spi peripheral
-        regs.cr1().modify(|w| {
-            w.set_spe(false);
-        });
+    // Disable the spi peripheral
+    regs.cr1().modify(|w| {
+        w.set_spe(false);
+    });
 
-        // The peripheral automatically disables the DMA stream on completion without error,
-        // but it does not clear the RXDMAEN/TXDMAEN flag in CR2.
-        #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
-        regs.cr2().modify(|reg| {
-            reg.set_txdmaen(false);
-            reg.set_rxdmaen(false);
-        });
-        #[cfg(any(spi_v3, spi_v4, spi_v5))]
-        regs.cfg1().modify(|reg| {
-            reg.set_txdmaen(false);
-            reg.set_rxdmaen(false);
-        });
-    }
+    // The peripheral automatically disables the DMA stream on completion without error,
+    // but it does not clear the RXDMAEN/TXDMAEN flag in CR2.
+    #[cfg(not(any(spi_v3, spi_v4, spi_v5)))]
+    regs.cr2().modify(|reg| {
+        reg.set_txdmaen(false);
+        reg.set_rxdmaen(false);
+    });
+    #[cfg(any(spi_v3, spi_v4, spi_v5))]
+    regs.cfg1().modify(|reg| {
+        reg.set_txdmaen(false);
+        reg.set_rxdmaen(false);
+    });
 }
 
 fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> {
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index e82501a45..2622442f4 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -155,8 +155,7 @@ impl RtcDriver {
 
         let timer_freq = T::frequency();
 
-        // NOTE(unsafe) Critical section to use the unsafe methods
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             r.cr1().modify(|w| w.set_cen(false));
             r.cnt().write(|w| w.set_cnt(0));
 
@@ -184,7 +183,7 @@ impl RtcDriver {
             });
 
             <T as BasicInstance>::Interrupt::unpend();
-            <T as BasicInstance>::Interrupt::enable();
+            unsafe { <T as BasicInstance>::Interrupt::enable() };
 
             r.cr1().modify(|w| w.set_cen(true));
         })
@@ -193,9 +192,8 @@ impl RtcDriver {
     fn on_interrupt(&self) {
         let r = T::regs_gp16();
 
-        // NOTE(unsafe) Use critical section to access the methods
         // XXX: reduce the size of this critical section ?
-        critical_section::with(|cs| unsafe {
+        critical_section::with(|cs| {
             let sr = r.sr().read();
             let dier = r.dier().read();
 
@@ -228,7 +226,7 @@ impl RtcDriver {
         let period = self.period.fetch_add(1, Ordering::Relaxed) + 1;
         let t = (period as u64) << 15;
 
-        critical_section::with(move |cs| unsafe {
+        critical_section::with(move |cs| {
             r.dier().modify(move |w| {
                 for n in 0..ALARM_COUNT {
                     let alarm = &self.alarms.borrow(cs)[n];
@@ -269,8 +267,7 @@ impl Driver for RtcDriver {
 
         let period = self.period.load(Ordering::Relaxed);
         compiler_fence(Ordering::Acquire);
-        // NOTE(unsafe) Atomic read with no side-effects
-        let counter = unsafe { r.cnt().read().cnt() };
+        let counter = r.cnt().read().cnt();
         calc_now(period, counter)
     }
 
@@ -310,7 +307,7 @@ impl Driver for RtcDriver {
             if timestamp <= t {
                 // If alarm timestamp has passed the alarm will not fire.
                 // Disarm the alarm and return `false` to indicate that.
-                unsafe { r.dier().modify(|w| w.set_ccie(n + 1, false)) };
+                r.dier().modify(|w| w.set_ccie(n + 1, false));
 
                 alarm.timestamp.set(u64::MAX);
 
@@ -321,12 +318,11 @@ impl Driver for RtcDriver {
 
             // Write the CCR value regardless of whether we're going to enable it now or not.
             // This way, when we enable it later, the right value is already set.
-            unsafe { r.ccr(n + 1).write(|w| w.set_ccr(safe_timestamp as u16)) };
+            r.ccr(n + 1).write(|w| w.set_ccr(safe_timestamp as u16));
 
             // Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
             let diff = timestamp - t;
-            // NOTE(unsafe) We're in a critical section
-            unsafe { r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)) };
+            r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
 
             true
         })
diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs
index 14db97024..09b7a3776 100644
--- a/embassy-stm32/src/timer/mod.rs
+++ b/embassy-stm32/src/timer/mod.rs
@@ -60,25 +60,19 @@ macro_rules! impl_basic_16bit_timer {
             type Interrupt = crate::interrupt::typelevel::$irq;
 
             fn regs() -> crate::pac::timer::TimBasic {
-                crate::pac::timer::TimBasic(crate::pac::$inst.0)
+                unsafe { crate::pac::timer::TimBasic::from_ptr(crate::pac::$inst.as_ptr()) }
             }
 
             fn start(&mut self) {
-                unsafe {
-                    Self::regs().cr1().modify(|r| r.set_cen(true));
-                }
+                Self::regs().cr1().modify(|r| r.set_cen(true));
             }
 
             fn stop(&mut self) {
-                unsafe {
-                    Self::regs().cr1().modify(|r| r.set_cen(false));
-                }
+                Self::regs().cr1().modify(|r| r.set_cen(false));
             }
 
             fn reset(&mut self) {
-                unsafe {
-                    Self::regs().cnt().write(|r| r.set_cnt(0));
-                }
+                Self::regs().cnt().write(|r| r.set_cnt(0));
             }
 
             fn set_frequency(&mut self, frequency: Hertz) {
@@ -90,35 +84,29 @@ macro_rules! impl_basic_16bit_timer {
                 let arr: u16 = unwrap!((pclk_ticks_per_timer_period / (u32::from(psc) + 1)).try_into());
 
                 let regs = Self::regs();
-                unsafe {
-                    regs.psc().write(|r| r.set_psc(psc));
-                    regs.arr().write(|r| r.set_arr(arr));
+                regs.psc().write(|r| r.set_psc(psc));
+                regs.arr().write(|r| r.set_arr(arr));
 
-                    regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
-                    regs.egr().write(|r| r.set_ug(true));
-                    regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
-                }
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
+                regs.egr().write(|r| r.set_ug(true));
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
             }
 
             fn clear_update_interrupt(&mut self) -> bool {
                 let regs = Self::regs();
-                unsafe {
-                    let sr = regs.sr().read();
-                    if sr.uif() {
-                        regs.sr().modify(|r| {
-                            r.set_uif(false);
-                        });
-                        true
-                    } else {
-                        false
-                    }
+                let sr = regs.sr().read();
+                if sr.uif() {
+                    regs.sr().modify(|r| {
+                        r.set_uif(false);
+                    });
+                    true
+                } else {
+                    false
                 }
             }
 
             fn enable_update_interrupt(&mut self, enable: bool) {
-                unsafe {
-                    Self::regs().dier().write(|r| r.set_uie(enable));
-                }
+                Self::regs().dier().write(|r| r.set_uie(enable));
             }
         }
     };
@@ -141,14 +129,12 @@ macro_rules! impl_32bit_timer {
                 let arr: u32 = unwrap!(((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into()));
 
                 let regs = Self::regs_gp32();
-                unsafe {
-                    regs.psc().write(|r| r.set_psc(psc));
-                    regs.arr().write(|r| r.set_arr(arr));
+                regs.psc().write(|r| r.set_psc(psc));
+                regs.arr().write(|r| r.set_arr(arr));
 
-                    regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
-                    regs.egr().write(|r| r.set_ug(true));
-                    regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
-                }
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY));
+                regs.egr().write(|r| r.set_ug(true));
+                regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT));
             }
         }
     };
@@ -185,7 +171,7 @@ foreach_interrupt! {
 
         impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
             fn regs_gp16() -> crate::pac::timer::TimGp16 {
-                crate::pac::timer::TimGp16(crate::pac::$inst.0)
+                unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) }
             }
         }
 
@@ -206,7 +192,7 @@ foreach_interrupt! {
 
         impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst {
             fn regs_gp16() -> crate::pac::timer::TimGp16 {
-                crate::pac::timer::TimGp16(crate::pac::$inst.0)
+                unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) }
             }
         }
 
diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs
index 613da5674..433ad299c 100644
--- a/embassy-stm32/src/usart/buffered.rs
+++ b/embassy-stm32/src/usart/buffered.rs
@@ -19,62 +19,64 @@ impl<T: BasicInstance> interrupt::typelevel::Handler<T::Interrupt> for Interrupt
         let state = T::buffered_state();
 
         // RX
-        unsafe {
-            let sr = sr(r).read();
-            clear_interrupt_flags(r, sr);
+        let sr_val = sr(r).read();
+        // On v1 & v2, reading DR clears the rxne, error and idle interrupt
+        // flags. Keep this close to the SR read to reduce the chance of a
+        // flag being set in-between.
+        let dr = if sr_val.rxne() || cfg!(any(usart_v1, usart_v2)) && (sr_val.ore() || sr_val.idle()) {
+            Some(rdr(r).read_volatile())
+        } else {
+            None
+        };
+        clear_interrupt_flags(r, sr_val);
 
-            if sr.rxne() {
-                if sr.pe() {
-                    warn!("Parity error");
-                }
-                if sr.fe() {
-                    warn!("Framing error");
-                }
-                if sr.ne() {
-                    warn!("Noise error");
-                }
-                if sr.ore() {
-                    warn!("Overrun error");
-                }
-
-                let mut rx_writer = state.rx_buf.writer();
-                let buf = rx_writer.push_slice();
-                if !buf.is_empty() {
-                    // This read also clears the error and idle interrupt flags on v1.
-                    buf[0] = rdr(r).read_volatile();
-                    rx_writer.push_done(1);
-                } else {
-                    // FIXME: Should we disable any further RX interrupts when the buffer becomes full.
-                }
-
-                if state.rx_buf.is_full() {
-                    state.rx_waker.wake();
-                }
+        if sr_val.pe() {
+            warn!("Parity error");
+        }
+        if sr_val.fe() {
+            warn!("Framing error");
+        }
+        if sr_val.ne() {
+            warn!("Noise error");
+        }
+        if sr_val.ore() {
+            warn!("Overrun error");
+        }
+        if sr_val.rxne() {
+            let mut rx_writer = state.rx_buf.writer();
+            let buf = rx_writer.push_slice();
+            if !buf.is_empty() {
+                buf[0] = dr.unwrap();
+                rx_writer.push_done(1);
+            } else {
+                // FIXME: Should we disable any further RX interrupts when the buffer becomes full.
             }
 
-            if sr.idle() {
+            if state.rx_buf.is_full() {
                 state.rx_waker.wake();
-            };
+            }
+        }
+
+        if sr_val.idle() {
+            state.rx_waker.wake();
         }
 
         // TX
-        unsafe {
-            if sr(r).read().txe() {
-                let mut tx_reader = state.tx_buf.reader();
-                let buf = tx_reader.pop_slice();
-                if !buf.is_empty() {
-                    r.cr1().modify(|w| {
-                        w.set_txeie(true);
-                    });
-                    tdr(r).write_volatile(buf[0].into());
-                    tx_reader.pop_done(1);
-                    state.tx_waker.wake();
-                } else {
-                    // Disable interrupt until we have something to transmit again
-                    r.cr1().modify(|w| {
-                        w.set_txeie(false);
-                    });
-                }
+        if sr(r).read().txe() {
+            let mut tx_reader = state.tx_buf.reader();
+            let buf = tx_reader.pop_slice();
+            if !buf.is_empty() {
+                r.cr1().modify(|w| {
+                    w.set_txeie(true);
+                });
+                tdr(r).write_volatile(buf[0].into());
+                tx_reader.pop_done(1);
+                state.tx_waker.wake();
+            } else {
+                // Disable interrupt until we have something to transmit again
+                r.cr1().modify(|w| {
+                    w.set_txeie(false);
+                });
             }
         }
     }
@@ -144,14 +146,12 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
         T::enable();
         T::reset();
 
-        unsafe {
-            rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
-            cts.set_as_af(cts.af_num(), AFType::Input);
-            T::regs().cr3().write(|w| {
-                w.set_rtse(true);
-                w.set_ctse(true);
-            });
-        }
+        rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
+        cts.set_as_af(cts.af_num(), AFType::Input);
+        T::regs().cr3().write(|w| {
+            w.set_rtse(true);
+            w.set_ctse(true);
+        });
 
         Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config)
     }
@@ -172,12 +172,10 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
         T::enable();
         T::reset();
 
-        unsafe {
-            de.set_as_af(de.af_num(), AFType::OutputPushPull);
-            T::regs().cr3().write(|w| {
-                w.set_dem(true);
-            });
-        }
+        de.set_as_af(de.af_num(), AFType::OutputPushPull);
+        T::regs().cr3().write(|w| {
+            w.set_dem(true);
+        });
 
         Self::new_inner(peri, rx, tx, tx_buffer, rx_buffer, config)
     }
@@ -199,22 +197,18 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
         unsafe { state.rx_buf.init(rx_buffer.as_mut_ptr(), len) };
 
         let r = T::regs();
-        unsafe {
-            rx.set_as_af(rx.af_num(), AFType::Input);
-            tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
-        }
+        rx.set_as_af(rx.af_num(), AFType::Input);
+        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
 
         configure(r, &config, T::frequency(), T::KIND, true, true);
 
-        unsafe {
-            r.cr1().modify(|w| {
-                #[cfg(lpuart_v2)]
-                w.set_fifoen(true);
+        r.cr1().modify(|w| {
+            #[cfg(lpuart_v2)]
+            w.set_fifoen(true);
 
-                w.set_rxneie(true);
-                w.set_idleie(true);
-            });
-        }
+            w.set_rxneie(true);
+            w.set_idleie(true);
+        });
 
         T::Interrupt::unpend();
         unsafe { T::Interrupt::enable() };
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs
index da3644a81..47a79c187 100644
--- a/embassy-stm32/src/usart/mod.rs
+++ b/embassy-stm32/src/usart/mod.rs
@@ -36,35 +36,31 @@ impl<T: BasicInstance> interrupt::typelevel::Handler<T::Interrupt> for Interrupt
         let r = T::regs();
         let s = T::state();
 
-        let (sr, cr1, cr3) = unsafe { (sr(r).read(), r.cr1().read(), r.cr3().read()) };
+        let (sr, cr1, cr3) = (sr(r).read(), r.cr1().read(), r.cr3().read());
 
         let has_errors = (sr.pe() && cr1.peie()) || ((sr.fe() || sr.ne() || sr.ore()) && cr3.eie());
         if has_errors {
             // clear all interrupts and DMA Rx Request
-            unsafe {
-                r.cr1().modify(|w| {
-                    // disable RXNE interrupt
-                    w.set_rxneie(false);
-                    // disable parity interrupt
-                    w.set_peie(false);
-                    // disable idle line interrupt
-                    w.set_idleie(false);
-                });
-                r.cr3().modify(|w| {
-                    // disable Error Interrupt: (Frame error, Noise error, Overrun error)
-                    w.set_eie(false);
-                    // disable DMA Rx Request
-                    w.set_dmar(false);
-                });
-            }
+            r.cr1().modify(|w| {
+                // disable RXNE interrupt
+                w.set_rxneie(false);
+                // disable parity interrupt
+                w.set_peie(false);
+                // disable idle line interrupt
+                w.set_idleie(false);
+            });
+            r.cr3().modify(|w| {
+                // disable Error Interrupt: (Frame error, Noise error, Overrun error)
+                w.set_eie(false);
+                // disable DMA Rx Request
+                w.set_dmar(false);
+            });
         } else if cr1.idleie() && sr.idle() {
             // IDLE detected: no more data will come
-            unsafe {
-                r.cr1().modify(|w| {
-                    // disable idle line detection
-                    w.set_idleie(false);
-                });
-            }
+            r.cr1().modify(|w| {
+                // disable idle line detection
+                w.set_idleie(false);
+            });
         } else if cr1.rxneie() {
             // We cannot check the RXNE flag as it is auto-cleared by the DMA controller
 
@@ -205,12 +201,10 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
         T::enable();
         T::reset();
 
-        unsafe {
-            cts.set_as_af(cts.af_num(), AFType::Input);
-            T::regs().cr3().write(|w| {
-                w.set_ctse(true);
-            });
-        }
+        cts.set_as_af(cts.af_num(), AFType::Input);
+        T::regs().cr3().write(|w| {
+            w.set_ctse(true);
+        });
         Self::new_inner(peri, tx, tx_dma, config)
     }
 
@@ -224,9 +218,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
 
         let r = T::regs();
 
-        unsafe {
-            tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
-        }
+        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
 
         configure(r, &config, T::frequency(), T::KIND, false, true);
 
@@ -245,11 +237,9 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
     {
         let ch = &mut self.tx_dma;
         let request = ch.request();
-        unsafe {
-            T::regs().cr3().modify(|reg| {
-                reg.set_dmat(true);
-            });
-        }
+        T::regs().cr3().modify(|reg| {
+            reg.set_dmat(true);
+        });
         // If we don't assign future to a variable, the data register pointer
         // is held across an await and makes the future non-Send.
         let transfer = unsafe { Transfer::new_write(ch, request, buffer, tdr(T::regs()), Default::default()) };
@@ -258,21 +248,17 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> {
     }
 
     pub fn blocking_write(&mut self, buffer: &[u8]) -> Result<(), Error> {
-        unsafe {
-            let r = T::regs();
-            for &b in buffer {
-                while !sr(r).read().txe() {}
-                tdr(r).write_volatile(b);
-            }
+        let r = T::regs();
+        for &b in buffer {
+            while !sr(r).read().txe() {}
+            unsafe { tdr(r).write_volatile(b) };
         }
         Ok(())
     }
 
     pub fn blocking_flush(&mut self) -> Result<(), Error> {
-        unsafe {
-            let r = T::regs();
-            while !sr(r).read().tc() {}
-        }
+        let r = T::regs();
+        while !sr(r).read().tc() {}
         Ok(())
     }
 }
@@ -305,12 +291,10 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
         T::enable();
         T::reset();
 
-        unsafe {
-            rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
-            T::regs().cr3().write(|w| {
-                w.set_rtse(true);
-            });
-        }
+        rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
+        T::regs().cr3().write(|w| {
+            w.set_rtse(true);
+        });
 
         Self::new_inner(peri, rx, rx_dma, config)
     }
@@ -325,9 +309,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
 
         let r = T::regs();
 
-        unsafe {
-            rx.set_as_af(rx.af_num(), AFType::Input);
-        }
+        rx.set_as_af(rx.af_num(), AFType::Input);
 
         configure(r, &config, T::frequency(), T::KIND, true, false);
 
@@ -347,7 +329,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
     }
 
     #[cfg(any(usart_v1, usart_v2))]
-    unsafe fn check_rx_flags(&mut self) -> Result<bool, Error> {
+    fn check_rx_flags(&mut self) -> Result<bool, Error> {
         let r = T::regs();
         loop {
             // Handle all buffered error flags.
@@ -380,7 +362,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
     }
 
     #[cfg(any(usart_v3, usart_v4))]
-    unsafe fn check_rx_flags(&mut self) -> Result<bool, Error> {
+    fn check_rx_flags(&mut self) -> Result<bool, Error> {
         let r = T::regs();
         let sr = r.isr().read();
         if sr.pe() {
@@ -410,22 +392,18 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
 
     pub fn nb_read(&mut self) -> Result<u8, nb::Error<Error>> {
         let r = T::regs();
-        unsafe {
-            if self.check_rx_flags()? {
-                Ok(rdr(r).read_volatile())
-            } else {
-                Err(nb::Error::WouldBlock)
-            }
+        if self.check_rx_flags()? {
+            Ok(unsafe { rdr(r).read_volatile() })
+        } else {
+            Err(nb::Error::WouldBlock)
         }
     }
 
     pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
-        unsafe {
-            let r = T::regs();
-            for b in buffer {
-                while !self.check_rx_flags()? {}
-                *b = rdr(r).read_volatile();
-            }
+        let r = T::regs();
+        for b in buffer {
+            while !self.check_rx_flags()? {}
+            unsafe { *b = rdr(r).read_volatile() }
         }
         Ok(())
     }
@@ -451,23 +429,20 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
         let on_drop = OnDrop::new(move || {
             // defmt::trace!("Clear all USART interrupts and DMA Read Request");
             // clear all interrupts and DMA Rx Request
-            // SAFETY: only clears Rx related flags
-            unsafe {
-                r.cr1().modify(|w| {
-                    // disable RXNE interrupt
-                    w.set_rxneie(false);
-                    // disable parity interrupt
-                    w.set_peie(false);
-                    // disable idle line interrupt
-                    w.set_idleie(false);
-                });
-                r.cr3().modify(|w| {
-                    // disable Error Interrupt: (Frame error, Noise error, Overrun error)
-                    w.set_eie(false);
-                    // disable DMA Rx Request
-                    w.set_dmar(false);
-                });
-            }
+            r.cr1().modify(|w| {
+                // disable RXNE interrupt
+                w.set_rxneie(false);
+                // disable parity interrupt
+                w.set_peie(false);
+                // disable idle line interrupt
+                w.set_idleie(false);
+            });
+            r.cr3().modify(|w| {
+                // disable Error Interrupt: (Frame error, Noise error, Overrun error)
+                w.set_eie(false);
+                // disable DMA Rx Request
+                w.set_dmar(false);
+            });
         });
 
         let ch = &mut self.rx_dma;
@@ -480,78 +455,74 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
         // future which will complete when DMA Read request completes
         let transfer = unsafe { Transfer::new_read(ch, request, rdr(T::regs()), buffer, Default::default()) };
 
-        // SAFETY: The only way we might have a problem is using split rx and tx
-        // here we only modify or read Rx related flags, interrupts and DMA channel
-        unsafe {
-            // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer
-            if !self.detect_previous_overrun {
-                let sr = sr(r).read();
-                // This read also clears the error and idle interrupt flags on v1.
-                rdr(r).read_volatile();
-                clear_interrupt_flags(r, sr);
+        // clear ORE flag just before enabling DMA Rx Request: can be mandatory for the second transfer
+        if !self.detect_previous_overrun {
+            let sr = sr(r).read();
+            // This read also clears the error and idle interrupt flags on v1.
+            unsafe { rdr(r).read_volatile() };
+            clear_interrupt_flags(r, sr);
+        }
+
+        r.cr1().modify(|w| {
+            // disable RXNE interrupt
+            w.set_rxneie(false);
+            // enable parity interrupt if not ParityNone
+            w.set_peie(w.pce());
+        });
+
+        r.cr3().modify(|w| {
+            // enable Error Interrupt: (Frame error, Noise error, Overrun error)
+            w.set_eie(true);
+            // enable DMA Rx Request
+            w.set_dmar(true);
+        });
+
+        compiler_fence(Ordering::SeqCst);
+
+        // In case of errors already pending when reception started, interrupts may have already been raised
+        // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts
+        // have been disabled in interrupt handler and DMA Rx Request has been disabled.
+
+        let cr3 = r.cr3().read();
+
+        if !cr3.dmar() {
+            // something went wrong
+            // because the only way to get this flag cleared is to have an interrupt
+
+            // DMA will be stopped when transfer is dropped
+
+            let sr = sr(r).read();
+            // This read also clears the error and idle interrupt flags on v1.
+            unsafe { rdr(r).read_volatile() };
+            clear_interrupt_flags(r, sr);
+
+            if sr.pe() {
+                return Err(Error::Parity);
+            }
+            if sr.fe() {
+                return Err(Error::Framing);
+            }
+            if sr.ne() {
+                return Err(Error::Noise);
+            }
+            if sr.ore() {
+                return Err(Error::Overrun);
             }
 
+            unreachable!();
+        }
+
+        if enable_idle_line_detection {
+            // clear idle flag
+            let sr = sr(r).read();
+            // This read also clears the error and idle interrupt flags on v1.
+            unsafe { rdr(r).read_volatile() };
+            clear_interrupt_flags(r, sr);
+
+            // enable idle interrupt
             r.cr1().modify(|w| {
-                // disable RXNE interrupt
-                w.set_rxneie(false);
-                // enable parity interrupt if not ParityNone
-                w.set_peie(w.pce());
+                w.set_idleie(true);
             });
-
-            r.cr3().modify(|w| {
-                // enable Error Interrupt: (Frame error, Noise error, Overrun error)
-                w.set_eie(true);
-                // enable DMA Rx Request
-                w.set_dmar(true);
-            });
-
-            compiler_fence(Ordering::SeqCst);
-
-            // In case of errors already pending when reception started, interrupts may have already been raised
-            // and lead to reception abortion (Overrun error for instance). In such a case, all interrupts
-            // have been disabled in interrupt handler and DMA Rx Request has been disabled.
-
-            let cr3 = r.cr3().read();
-
-            if !cr3.dmar() {
-                // something went wrong
-                // because the only way to get this flag cleared is to have an interrupt
-
-                // DMA will be stopped when transfer is dropped
-
-                let sr = sr(r).read();
-                // This read also clears the error and idle interrupt flags on v1.
-                rdr(r).read_volatile();
-                clear_interrupt_flags(r, sr);
-
-                if sr.pe() {
-                    return Err(Error::Parity);
-                }
-                if sr.fe() {
-                    return Err(Error::Framing);
-                }
-                if sr.ne() {
-                    return Err(Error::Noise);
-                }
-                if sr.ore() {
-                    return Err(Error::Overrun);
-                }
-
-                unreachable!();
-            }
-
-            if enable_idle_line_detection {
-                // clear idle flag
-                let sr = sr(r).read();
-                // This read also clears the error and idle interrupt flags on v1.
-                rdr(r).read_volatile();
-                clear_interrupt_flags(r, sr);
-
-                // enable idle interrupt
-                r.cr1().modify(|w| {
-                    w.set_idleie(true);
-                });
-            }
         }
 
         compiler_fence(Ordering::SeqCst);
@@ -562,15 +533,11 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> {
 
             s.rx_waker.register(cx.waker());
 
-            // SAFETY: read only and we only use Rx related flags
-            let sr = unsafe { sr(r).read() };
+            let sr = sr(r).read();
 
-            // SAFETY: only clears Rx related flags
-            unsafe {
-                // This read also clears the error and idle interrupt flags on v1.
-                rdr(r).read_volatile();
-                clear_interrupt_flags(r, sr);
-            }
+            // This read also clears the error and idle interrupt flags on v1.
+            unsafe { rdr(r).read_volatile() };
+            clear_interrupt_flags(r, sr);
 
             compiler_fence(Ordering::SeqCst);
 
@@ -677,14 +644,12 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
         T::enable();
         T::reset();
 
-        unsafe {
-            rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
-            cts.set_as_af(cts.af_num(), AFType::Input);
-            T::regs().cr3().write(|w| {
-                w.set_rtse(true);
-                w.set_ctse(true);
-            });
-        }
+        rts.set_as_af(rts.af_num(), AFType::OutputPushPull);
+        cts.set_as_af(cts.af_num(), AFType::Input);
+        T::regs().cr3().write(|w| {
+            w.set_rtse(true);
+            w.set_ctse(true);
+        });
         Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config)
     }
 
@@ -704,12 +669,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
         T::enable();
         T::reset();
 
-        unsafe {
-            de.set_as_af(de.af_num(), AFType::OutputPushPull);
-            T::regs().cr3().write(|w| {
-                w.set_dem(true);
-            });
-        }
+        de.set_as_af(de.af_num(), AFType::OutputPushPull);
+        T::regs().cr3().write(|w| {
+            w.set_dem(true);
+        });
         Self::new_inner(peri, rx, tx, tx_dma, rx_dma, config)
     }
 
@@ -725,10 +688,8 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
 
         let r = T::regs();
 
-        unsafe {
-            rx.set_as_af(rx.af_num(), AFType::Input);
-            tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
-        }
+        rx.set_as_af(rx.af_num(), AFType::Input);
+        tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
 
         configure(r, &config, T::frequency(), T::KIND, true, true);
 
@@ -847,11 +808,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
             if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) {
                 over8 = true;
                 let div = div as u32;
-                unsafe {
-                    r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07)));
-                    #[cfg(usart_v4)]
-                    r.presc().write(|w| w.set_prescaler(_presc_val));
-                }
+                r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07)));
+                #[cfg(usart_v4)]
+                r.presc().write(|w| w.set_prescaler(_presc_val));
                 found = Some(div);
                 break;
             }
@@ -860,11 +819,9 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
 
         if div < brr_max {
             let div = div as u32;
-            unsafe {
-                r.brr().write_value(regs::Brr(div));
-                #[cfg(usart_v4)]
-                r.presc().write(|w| w.set_prescaler(_presc_val));
-            }
+            r.brr().write_value(regs::Brr(div));
+            #[cfg(usart_v4)]
+            r.presc().write(|w| w.set_prescaler(_presc_val));
             found = Some(div);
             break;
         }
@@ -883,44 +840,42 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx:
         pclk_freq.0 / div
     );
 
-    unsafe {
-        r.cr2().write(|w| {
-            w.set_stop(match config.stop_bits {
-                StopBits::STOP0P5 => vals::Stop::STOP0P5,
-                StopBits::STOP1 => vals::Stop::STOP1,
-                StopBits::STOP1P5 => vals::Stop::STOP1P5,
-                StopBits::STOP2 => vals::Stop::STOP2,
-            });
+    r.cr2().write(|w| {
+        w.set_stop(match config.stop_bits {
+            StopBits::STOP0P5 => vals::Stop::STOP0P5,
+            StopBits::STOP1 => vals::Stop::STOP1,
+            StopBits::STOP1P5 => vals::Stop::STOP1P5,
+            StopBits::STOP2 => vals::Stop::STOP2,
         });
-        r.cr1().write(|w| {
-            // enable uart
-            w.set_ue(true);
-            // enable transceiver
-            w.set_te(enable_tx);
-            // enable receiver
-            w.set_re(enable_rx);
-            // configure word size
-            w.set_m0(if config.parity != Parity::ParityNone {
-                vals::M0::BIT9
-            } else {
-                vals::M0::BIT8
-            });
-            // configure parity
-            w.set_pce(config.parity != Parity::ParityNone);
-            w.set_ps(match config.parity {
-                Parity::ParityOdd => vals::Ps::ODD,
-                Parity::ParityEven => vals::Ps::EVEN,
-                _ => vals::Ps::EVEN,
-            });
-            #[cfg(not(usart_v1))]
-            w.set_over8(vals::Over8(over8 as _));
+    });
+    r.cr1().write(|w| {
+        // enable uart
+        w.set_ue(true);
+        // enable transceiver
+        w.set_te(enable_tx);
+        // enable receiver
+        w.set_re(enable_rx);
+        // configure word size
+        w.set_m0(if config.parity != Parity::ParityNone {
+            vals::M0::BIT9
+        } else {
+            vals::M0::BIT8
+        });
+        // configure parity
+        w.set_pce(config.parity != Parity::ParityNone);
+        w.set_ps(match config.parity {
+            Parity::ParityOdd => vals::Ps::ODD,
+            Parity::ParityEven => vals::Ps::EVEN,
+            _ => vals::Ps::EVEN,
         });
-
         #[cfg(not(usart_v1))]
-        r.cr3().modify(|w| {
-            w.set_onebit(config.assume_noise_free);
-        });
-    }
+        w.set_over8(vals::Over8(over8 as _));
+    });
+
+    #[cfg(not(usart_v1))]
+    r.cr3().modify(|w| {
+        w.set_onebit(config.assume_noise_free);
+    });
 }
 
 mod eh02 {
@@ -1111,12 +1066,12 @@ use self::sealed::Kind;
 
 #[cfg(any(usart_v1, usart_v2))]
 fn tdr(r: crate::pac::usart::Usart) -> *mut u8 {
-    r.dr().ptr() as _
+    r.dr().as_ptr() as _
 }
 
 #[cfg(any(usart_v1, usart_v2))]
 fn rdr(r: crate::pac::usart::Usart) -> *mut u8 {
-    r.dr().ptr() as _
+    r.dr().as_ptr() as _
 }
 
 #[cfg(any(usart_v1, usart_v2))]
@@ -1126,18 +1081,18 @@ fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Sr, crate::p
 
 #[cfg(any(usart_v1, usart_v2))]
 #[allow(unused)]
-unsafe fn clear_interrupt_flags(_r: Regs, _sr: regs::Sr) {
+fn clear_interrupt_flags(_r: Regs, _sr: regs::Sr) {
     // On v1 the flags are cleared implicitly by reads and writes to DR.
 }
 
 #[cfg(any(usart_v3, usart_v4))]
 fn tdr(r: Regs) -> *mut u8 {
-    r.tdr().ptr() as _
+    r.tdr().as_ptr() as _
 }
 
 #[cfg(any(usart_v3, usart_v4))]
 fn rdr(r: Regs) -> *mut u8 {
-    r.rdr().ptr() as _
+    r.rdr().as_ptr() as _
 }
 
 #[cfg(any(usart_v3, usart_v4))]
@@ -1147,7 +1102,7 @@ fn sr(r: Regs) -> crate::pac::common::Reg<regs::Isr, crate::pac::common::R> {
 
 #[cfg(any(usart_v3, usart_v4))]
 #[allow(unused)]
-unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) {
+fn clear_interrupt_flags(r: Regs, sr: regs::Isr) {
     r.icr().write(|w| *w = regs::Icr(sr.0));
 }
 
@@ -1214,7 +1169,7 @@ macro_rules! impl_usart {
             type Interrupt = crate::interrupt::typelevel::$irq;
 
             fn regs() -> Regs {
-                Regs(crate::pac::$inst.0)
+                unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) }
             }
 
             fn state() -> &'static crate::usart::sealed::State {
diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs
index 511b71c7f..c74d7e092 100644
--- a/embassy-stm32/src/usart/ringbuffered.rs
+++ b/embassy-stm32/src/usart/ringbuffered.rs
@@ -59,23 +59,20 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
 
         let r = T::regs();
         // clear all interrupts and DMA Rx Request
-        // SAFETY: only clears Rx related flags
-        unsafe {
-            r.cr1().modify(|w| {
-                // disable RXNE interrupt
-                w.set_rxneie(false);
-                // enable parity interrupt if not ParityNone
-                w.set_peie(w.pce());
-                // enable idle line interrupt
-                w.set_idleie(true);
-            });
-            r.cr3().modify(|w| {
-                // enable Error Interrupt: (Frame error, Noise error, Overrun error)
-                w.set_eie(true);
-                // enable DMA Rx Request
-                w.set_dmar(true);
-            });
-        }
+        r.cr1().modify(|w| {
+            // disable RXNE interrupt
+            w.set_rxneie(false);
+            // enable parity interrupt if not ParityNone
+            w.set_peie(w.pce());
+            // enable idle line interrupt
+            w.set_idleie(true);
+        });
+        r.cr3().modify(|w| {
+            // enable Error Interrupt: (Frame error, Noise error, Overrun error)
+            w.set_eie(true);
+            // enable DMA Rx Request
+            w.set_dmar(true);
+        });
     }
 
     /// Stop uart background receive
@@ -84,23 +81,20 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
 
         let r = T::regs();
         // clear all interrupts and DMA Rx Request
-        // SAFETY: only clears Rx related flags
-        unsafe {
-            r.cr1().modify(|w| {
-                // disable RXNE interrupt
-                w.set_rxneie(false);
-                // disable parity interrupt
-                w.set_peie(false);
-                // disable idle line interrupt
-                w.set_idleie(false);
-            });
-            r.cr3().modify(|w| {
-                // disable Error Interrupt: (Frame error, Noise error, Overrun error)
-                w.set_eie(false);
-                // disable DMA Rx Request
-                w.set_dmar(false);
-            });
-        }
+        r.cr1().modify(|w| {
+            // disable RXNE interrupt
+            w.set_rxneie(false);
+            // disable parity interrupt
+            w.set_peie(false);
+            // disable idle line interrupt
+            w.set_idleie(false);
+        });
+        r.cr3().modify(|w| {
+            // disable Error Interrupt: (Frame error, Noise error, Overrun error)
+            w.set_eie(false);
+            // disable DMA Rx Request
+            w.set_dmar(false);
+        });
 
         compiler_fence(Ordering::SeqCst);
     }
@@ -117,8 +111,7 @@ impl<'d, T: BasicInstance, RxDma: super::RxDma<T>> RingBufferedUartRx<'d, T, RxD
         let r = T::regs();
 
         // Start background receive if it was not already started
-        // SAFETY: read only
-        match unsafe { r.cr3().read().dmar() } {
+        match r.cr3().read().dmar() {
             false => self.start()?,
             _ => {}
         };
@@ -213,19 +206,17 @@ fn check_for_errors(s: Sr) -> Result<(), Error> {
 
 /// Clear IDLE and return the Sr register
 fn clear_idle_flag(r: Regs) -> Sr {
-    unsafe {
-        // SAFETY: read only and we only use Rx related flags
+    // SAFETY: read only and we only use Rx related flags
 
-        let sr = sr(r).read();
+    let sr = sr(r).read();
 
-        // This read also clears the error and idle interrupt flags on v1.
-        rdr(r).read_volatile();
-        clear_interrupt_flags(r, sr);
+    // This read also clears the error and idle interrupt flags on v1.
+    unsafe { rdr(r).read_volatile() };
+    clear_interrupt_flags(r, sr);
 
-        r.cr1().modify(|w| w.set_idleie(true));
+    r.cr1().modify(|w| w.set_idleie(true));
 
-        sr
-    }
+    sr
 }
 
 #[cfg(all(feature = "unstable-traits", feature = "nightly"))]
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index 7486bd376..2367127e8 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -28,82 +28,80 @@ pub struct InterruptHandler<T: Instance> {
 
 impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
     unsafe fn on_interrupt() {
-        unsafe {
-            let regs = T::regs();
-            //let x = regs.istr().read().0;
-            //trace!("USB IRQ: {:08x}", x);
+        let regs = T::regs();
+        //let x = regs.istr().read().0;
+        //trace!("USB IRQ: {:08x}", x);
 
-            let istr = regs.istr().read();
+        let istr = regs.istr().read();
 
-            if istr.susp() {
-                //trace!("USB IRQ: susp");
-                IRQ_SUSPEND.store(true, Ordering::Relaxed);
-                regs.cntr().modify(|w| {
-                    w.set_fsusp(true);
-                    w.set_lpmode(true);
-                });
+        if istr.susp() {
+            //trace!("USB IRQ: susp");
+            IRQ_SUSPEND.store(true, Ordering::Relaxed);
+            regs.cntr().modify(|w| {
+                w.set_fsusp(true);
+                w.set_lpmode(true);
+            });
 
-                // Write 0 to clear.
-                let mut clear = regs::Istr(!0);
-                clear.set_susp(false);
-                regs.istr().write_value(clear);
+            // Write 0 to clear.
+            let mut clear = regs::Istr(!0);
+            clear.set_susp(false);
+            regs.istr().write_value(clear);
 
-                // Wake main thread.
-                BUS_WAKER.wake();
-            }
+            // Wake main thread.
+            BUS_WAKER.wake();
+        }
 
-            if istr.wkup() {
-                //trace!("USB IRQ: wkup");
-                IRQ_RESUME.store(true, Ordering::Relaxed);
-                regs.cntr().modify(|w| {
-                    w.set_fsusp(false);
-                    w.set_lpmode(false);
-                });
+        if istr.wkup() {
+            //trace!("USB IRQ: wkup");
+            IRQ_RESUME.store(true, Ordering::Relaxed);
+            regs.cntr().modify(|w| {
+                w.set_fsusp(false);
+                w.set_lpmode(false);
+            });
 
-                // Write 0 to clear.
-                let mut clear = regs::Istr(!0);
-                clear.set_wkup(false);
-                regs.istr().write_value(clear);
+            // Write 0 to clear.
+            let mut clear = regs::Istr(!0);
+            clear.set_wkup(false);
+            regs.istr().write_value(clear);
 
-                // Wake main thread.
-                BUS_WAKER.wake();
-            }
+            // Wake main thread.
+            BUS_WAKER.wake();
+        }
 
-            if istr.reset() {
-                //trace!("USB IRQ: reset");
-                IRQ_RESET.store(true, Ordering::Relaxed);
+        if istr.reset() {
+            //trace!("USB IRQ: reset");
+            IRQ_RESET.store(true, Ordering::Relaxed);
 
-                // Write 0 to clear.
-                let mut clear = regs::Istr(!0);
-                clear.set_reset(false);
-                regs.istr().write_value(clear);
+            // Write 0 to clear.
+            let mut clear = regs::Istr(!0);
+            clear.set_reset(false);
+            regs.istr().write_value(clear);
 
-                // Wake main thread.
-                BUS_WAKER.wake();
-            }
+            // Wake main thread.
+            BUS_WAKER.wake();
+        }
 
-            if istr.ctr() {
-                let index = istr.ep_id() as usize;
-                let mut epr = regs.epr(index).read();
-                if epr.ctr_rx() {
-                    if index == 0 && epr.setup() {
-                        EP0_SETUP.store(true, Ordering::Relaxed);
-                    }
-                    //trace!("EP {} RX, setup={}", index, epr.setup());
-                    EP_OUT_WAKERS[index].wake();
+        if istr.ctr() {
+            let index = istr.ep_id() as usize;
+            let mut epr = regs.epr(index).read();
+            if epr.ctr_rx() {
+                if index == 0 && epr.setup() {
+                    EP0_SETUP.store(true, Ordering::Relaxed);
                 }
-                if epr.ctr_tx() {
-                    //trace!("EP {} TX", index);
-                    EP_IN_WAKERS[index].wake();
-                }
-                epr.set_dtog_rx(false);
-                epr.set_dtog_tx(false);
-                epr.set_stat_rx(Stat(0));
-                epr.set_stat_tx(Stat(0));
-                epr.set_ctr_rx(!epr.ctr_rx());
-                epr.set_ctr_tx(!epr.ctr_tx());
-                regs.epr(index).write_value(epr);
+                //trace!("EP {} RX, setup={}", index, epr.setup());
+                EP_OUT_WAKERS[index].wake();
             }
+            if epr.ctr_tx() {
+                //trace!("EP {} TX", index);
+                EP_IN_WAKERS[index].wake();
+            }
+            epr.set_dtog_rx(false);
+            epr.set_dtog_tx(false);
+            epr.set_stat_rx(Stat(0));
+            epr.set_stat_tx(Stat(0));
+            epr.set_ctr_rx(!epr.ctr_rx());
+            epr.set_ctr_tx(!epr.ctr_tx());
+            regs.epr(index).write_value(epr);
         }
     }
 }
@@ -168,20 +166,20 @@ fn calc_out_len(len: u16) -> (u16, u16) {
 mod btable {
     use super::*;
 
-    pub(super) unsafe fn write_in<T: Instance>(index: usize, addr: u16) {
+    pub(super) fn write_in<T: Instance>(index: usize, addr: u16) {
         USBRAM.mem(index * 4 + 0).write_value(addr);
     }
 
-    pub(super) unsafe fn write_in_len<T: Instance>(index: usize, _addr: u16, len: u16) {
+    pub(super) fn write_in_len<T: Instance>(index: usize, _addr: u16, len: u16) {
         USBRAM.mem(index * 4 + 1).write_value(len);
     }
 
-    pub(super) unsafe fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
+    pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
         USBRAM.mem(index * 4 + 2).write_value(addr);
         USBRAM.mem(index * 4 + 3).write_value(max_len_bits);
     }
 
-    pub(super) unsafe fn read_out_len<T: Instance>(index: usize) -> u16 {
+    pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 {
         USBRAM.mem(index * 4 + 3).read()
     }
 }
@@ -189,19 +187,19 @@ mod btable {
 mod btable {
     use super::*;
 
-    pub(super) unsafe fn write_in<T: Instance>(_index: usize, _addr: u16) {}
+    pub(super) fn write_in<T: Instance>(_index: usize, _addr: u16) {}
 
-    pub(super) unsafe fn write_in_len<T: Instance>(index: usize, addr: u16, len: u16) {
+    pub(super) fn write_in_len<T: Instance>(index: usize, addr: u16, len: u16) {
         USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16));
     }
 
-    pub(super) unsafe fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
+    pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) {
         USBRAM
             .mem(index * 2 + 1)
             .write_value((addr as u32) | ((max_len_bits as u32) << 16));
     }
 
-    pub(super) unsafe fn read_out_len<T: Instance>(index: usize) -> u16 {
+    pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 {
         (USBRAM.mem(index * 2 + 1).read() >> 16) as u16
     }
 }
@@ -216,7 +214,7 @@ impl<T: Instance> EndpointBuffer<T> {
     fn read(&mut self, buf: &mut [u8]) {
         assert!(buf.len() <= self.len as usize);
         for i in 0..(buf.len() + USBRAM_ALIGN - 1) / USBRAM_ALIGN {
-            let val = unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read() };
+            let val = USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).read();
             let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN);
             buf[i * USBRAM_ALIGN..][..n].copy_from_slice(&val.to_le_bytes()[..n]);
         }
@@ -233,7 +231,7 @@ impl<T: Instance> EndpointBuffer<T> {
             let val = u16::from_le_bytes(val);
             #[cfg(usbram_32_2048)]
             let val = u32::from_le_bytes(val);
-            unsafe { USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val) };
+            USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val);
         }
     }
 }
@@ -266,36 +264,32 @@ impl<'d, T: Instance> Driver<'d, T> {
         let regs = T::regs();
 
         #[cfg(stm32l5)]
-        unsafe {
+        {
             crate::peripherals::PWR::enable();
             crate::pac::PWR.cr2().modify(|w| w.set_usv(true));
         }
 
         #[cfg(pwr_h5)]
-        unsafe {
-            crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true))
-        }
+        crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true));
 
-        unsafe {
-            <T as RccPeripheral>::enable();
-            <T as RccPeripheral>::reset();
+        <T as RccPeripheral>::enable();
+        <T as RccPeripheral>::reset();
 
-            regs.cntr().write(|w| {
-                w.set_pdwn(false);
-                w.set_fres(true);
-            });
+        regs.cntr().write(|w| {
+            w.set_pdwn(false);
+            w.set_fres(true);
+        });
 
-            #[cfg(time)]
-            embassy_time::block_for(embassy_time::Duration::from_millis(100));
-            #[cfg(not(time))]
-            cortex_m::asm::delay(crate::rcc::get_freqs().sys.0 / 10);
+        #[cfg(time)]
+        embassy_time::block_for(embassy_time::Duration::from_millis(100));
+        #[cfg(not(time))]
+        cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.0 / 10);
 
-            #[cfg(not(usb_v4))]
-            regs.btable().write(|w| w.set_btable(0));
+        #[cfg(not(usb_v4))]
+        regs.btable().write(|w| w.set_btable(0));
 
-            dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
-            dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
-        }
+        dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
+        dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
 
         // Initialize the bus so that it signals that power is available
         BUS_WAKER.wake();
@@ -363,7 +357,7 @@ impl<'d, T: Instance> Driver<'d, T> {
                 let addr = self.alloc_ep_mem(len);
 
                 trace!("  len_bits = {:04x}", len_bits);
-                unsafe { btable::write_out::<T>(index, addr, len_bits) }
+                btable::write_out::<T>(index, addr, len_bits);
 
                 EndpointBuffer {
                     addr,
@@ -379,7 +373,7 @@ impl<'d, T: Instance> Driver<'d, T> {
                 let addr = self.alloc_ep_mem(len);
 
                 // ep_in_len is written when actually TXing packets.
-                unsafe { btable::write_in::<T>(index, addr) }
+                btable::write_in::<T>(index, addr);
 
                 EndpointBuffer {
                     addr,
@@ -440,19 +434,17 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
 
         let regs = T::regs();
 
-        unsafe {
-            regs.cntr().write(|w| {
-                w.set_pdwn(false);
-                w.set_fres(false);
-                w.set_resetm(true);
-                w.set_suspm(true);
-                w.set_wkupm(true);
-                w.set_ctrm(true);
-            });
+        regs.cntr().write(|w| {
+            w.set_pdwn(false);
+            w.set_fres(false);
+            w.set_resetm(true);
+            w.set_suspm(true);
+            w.set_wkupm(true);
+            w.set_ctrm(true);
+        });
 
-            #[cfg(any(usb_v3, usb_v4))]
-            regs.bcdr().write(|w| w.set_dppu(true))
-        }
+        #[cfg(any(usb_v3, usb_v4))]
+        regs.bcdr().write(|w| w.set_dppu(true));
 
         trace!("enabled");
 
@@ -485,7 +477,7 @@ pub struct Bus<'d, T: Instance> {
 
 impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
     async fn poll(&mut self) -> Event {
-        poll_fn(move |cx| unsafe {
+        poll_fn(move |cx| {
             BUS_WAKER.register(cx.waker());
 
             if self.inited {
@@ -548,7 +540,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
         match ep_addr.direction() {
             Direction::In => {
                 loop {
-                    let r = unsafe { reg.read() };
+                    let r = reg.read();
                     match r.stat_tx() {
                         Stat::DISABLED => break, // if disabled, stall does nothing.
                         Stat::STALL => break,    // done!
@@ -559,7 +551,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
                             };
                             let mut w = invariant(r);
                             w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
-                            unsafe { reg.write_value(w) };
+                            reg.write_value(w);
                         }
                     }
                 }
@@ -567,7 +559,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
             }
             Direction::Out => {
                 loop {
-                    let r = unsafe { reg.read() };
+                    let r = reg.read();
                     match r.stat_rx() {
                         Stat::DISABLED => break, // if disabled, stall does nothing.
                         Stat::STALL => break,    // done!
@@ -578,7 +570,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
                             };
                             let mut w = invariant(r);
                             w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
-                            unsafe { reg.write_value(w) };
+                            reg.write_value(w);
                         }
                     }
                 }
@@ -589,7 +581,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
 
     fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
         let regs = T::regs();
-        let epr = unsafe { regs.epr(ep_addr.index() as _).read() };
+        let epr = regs.epr(ep_addr.index() as _).read();
         match ep_addr.direction() {
             Direction::In => epr.stat_tx() == Stat::STALL,
             Direction::Out => epr.stat_rx() == Stat::STALL,
@@ -600,7 +592,7 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
         trace!("set_enabled {:x} {}", ep_addr, enabled);
         // This can race, so do a retry loop.
         let reg = T::regs().epr(ep_addr.index() as _);
-        trace!("EPR before: {:04x}", unsafe { reg.read() }.0);
+        trace!("EPR before: {:04x}", reg.read().0);
         match ep_addr.direction() {
             Direction::In => {
                 loop {
@@ -608,13 +600,13 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
                         false => Stat::DISABLED,
                         true => Stat::NAK,
                     };
-                    let r = unsafe { reg.read() };
+                    let r = reg.read();
                     if r.stat_tx() == want_stat {
                         break;
                     }
                     let mut w = invariant(r);
                     w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0));
-                    unsafe { reg.write_value(w) };
+                    reg.write_value(w);
                 }
                 EP_IN_WAKERS[ep_addr.index()].wake();
             }
@@ -624,18 +616,18 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
                         false => Stat::DISABLED,
                         true => Stat::VALID,
                     };
-                    let r = unsafe { reg.read() };
+                    let r = reg.read();
                     if r.stat_rx() == want_stat {
                         break;
                     }
                     let mut w = invariant(r);
                     w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0));
-                    unsafe { reg.write_value(w) };
+                    reg.write_value(w);
                 }
                 EP_OUT_WAKERS[ep_addr.index()].wake();
             }
         }
-        trace!("EPR after: {:04x}", unsafe { reg.read() }.0);
+        trace!("EPR after: {:04x}", reg.read().0);
     }
 
     async fn enable(&mut self) {}
@@ -685,12 +677,12 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> {
     fn write_data(&mut self, buf: &[u8]) {
         let index = self.info.addr.index();
         self.buf.write(buf);
-        unsafe { btable::write_in_len::<T>(index, self.buf.addr, buf.len() as _) }
+        btable::write_in_len::<T>(index, self.buf.addr, buf.len() as _);
     }
 
     fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
         let index = self.info.addr.index();
-        let rx_len = unsafe { btable::read_out_len::<T>(index) as usize } & 0x3FF;
+        let rx_len = btable::read_out_len::<T>(index) as usize & 0x3FF;
         trace!("READ DONE, rx_len = {}", rx_len);
         if rx_len > buf.len() {
             return Err(EndpointError::BufferOverflow);
@@ -711,7 +703,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
         poll_fn(|cx| {
             EP_OUT_WAKERS[index].register(cx.waker());
             let regs = T::regs();
-            if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED {
+            if regs.epr(index).read().stat_tx() == Stat::DISABLED {
                 Poll::Pending
             } else {
                 Poll::Ready(())
@@ -733,7 +725,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> {
         poll_fn(|cx| {
             EP_OUT_WAKERS[index].register(cx.waker());
             let regs = T::regs();
-            if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED {
+            if regs.epr(index).read().stat_rx() == Stat::DISABLED {
                 Poll::Pending
             } else {
                 Poll::Ready(())
@@ -751,7 +743,7 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
         let stat = poll_fn(|cx| {
             EP_OUT_WAKERS[index].register(cx.waker());
             let regs = T::regs();
-            let stat = unsafe { regs.epr(index).read() }.stat_rx();
+            let stat = regs.epr(index).read().stat_rx();
             if matches!(stat, Stat::NAK | Stat::DISABLED) {
                 Poll::Ready(stat)
             } else {
@@ -767,16 +759,14 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
         let rx_len = self.read_data(buf)?;
 
         let regs = T::regs();
-        unsafe {
-            regs.epr(index).write(|w| {
-                w.set_ep_type(convert_type(self.info.ep_type));
-                w.set_ea(self.info.addr.index() as _);
-                w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
-                w.set_stat_tx(Stat(0));
-                w.set_ctr_rx(true); // don't clear
-                w.set_ctr_tx(true); // don't clear
-            })
-        };
+        regs.epr(index).write(|w| {
+            w.set_ep_type(convert_type(self.info.ep_type));
+            w.set_ea(self.info.addr.index() as _);
+            w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
+            w.set_stat_tx(Stat(0));
+            w.set_ctr_rx(true); // don't clear
+            w.set_ctr_tx(true); // don't clear
+        });
         trace!("READ OK, rx_len = {}", rx_len);
 
         Ok(rx_len)
@@ -795,7 +785,7 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
         let stat = poll_fn(|cx| {
             EP_IN_WAKERS[index].register(cx.waker());
             let regs = T::regs();
-            let stat = unsafe { regs.epr(index).read() }.stat_tx();
+            let stat = regs.epr(index).read().stat_tx();
             if matches!(stat, Stat::NAK | Stat::DISABLED) {
                 Poll::Ready(stat)
             } else {
@@ -811,16 +801,14 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
         self.write_data(buf);
 
         let regs = T::regs();
-        unsafe {
-            regs.epr(index).write(|w| {
-                w.set_ep_type(convert_type(self.info.ep_type));
-                w.set_ea(self.info.addr.index() as _);
-                w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
-                w.set_stat_rx(Stat(0));
-                w.set_ctr_rx(true); // don't clear
-                w.set_ctr_tx(true); // don't clear
-            })
-        };
+        regs.epr(index).write(|w| {
+            w.set_ep_type(convert_type(self.info.ep_type));
+            w.set_ea(self.info.addr.index() as _);
+            w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
+            w.set_stat_rx(Stat(0));
+            w.set_ctr_rx(true); // don't clear
+            w.set_ctr_tx(true); // don't clear
+        });
 
         trace!("WRITE OK");
 
@@ -889,22 +877,20 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
             }
             // Note: if this is the first AND last transfer, the above effectively
             // changes stat_tx like NAK -> NAK, so noop.
-            unsafe {
-                regs.epr(0).write(|w| {
-                    w.set_ep_type(EpType::CONTROL);
-                    w.set_stat_rx(Stat(stat_rx));
-                    w.set_stat_tx(Stat(stat_tx));
-                    w.set_ctr_rx(true); // don't clear
-                    w.set_ctr_tx(true); // don't clear
-                })
-            }
+            regs.epr(0).write(|w| {
+                w.set_ep_type(EpType::CONTROL);
+                w.set_stat_rx(Stat(stat_rx));
+                w.set_stat_tx(Stat(stat_tx));
+                w.set_ctr_rx(true); // don't clear
+                w.set_ctr_tx(true); // don't clear
+            });
         }
 
         trace!("data_out WAITING, buf.len() = {}", buf.len());
         poll_fn(|cx| {
             EP_OUT_WAKERS[0].register(cx.waker());
             let regs = T::regs();
-            if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK {
+            if regs.epr(0).read().stat_rx() == Stat::NAK {
                 Poll::Ready(())
             } else {
                 Poll::Pending
@@ -919,19 +905,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
 
         let rx_len = self.ep_out.read_data(buf)?;
 
-        unsafe {
-            regs.epr(0).write(|w| {
-                w.set_ep_type(EpType::CONTROL);
-                w.set_stat_rx(Stat(match last {
-                    // If last, set STAT_RX=STALL.
-                    true => Stat::NAK.0 ^ Stat::STALL.0,
-                    // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
-                    false => Stat::NAK.0 ^ Stat::VALID.0,
-                }));
-                w.set_ctr_rx(true); // don't clear
-                w.set_ctr_tx(true); // don't clear
-            })
-        };
+        regs.epr(0).write(|w| {
+            w.set_ep_type(EpType::CONTROL);
+            w.set_stat_rx(Stat(match last {
+                // If last, set STAT_RX=STALL.
+                true => Stat::NAK.0 ^ Stat::STALL.0,
+                // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet.
+                false => Stat::NAK.0 ^ Stat::VALID.0,
+            }));
+            w.set_ctr_rx(true); // don't clear
+            w.set_ctr_tx(true); // don't clear
+        });
 
         Ok(rx_len)
     }
@@ -960,15 +944,13 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
             }
             // Note: if this is the first AND last transfer, the above effectively
             // does a change of NAK -> VALID.
-            unsafe {
-                regs.epr(0).write(|w| {
-                    w.set_ep_type(EpType::CONTROL);
-                    w.set_stat_rx(Stat(stat_rx));
-                    w.set_ep_kind(last); // set OUT_STATUS if last.
-                    w.set_ctr_rx(true); // don't clear
-                    w.set_ctr_tx(true); // don't clear
-                })
-            }
+            regs.epr(0).write(|w| {
+                w.set_ep_type(EpType::CONTROL);
+                w.set_stat_rx(Stat(stat_rx));
+                w.set_ep_kind(last); // set OUT_STATUS if last.
+                w.set_ctr_rx(true); // don't clear
+                w.set_ctr_tx(true); // don't clear
+            });
         }
 
         trace!("WRITE WAITING");
@@ -976,7 +958,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
             EP_IN_WAKERS[0].register(cx.waker());
             EP_OUT_WAKERS[0].register(cx.waker());
             let regs = T::regs();
-            if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
+            if regs.epr(0).read().stat_tx() == Stat::NAK {
                 Poll::Ready(())
             } else {
                 Poll::Pending
@@ -992,15 +974,13 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
         self.ep_in.write_data(data);
 
         let regs = T::regs();
-        unsafe {
-            regs.epr(0).write(|w| {
-                w.set_ep_type(EpType::CONTROL);
-                w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
-                w.set_ep_kind(last); // set OUT_STATUS if last.
-                w.set_ctr_rx(true); // don't clear
-                w.set_ctr_tx(true); // don't clear
-            })
-        };
+        regs.epr(0).write(|w| {
+            w.set_ep_type(EpType::CONTROL);
+            w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0));
+            w.set_ep_kind(last); // set OUT_STATUS if last.
+            w.set_ctr_rx(true); // don't clear
+            w.set_ctr_tx(true); // don't clear
+        });
 
         trace!("WRITE OK");
 
@@ -1014,16 +994,14 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
         self.ep_in.write_data(&[]);
 
         // Set OUT=stall, IN=accept
-        unsafe {
-            let epr = regs.epr(0).read();
-            regs.epr(0).write(|w| {
-                w.set_ep_type(EpType::CONTROL);
-                w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
-                w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0));
-                w.set_ctr_rx(true); // don't clear
-                w.set_ctr_tx(true); // don't clear
-            });
-        }
+        let epr = regs.epr(0).read();
+        regs.epr(0).write(|w| {
+            w.set_ep_type(EpType::CONTROL);
+            w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
+            w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0));
+            w.set_ctr_rx(true); // don't clear
+            w.set_ctr_tx(true); // don't clear
+        });
         trace!("control: accept WAITING");
 
         // Wait is needed, so that we don't set the address too soon, breaking the status stage.
@@ -1031,7 +1009,7 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
         poll_fn(|cx| {
             EP_IN_WAKERS[0].register(cx.waker());
             let regs = T::regs();
-            if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK {
+            if regs.epr(0).read().stat_tx() == Stat::NAK {
                 Poll::Ready(())
             } else {
                 Poll::Pending
@@ -1047,16 +1025,14 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
         trace!("control: reject");
 
         // Set IN+OUT to stall
-        unsafe {
-            let epr = regs.epr(0).read();
-            regs.epr(0).write(|w| {
-                w.set_ep_type(EpType::CONTROL);
-                w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
-                w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0));
-                w.set_ctr_rx(true); // don't clear
-                w.set_ctr_tx(true); // don't clear
-            });
-        }
+        let epr = regs.epr(0).read();
+        regs.epr(0).write(|w| {
+            w.set_ep_type(EpType::CONTROL);
+            w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0));
+            w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0));
+            w.set_ctr_rx(true); // don't clear
+            w.set_ctr_tx(true); // don't clear
+        });
     }
 
     async fn accept_set_address(&mut self, addr: u8) {
@@ -1064,11 +1040,9 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
 
         let regs = T::regs();
         trace!("setting addr: {}", addr);
-        unsafe {
-            regs.daddr().write(|w| {
-                w.set_ef(true);
-                w.set_add(addr);
-            })
-        }
+        regs.daddr().write(|w| {
+            w.set_ef(true);
+            w.set_add(addr);
+        });
     }
 }
diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs
index 317264cbb..12e5f0e60 100644
--- a/embassy-stm32/src/usb_otg/mod.rs
+++ b/embassy-stm32/src/usb_otg/mod.rs
@@ -148,7 +148,7 @@ foreach_interrupt!(
 
             fn regs() -> crate::pac::otg::Otg {
                 // OTG HS registers are a superset of FS registers
-                crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0)
+                unsafe { crate::pac::otg::Otg::from_ptr(crate::pac::USB_OTG_HS.as_ptr()) }
             }
 
             #[cfg(feature = "nightly")]
diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs
index 16cbf1a95..8af5c7bd5 100644
--- a/embassy-stm32/src/usb_otg/usb.rs
+++ b/embassy-stm32/src/usb_otg/usb.rs
@@ -30,19 +30,16 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
         let r = T::regs();
         let state = T::state();
 
-        // SAFETY: atomic read/write
-        let ints = unsafe { r.gintsts().read() };
+        let ints = r.gintsts().read();
         if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() {
             // Mask interrupts and notify `Bus` to process them
-            unsafe { r.gintmsk().write(|_| {}) };
+            r.gintmsk().write(|_| {});
             T::state().bus_waker.wake();
         }
 
         // Handle RX
-        // SAFETY: atomic read with no side effects
-        while unsafe { r.gintsts().read().rxflvl() } {
-            // SAFETY: atomic "pop" register
-            let status = unsafe { r.grxstsp().read() };
+        while r.gintsts().read().rxflvl() {
+            let status = r.grxstsp().read();
             let ep_num = status.epnum() as usize;
             let len = status.bcnt() as usize;
 
@@ -57,21 +54,15 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
                     if state.ep0_setup_ready.load(Ordering::Relaxed) == false {
                         // SAFETY: exclusive access ensured by atomic bool
                         let data = unsafe { &mut *state.ep0_setup_data.get() };
-                        // SAFETY: FIFO reads are exclusive to this IRQ
-                        unsafe {
-                            data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes());
-                            data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes());
-                        }
+                        data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes());
+                        data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes());
                         state.ep0_setup_ready.store(true, Ordering::Release);
                         state.ep_out_wakers[0].wake();
                     } else {
                         error!("received SETUP before previous finished processing");
                         // discard FIFO
-                        // SAFETY: FIFO reads are exclusive to IRQ
-                        unsafe {
-                            r.fifo(0).read();
-                            r.fifo(0).read();
-                        }
+                        r.fifo(0).read();
+                        r.fifo(0).read();
                     }
                 }
                 vals::Pktstsd::OUT_DATA_RX => {
@@ -84,8 +75,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
 
                         for chunk in buf.chunks_mut(4) {
                             // RX FIFO is shared so always read from fifo(0)
-                            // SAFETY: FIFO reads are exclusive to IRQ
-                            let data = unsafe { r.fifo(0).read().0 };
+                            let data = r.fifo(0).read().0;
                             chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]);
                         }
 
@@ -97,8 +87,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
                         // discard FIFO data
                         let len_words = (len + 3) / 4;
                         for _ in 0..len_words {
-                            // SAFETY: FIFO reads are exclusive to IRQ
-                            unsafe { r.fifo(0).read().data() };
+                            r.fifo(0).read().data();
                         }
                     }
                 }
@@ -114,24 +103,20 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
 
         // IN endpoint interrupt
         if ints.iepint() {
-            // SAFETY: atomic read with no side effects
-            let mut ep_mask = unsafe { r.daint().read().iepint() };
+            let mut ep_mask = r.daint().read().iepint();
             let mut ep_num = 0;
 
             // Iterate over endpoints while there are non-zero bits in the mask
             while ep_mask != 0 {
                 if ep_mask & 1 != 0 {
-                    // SAFETY: atomic read with no side effects
-                    let ep_ints = unsafe { r.diepint(ep_num).read() };
+                    let ep_ints = r.diepint(ep_num).read();
 
                     // clear all
-                    // SAFETY: DIEPINT is exclusive to IRQ
-                    unsafe { r.diepint(ep_num).write_value(ep_ints) };
+                    r.diepint(ep_num).write_value(ep_ints);
 
                     // TXFE is cleared in DIEPEMPMSK
                     if ep_ints.txfe() {
-                        // SAFETY: DIEPEMPMSK is shared with `Endpoint` so critical section is needed for RMW
-                        critical_section::with(|_| unsafe {
+                        critical_section::with(|_| {
                             r.diepempmsk().modify(|w| {
                                 w.set_ineptxfem(w.ineptxfem() & !(1 << ep_num));
                             });
@@ -172,8 +157,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl
 macro_rules! config_ulpi_pins {
     ($($pin:ident),*) => {
         into_ref!($($pin),*);
-        // NOTE(unsafe) Exclusive access to the registers
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             $(
                 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
                 #[cfg(gpio_v2)]
@@ -298,10 +282,8 @@ impl<'d, T: Instance> Driver<'d, T> {
     ) -> Self {
         into_ref!(dp, dm);
 
-        unsafe {
-            dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
-            dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
-        }
+        dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
+        dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
 
         Self {
             phantom: PhantomData,
@@ -508,18 +490,15 @@ pub struct Bus<'d, T: Instance> {
 
 impl<'d, T: Instance> Bus<'d, T> {
     fn restore_irqs() {
-        // SAFETY: atomic write
-        unsafe {
-            T::regs().gintmsk().write(|w| {
-                w.set_usbrst(true);
-                w.set_enumdnem(true);
-                w.set_usbsuspm(true);
-                w.set_wuim(true);
-                w.set_iepint(true);
-                w.set_oepint(true);
-                w.set_rxflvlm(true);
-            });
-        }
+        T::regs().gintmsk().write(|w| {
+            w.set_usbrst(true);
+            w.set_enumdnem(true);
+            w.set_usbsuspm(true);
+            w.set_wuim(true);
+            w.set_iepint(true);
+            w.set_oepint(true);
+            w.set_rxflvlm(true);
+        });
     }
 }
 
@@ -533,8 +512,7 @@ impl<'d, T: Instance> Bus<'d, T> {
         let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
         trace!("configuring rx fifo size={}", rx_fifo_size_words);
 
-        // SAFETY: register is exclusive to `Bus` with `&mut self`
-        unsafe { r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)) };
+        r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words));
 
         // Configure TX (USB in direction) fifo size for each endpoint
         let mut fifo_top = rx_fifo_size_words;
@@ -549,13 +527,10 @@ impl<'d, T: Instance> Bus<'d, T> {
 
                 let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
 
-                // SAFETY: register is exclusive to `Bus` with `&mut self`
-                unsafe {
-                    dieptxf.write(|w| {
-                        w.set_fd(ep.fifo_size_words);
-                        w.set_sa(fifo_top);
-                    });
-                }
+                dieptxf.write(|w| {
+                    w.set_fd(ep.fifo_size_words);
+                    w.set_sa(fifo_top);
+                });
 
                 fifo_top += ep.fifo_size_words;
             }
@@ -575,8 +550,7 @@ impl<'d, T: Instance> Bus<'d, T> {
         // Configure IN endpoints
         for (index, ep) in self.ep_in.iter().enumerate() {
             if let Some(ep) = ep {
-                // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     r.diepctl(index).write(|w| {
                         if index == 0 {
                             w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
@@ -593,8 +567,7 @@ impl<'d, T: Instance> Bus<'d, T> {
         // Configure OUT endpoints
         for (index, ep) in self.ep_out.iter().enumerate() {
             if let Some(ep) = ep {
-                // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Endpoint` so critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     r.doepctl(index).write(|w| {
                         if index == 0 {
                             w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
@@ -618,14 +591,11 @@ impl<'d, T: Instance> Bus<'d, T> {
         }
 
         // Enable IRQs for allocated endpoints
-        // SAFETY: register is exclusive to `Bus` with `&mut self`
-        unsafe {
-            r.daintmsk().modify(|w| {
-                w.set_iepm(ep_irq_mask(&self.ep_in));
-                // OUT interrupts not used, handled in RXFLVL
-                // w.set_oepm(ep_irq_mask(&self.ep_out));
-            });
-        }
+        r.daintmsk().modify(|w| {
+            w.set_iepm(ep_irq_mask(&self.ep_in));
+            // OUT interrupts not used, handled in RXFLVL
+            // w.set_oepm(ep_irq_mask(&self.ep_out));
+        });
     }
 
     fn disable(&mut self) {
@@ -634,10 +604,8 @@ impl<'d, T: Instance> Bus<'d, T> {
         <T as RccPeripheral>::disable();
 
         #[cfg(stm32l4)]
-        unsafe {
-            crate::pac::PWR.cr2().modify(|w| w.set_usv(false));
-            // Cannot disable PWR, because other peripherals might be using it
-        }
+        crate::pac::PWR.cr2().modify(|w| w.set_usv(false));
+        // Cannot disable PWR, because other peripherals might be using it
     }
 }
 
@@ -653,7 +621,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
 
             T::state().bus_waker.register(cx.waker());
 
-            let ints = unsafe { r.gintsts().read() };
+            let ints = r.gintsts().read();
             if ints.usbrst() {
                 trace!("reset");
 
@@ -661,34 +629,27 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
                 self.configure_endpoints();
 
                 // Reset address
-                // SAFETY: DCFG is shared with `ControlPipe` so critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     r.dcfg().modify(|w| {
                         w.set_dad(0);
                     });
                 });
 
-                // SAFETY: atomic clear on rc_w1 register
-                unsafe { r.gintsts().write(|w| w.set_usbrst(true)) }; // clear
+                r.gintsts().write(|w| w.set_usbrst(true)); // clear
                 Self::restore_irqs();
             }
 
             if ints.enumdne() {
                 trace!("enumdne");
 
-                // SAFETY: atomic read with no side effects
-                let speed = unsafe { r.dsts().read().enumspd() };
+                let speed = r.dsts().read().enumspd();
                 trace!("  speed={}", speed.0);
 
-                // SAFETY: register is only accessed by `Bus` under `&mut self`
-                unsafe {
-                    r.gusbcfg().modify(|w| {
-                        w.set_trdt(calculate_trdt(speed, T::frequency()));
-                    })
-                };
+                r.gusbcfg().modify(|w| {
+                    w.set_trdt(calculate_trdt(speed, T::frequency()));
+                });
 
-                // SAFETY: atomic clear on rc_w1 register
-                unsafe { r.gintsts().write(|w| w.set_enumdne(true)) }; // clear
+                r.gintsts().write(|w| w.set_enumdne(true)); // clear
                 Self::restore_irqs();
 
                 return Poll::Ready(Event::Reset);
@@ -696,16 +657,14 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
 
             if ints.usbsusp() {
                 trace!("suspend");
-                // SAFETY: atomic clear on rc_w1 register
-                unsafe { r.gintsts().write(|w| w.set_usbsusp(true)) }; // clear
+                r.gintsts().write(|w| w.set_usbsusp(true)); // clear
                 Self::restore_irqs();
                 return Poll::Ready(Event::Suspend);
             }
 
             if ints.wkupint() {
                 trace!("resume");
-                // SAFETY: atomic clear on rc_w1 register
-                unsafe { r.gintsts().write(|w| w.set_wkupint(true)) }; // clear
+                r.gintsts().write(|w| w.set_wkupint(true)); // clear
                 Self::restore_irqs();
                 return Poll::Ready(Event::Resume);
             }
@@ -727,8 +686,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
         let regs = T::regs();
         match ep_addr.direction() {
             Direction::Out => {
-                // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     regs.doepctl(ep_addr.index()).modify(|w| {
                         w.set_stall(stalled);
                     });
@@ -737,8 +695,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
                 T::state().ep_out_wakers[ep_addr.index()].wake();
             }
             Direction::In => {
-                // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     regs.diepctl(ep_addr.index()).modify(|w| {
                         w.set_stall(stalled);
                     });
@@ -758,10 +715,9 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
 
         let regs = T::regs();
 
-        // SAFETY: atomic read with no side effects
         match ep_addr.direction() {
-            Direction::Out => unsafe { regs.doepctl(ep_addr.index()).read().stall() },
-            Direction::In => unsafe { regs.diepctl(ep_addr.index()).read().stall() },
+            Direction::Out => regs.doepctl(ep_addr.index()).read().stall(),
+            Direction::In => regs.diepctl(ep_addr.index()).read().stall(),
         }
     }
 
@@ -777,8 +733,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
         let r = T::regs();
         match ep_addr.direction() {
             Direction::Out => {
-                // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     // cancel transfer if active
                     if !enabled && r.doepctl(ep_addr.index()).read().epena() {
                         r.doepctl(ep_addr.index()).modify(|w| {
@@ -796,8 +751,7 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
                 T::state().ep_out_wakers[ep_addr.index()].wake();
             }
             Direction::In => {
-                // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     // cancel transfer if active
                     if !enabled && r.diepctl(ep_addr.index()).read().epena() {
                         r.diepctl(ep_addr.index()).modify(|w| {
@@ -820,196 +774,193 @@ impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
     async fn enable(&mut self) {
         trace!("enable");
 
-        // SAFETY: registers are only accessed by `Bus` under `&mut self`
-        unsafe {
-            #[cfg(stm32l4)]
-            {
-                crate::peripherals::PWR::enable();
-                critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));
-            }
-
-            #[cfg(stm32f7)]
-            {
-                // Enable ULPI clock if external PHY is used
-                let ulpien = !self.phy_type.internal();
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb1enr().modify(|w| {
-                        if T::HIGH_SPEED {
-                            w.set_usb_otg_hsulpien(ulpien);
-                        } else {
-                            w.set_usb_otg_hsen(ulpien);
-                        }
-                    });
-
-                    // Low power mode
-                    crate::pac::RCC.ahb1lpenr().modify(|w| {
-                        if T::HIGH_SPEED {
-                            w.set_usb_otg_hsulpilpen(ulpien);
-                        } else {
-                            w.set_usb_otg_hslpen(ulpien);
-                        }
-                    });
-                });
-            }
-
-            #[cfg(stm32h7)]
-            {
-                // If true, VDD33USB is generated by internal regulator from VDD50USB
-                // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
-                // TODO: unhardcode
-                let internal_regulator = false;
-
-                // Enable USB power
-                critical_section::with(|_| {
-                    crate::pac::PWR.cr3().modify(|w| {
-                        w.set_usb33den(true);
-                        w.set_usbregen(internal_regulator);
-                    })
-                });
-
-                // Wait for USB power to stabilize
-                while !crate::pac::PWR.cr3().read().usb33rdy() {}
-
-                // Use internal 48MHz HSI clock. Should be enabled in RCC by default.
-                critical_section::with(|_| {
-                    crate::pac::RCC
-                        .d2ccip2r()
-                        .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48))
-                });
-
-                // Enable ULPI clock if external PHY is used
-                let ulpien = !self.phy_type.internal();
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb1enr().modify(|w| {
-                        if T::HIGH_SPEED {
-                            w.set_usb_otg_hs_ulpien(ulpien);
-                        } else {
-                            w.set_usb_otg_fs_ulpien(ulpien);
-                        }
-                    });
-                    crate::pac::RCC.ahb1lpenr().modify(|w| {
-                        if T::HIGH_SPEED {
-                            w.set_usb_otg_hs_ulpilpen(ulpien);
-                        } else {
-                            w.set_usb_otg_fs_ulpilpen(ulpien);
-                        }
-                    });
-                });
-            }
-
-            #[cfg(stm32u5)]
-            {
-                // Enable USB power
-                critical_section::with(|_| {
-                    crate::pac::RCC.ahb3enr().modify(|w| {
-                        w.set_pwren(true);
-                    });
-                    cortex_m::asm::delay(2);
-
-                    crate::pac::PWR.svmcr().modify(|w| {
-                        w.set_usv(true);
-                        w.set_uvmen(true);
-                    });
-                });
-
-                // Wait for USB power to stabilize
-                while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
-
-                // Select HSI48 as USB clock source.
-                critical_section::with(|_| {
-                    crate::pac::RCC.ccipr1().modify(|w| {
-                        w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48);
-                    })
-                });
-            }
-
-            <T as RccPeripheral>::enable();
-            <T as RccPeripheral>::reset();
-
-            T::Interrupt::unpend();
-            T::Interrupt::enable();
-
-            let r = T::regs();
-            let core_id = r.cid().read().0;
-            info!("Core id {:08x}", core_id);
-
-            // Wait for AHB ready.
-            while !r.grstctl().read().ahbidl() {}
-
-            // Configure as device.
-            r.gusbcfg().write(|w| {
-                // Force device mode
-                w.set_fdmod(true);
-                // Enable internal full-speed PHY
-                w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed());
-            });
-
-            // Configuring Vbus sense and SOF output
-            match core_id {
-                0x0000_1200 | 0x0000_1100 => {
-                    assert!(self.phy_type != PhyType::InternalHighSpeed);
-
-                    r.gccfg_v1().modify(|w| {
-                        // Enable internal full-speed PHY, logic is inverted
-                        w.set_pwrdwn(self.phy_type.internal());
-                    });
-
-                    // F429-like chips have the GCCFG.NOVBUSSENS bit
-                    r.gccfg_v1().modify(|w| {
-                        w.set_novbussens(true);
-                        w.set_vbusasen(false);
-                        w.set_vbusbsen(false);
-                        w.set_sofouten(false);
-                    });
-                }
-                0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => {
-                    // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning
-                    r.gccfg_v2().modify(|w| {
-                        // Enable internal full-speed PHY, logic is inverted
-                        w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed());
-                        w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed());
-                    });
-
-                    r.gccfg_v2().modify(|w| {
-                        w.set_vbden(false);
-                    });
-
-                    // Force B-peripheral session
-                    r.gotgctl().modify(|w| {
-                        w.set_bvaloen(true);
-                        w.set_bvaloval(true);
-                    });
-                }
-                _ => unimplemented!("Unknown USB core id {:X}", core_id),
-            }
-
-            // Soft disconnect.
-            r.dctl().write(|w| w.set_sdis(true));
-
-            // Set speed.
-            r.dcfg().write(|w| {
-                w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80);
-                w.set_dspd(self.phy_type.to_dspd());
-            });
-
-            // Unmask transfer complete EP interrupt
-            r.diepmsk().write(|w| {
-                w.set_xfrcm(true);
-            });
-
-            // Unmask and clear core interrupts
-            Bus::<T>::restore_irqs();
-            r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF));
-
-            // Unmask global interrupt
-            r.gahbcfg().write(|w| {
-                w.set_gint(true); // unmask global interrupt
-            });
-
-            // Connect
-            r.dctl().write(|w| w.set_sdis(false));
+        #[cfg(stm32l4)]
+        {
+            crate::peripherals::PWR::enable();
+            critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));
         }
 
+        #[cfg(stm32f7)]
+        {
+            // Enable ULPI clock if external PHY is used
+            let ulpien = !self.phy_type.internal();
+            critical_section::with(|_| {
+                crate::pac::RCC.ahb1enr().modify(|w| {
+                    if T::HIGH_SPEED {
+                        w.set_usb_otg_hsulpien(ulpien);
+                    } else {
+                        w.set_usb_otg_hsen(ulpien);
+                    }
+                });
+
+                // Low power mode
+                crate::pac::RCC.ahb1lpenr().modify(|w| {
+                    if T::HIGH_SPEED {
+                        w.set_usb_otg_hsulpilpen(ulpien);
+                    } else {
+                        w.set_usb_otg_hslpen(ulpien);
+                    }
+                });
+            });
+        }
+
+        #[cfg(stm32h7)]
+        {
+            // If true, VDD33USB is generated by internal regulator from VDD50USB
+            // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
+            // TODO: unhardcode
+            let internal_regulator = false;
+
+            // Enable USB power
+            critical_section::with(|_| {
+                crate::pac::PWR.cr3().modify(|w| {
+                    w.set_usb33den(true);
+                    w.set_usbregen(internal_regulator);
+                })
+            });
+
+            // Wait for USB power to stabilize
+            while !crate::pac::PWR.cr3().read().usb33rdy() {}
+
+            // Use internal 48MHz HSI clock. Should be enabled in RCC by default.
+            critical_section::with(|_| {
+                crate::pac::RCC
+                    .d2ccip2r()
+                    .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48))
+            });
+
+            // Enable ULPI clock if external PHY is used
+            let ulpien = !self.phy_type.internal();
+            critical_section::with(|_| {
+                crate::pac::RCC.ahb1enr().modify(|w| {
+                    if T::HIGH_SPEED {
+                        w.set_usb_otg_hs_ulpien(ulpien);
+                    } else {
+                        w.set_usb_otg_fs_ulpien(ulpien);
+                    }
+                });
+                crate::pac::RCC.ahb1lpenr().modify(|w| {
+                    if T::HIGH_SPEED {
+                        w.set_usb_otg_hs_ulpilpen(ulpien);
+                    } else {
+                        w.set_usb_otg_fs_ulpilpen(ulpien);
+                    }
+                });
+            });
+        }
+
+        #[cfg(stm32u5)]
+        {
+            // Enable USB power
+            critical_section::with(|_| {
+                crate::pac::RCC.ahb3enr().modify(|w| {
+                    w.set_pwren(true);
+                });
+                cortex_m::asm::delay(2);
+
+                crate::pac::PWR.svmcr().modify(|w| {
+                    w.set_usv(true);
+                    w.set_uvmen(true);
+                });
+            });
+
+            // Wait for USB power to stabilize
+            while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
+
+            // Select HSI48 as USB clock source.
+            critical_section::with(|_| {
+                crate::pac::RCC.ccipr1().modify(|w| {
+                    w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48);
+                })
+            });
+        }
+
+        <T as RccPeripheral>::enable();
+        <T as RccPeripheral>::reset();
+
+        T::Interrupt::unpend();
+        unsafe { T::Interrupt::enable() };
+
+        let r = T::regs();
+        let core_id = r.cid().read().0;
+        info!("Core id {:08x}", core_id);
+
+        // Wait for AHB ready.
+        while !r.grstctl().read().ahbidl() {}
+
+        // Configure as device.
+        r.gusbcfg().write(|w| {
+            // Force device mode
+            w.set_fdmod(true);
+            // Enable internal full-speed PHY
+            w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed());
+        });
+
+        // Configuring Vbus sense and SOF output
+        match core_id {
+            0x0000_1200 | 0x0000_1100 => {
+                assert!(self.phy_type != PhyType::InternalHighSpeed);
+
+                r.gccfg_v1().modify(|w| {
+                    // Enable internal full-speed PHY, logic is inverted
+                    w.set_pwrdwn(self.phy_type.internal());
+                });
+
+                // F429-like chips have the GCCFG.NOVBUSSENS bit
+                r.gccfg_v1().modify(|w| {
+                    w.set_novbussens(true);
+                    w.set_vbusasen(false);
+                    w.set_vbusbsen(false);
+                    w.set_sofouten(false);
+                });
+            }
+            0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => {
+                // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning
+                r.gccfg_v2().modify(|w| {
+                    // Enable internal full-speed PHY, logic is inverted
+                    w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed());
+                    w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed());
+                });
+
+                r.gccfg_v2().modify(|w| {
+                    w.set_vbden(false);
+                });
+
+                // Force B-peripheral session
+                r.gotgctl().modify(|w| {
+                    w.set_bvaloen(true);
+                    w.set_bvaloval(true);
+                });
+            }
+            _ => unimplemented!("Unknown USB core id {:X}", core_id),
+        }
+
+        // Soft disconnect.
+        r.dctl().write(|w| w.set_sdis(true));
+
+        // Set speed.
+        r.dcfg().write(|w| {
+            w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80);
+            w.set_dspd(self.phy_type.to_dspd());
+        });
+
+        // Unmask transfer complete EP interrupt
+        r.diepmsk().write(|w| {
+            w.set_xfrcm(true);
+        });
+
+        // Unmask and clear core interrupts
+        Bus::<T>::restore_irqs();
+        r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF));
+
+        // Unmask global interrupt
+        r.gahbcfg().write(|w| {
+            w.set_gint(true); // unmask global interrupt
+        });
+
+        // Connect
+        r.dctl().write(|w| w.set_sdis(false));
+
         self.enabled = true;
     }
 
@@ -1066,8 +1017,7 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> {
 
             T::state().ep_in_wakers[ep_index].register(cx.waker());
 
-            // SAFETY: atomic read without side effects
-            if unsafe { T::regs().diepctl(ep_index).read().usbaep() } {
+            if T::regs().diepctl(ep_index).read().usbaep() {
                 Poll::Ready(())
             } else {
                 Poll::Pending
@@ -1088,8 +1038,7 @@ impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> {
 
             T::state().ep_out_wakers[ep_index].register(cx.waker());
 
-            // SAFETY: atomic read without side effects
-            if unsafe { T::regs().doepctl(ep_index).read().usbaep() } {
+            if T::regs().doepctl(ep_index).read().usbaep() {
                 Poll::Ready(())
             } else {
                 Poll::Pending
@@ -1124,8 +1073,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> {
                 // Release buffer
                 state.ep_out_size[index].store(EP_OUT_BUFFER_EMPTY, Ordering::Release);
 
-                // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Bus` so a critical section is needed for RMW
-                critical_section::with(|_| unsafe {
+                critical_section::with(|_| {
                     // Receive 1 packet
                     T::regs().doeptsiz(index).modify(|w| {
                         w.set_xfrsiz(self.info.max_packet_size as _);
@@ -1163,8 +1111,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
         poll_fn(|cx| {
             state.ep_in_wakers[index].register(cx.waker());
 
-            // SAFETY: atomic read with no side effects
-            let diepctl = unsafe { r.diepctl(index).read() };
+            let diepctl = r.diepctl(index).read();
             if !diepctl.usbaep() {
                 Poll::Ready(Err(EndpointError::Disabled))
             } else if !diepctl.epena() {
@@ -1181,12 +1128,10 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
 
                 let size_words = (buf.len() + 3) / 4;
 
-                // SAFETY: atomic read with no side effects
-                let fifo_space = unsafe { r.dtxfsts(index).read().ineptfsav() as usize };
+                let fifo_space = r.dtxfsts(index).read().ineptfsav() as usize;
                 if size_words > fifo_space {
                     // Not enough space in fifo, enable tx fifo empty interrupt
-                    // SAFETY: DIEPEMPMSK is shared with IRQ so critical section is needed for RMW
-                    critical_section::with(|_| unsafe {
+                    critical_section::with(|_| {
                         r.diepempmsk().modify(|w| {
                             w.set_ineptxfem(w.ineptxfem() | (1 << index));
                         });
@@ -1202,18 +1147,14 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
             .await
         }
 
-        // SAFETY: DIEPTSIZ is exclusive to this endpoint under `&mut self`
-        unsafe {
-            // Setup transfer size
-            r.dieptsiz(index).write(|w| {
-                w.set_mcnt(1);
-                w.set_pktcnt(1);
-                w.set_xfrsiz(buf.len() as _);
-            });
-        }
+        // Setup transfer size
+        r.dieptsiz(index).write(|w| {
+            w.set_mcnt(1);
+            w.set_pktcnt(1);
+            w.set_xfrsiz(buf.len() as _);
+        });
 
-        // SAFETY: DIEPCTL is shared with `Bus` so a critical section is needed for RMW
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             // Enable endpoint
             r.diepctl(index).modify(|w| {
                 w.set_cnak(true);
@@ -1225,8 +1166,7 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
         for chunk in buf.chunks(4) {
             let mut tmp = [0u8; 4];
             tmp[0..chunk.len()].copy_from_slice(chunk);
-            // SAFETY: FIFO is exclusive to this endpoint under `&mut self`
-            unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) };
+            r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp)));
         }
 
         trace!("write done ep={:?}", self.info.addr);
@@ -1258,17 +1198,15 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> {
                 state.ep0_setup_ready.store(false, Ordering::Release);
 
                 // EP0 should not be controlled by `Bus` so this RMW does not need a critical section
-                unsafe {
-                    // Receive 1 SETUP packet
-                    T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| {
-                        w.set_rxdpid_stupcnt(1);
-                    });
+                // Receive 1 SETUP packet
+                T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| {
+                    w.set_rxdpid_stupcnt(1);
+                });
 
-                    // Clear NAK to indicate we are ready to receive more data
-                    T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| {
-                        w.set_cnak(true);
-                    });
-                }
+                // Clear NAK to indicate we are ready to receive more data
+                T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| {
+                    w.set_cnak(true);
+                });
 
                 trace!("SETUP received: {:?}", data);
                 Poll::Ready(data)
@@ -1313,20 +1251,18 @@ impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> {
         trace!("control: reject");
 
         // EP0 should not be controlled by `Bus` so this RMW does not need a critical section
-        unsafe {
-            let regs = T::regs();
-            regs.diepctl(self.ep_in.info.addr.index()).modify(|w| {
-                w.set_stall(true);
-            });
-            regs.doepctl(self.ep_out.info.addr.index()).modify(|w| {
-                w.set_stall(true);
-            });
-        }
+        let regs = T::regs();
+        regs.diepctl(self.ep_in.info.addr.index()).modify(|w| {
+            w.set_stall(true);
+        });
+        regs.doepctl(self.ep_out.info.addr.index()).modify(|w| {
+            w.set_stall(true);
+        });
     }
 
     async fn accept_set_address(&mut self, addr: u8) {
         trace!("setting addr: {}", addr);
-        critical_section::with(|_| unsafe {
+        critical_section::with(|_| {
             T::regs().dcfg().modify(|w| {
                 w.set_dad(addr);
             });
diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs
index 18ebf97d8..5907a4e54 100644
--- a/embassy-stm32/src/wdg/mod.rs
+++ b/embassy-stm32/src/wdg/mod.rs
@@ -48,11 +48,9 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
         let rl = reload_value(psc, timeout_us);
 
         let wdg = T::regs();
-        unsafe {
-            wdg.kr().write(|w| w.set_key(Key::ENABLE));
-            wdg.pr().write(|w| w.set_pr(Pr(pr)));
-            wdg.rlr().write(|w| w.set_rl(rl));
-        }
+        wdg.kr().write(|w| w.set_key(Key::ENABLE));
+        wdg.pr().write(|w| w.set_pr(Pr(pr)));
+        wdg.rlr().write(|w| w.set_rl(rl));
 
         trace!(
             "Watchdog configured with {}us timeout, desired was {}us (PR={}, RL={})",
@@ -67,11 +65,11 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> {
         }
     }
 
-    pub unsafe fn unleash(&mut self) {
+    pub fn unleash(&mut self) {
         T::regs().kr().write(|w| w.set_key(Key::START));
     }
 
-    pub unsafe fn pet(&mut self) {
+    pub fn pet(&mut self) {
         T::regs().kr().write(|w| w.set_key(Key::RESET));
     }
 }
diff --git a/examples/stm32f0/src/bin/wdg.rs b/examples/stm32f0/src/bin/wdg.rs
index 80e76f901..a44b17528 100644
--- a/examples/stm32f0/src/bin/wdg.rs
+++ b/examples/stm32f0/src/bin/wdg.rs
@@ -16,10 +16,10 @@ async fn main(_spawner: Spawner) {
     let mut wdg = IndependentWatchdog::new(p.IWDG, 20_000_00);
 
     info!("Watchdog start");
-    unsafe { wdg.unleash() };
+    wdg.unleash();
 
     loop {
         Timer::after(Duration::from_secs(1)).await;
-        unsafe { wdg.pet() };
+        wdg.pet();
     }
 }
diff --git a/examples/stm32f4/src/bin/wdt.rs b/examples/stm32f4/src/bin/wdt.rs
index b2c587fa1..e5d122af7 100644
--- a/examples/stm32f4/src/bin/wdt.rs
+++ b/examples/stm32f4/src/bin/wdt.rs
@@ -17,9 +17,7 @@ async fn main(_spawner: Spawner) {
     let mut led = Output::new(p.PB7, Level::High, Speed::Low);
 
     let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000);
-    unsafe {
-        wdt.unleash();
-    }
+    wdt.unleash();
 
     let mut i = 0;
 
@@ -36,9 +34,7 @@ async fn main(_spawner: Spawner) {
         // MCU should restart in 1 second after the last pet.
         if i < 5 {
             info!("Petting watchdog");
-            unsafe {
-                wdt.pet();
-            }
+            wdt.pet();
         }
 
         i += 1;
diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs
index ecbe3a6e6..c111a9787 100644
--- a/examples/stm32g4/src/bin/usb_serial.rs
+++ b/examples/stm32g4/src/bin/usb_serial.rs
@@ -38,9 +38,7 @@ async fn main(_spawner: Spawner) {
     let p = embassy_stm32::init(config);
     info!("Hello World!");
 
-    unsafe {
-        pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10));
-    }
+    pac::RCC.ccipr().write(|w| w.set_clk48sel(0b10));
 
     let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
 
diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs
index 3912327e2..336eed644 100644
--- a/examples/stm32h5/src/bin/usb_serial.rs
+++ b/examples/stm32h5/src/bin/usb_serial.rs
@@ -45,11 +45,9 @@ async fn main(_spawner: Spawner) {
 
     info!("Hello World!");
 
-    unsafe {
-        pac::RCC.ccipr4().write(|w| {
-            w.set_usbsel(pac::rcc::vals::Usbsel::HSI48);
-        });
-    }
+    pac::RCC.ccipr4().write(|w| {
+        w.set_usbsel(pac::rcc::vals::Usbsel::HSI48);
+    });
 
     // Create the driver, from the HAL.
     let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11);
diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs
index 1972f8ff2..d360df085 100644
--- a/examples/stm32h7/src/bin/low_level_timer_api.rs
+++ b/examples/stm32h7/src/bin/low_level_timer_api.rs
@@ -62,49 +62,39 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
         T::enable();
         <T as embassy_stm32::rcc::low_level::RccPeripheral>::reset();
 
-        unsafe {
-            ch1.set_speed(Speed::VeryHigh);
-            ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull);
-            ch2.set_speed(Speed::VeryHigh);
-            ch2.set_as_af(ch1.af_num(), AFType::OutputPushPull);
-            ch3.set_speed(Speed::VeryHigh);
-            ch3.set_as_af(ch1.af_num(), AFType::OutputPushPull);
-            ch4.set_speed(Speed::VeryHigh);
-            ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull);
-        }
+        ch1.set_speed(Speed::VeryHigh);
+        ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull);
+        ch2.set_speed(Speed::VeryHigh);
+        ch2.set_as_af(ch1.af_num(), AFType::OutputPushPull);
+        ch3.set_speed(Speed::VeryHigh);
+        ch3.set_as_af(ch1.af_num(), AFType::OutputPushPull);
+        ch4.set_speed(Speed::VeryHigh);
+        ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull);
 
         let mut this = Self { inner: tim };
 
         this.set_freq(freq);
         this.inner.start();
 
-        unsafe {
-            T::regs_gp32()
-                .ccmr_output(0)
-                .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
-            T::regs_gp32()
-                .ccmr_output(0)
-                .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));
-            T::regs_gp32()
-                .ccmr_output(1)
-                .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
-            T::regs_gp32()
-                .ccmr_output(1)
-                .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));
-        }
+        let r = T::regs_gp32();
+        r.ccmr_output(0)
+            .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
+        r.ccmr_output(0)
+            .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));
+        r.ccmr_output(1)
+            .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into()));
+        r.ccmr_output(1)
+            .modify(|w| w.set_ocm(1, OutputCompareMode::PwmMode1.into()));
+
         this
     }
 
     pub fn enable(&mut self, channel: Channel) {
-        unsafe {
-            T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true));
-        }
+        T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), true));
     }
 
     pub fn disable(&mut self, channel: Channel) {
-        unsafe {
-            T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false));
-        }
+        T::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), false));
     }
 
     pub fn set_freq(&mut self, freq: Hertz) {
@@ -112,11 +102,11 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> {
     }
 
     pub fn get_max_duty(&self) -> u32 {
-        unsafe { T::regs_gp32().arr().read().arr() }
+        T::regs_gp32().arr().read().arr()
     }
 
     pub fn set_duty(&mut self, channel: Channel, duty: u32) {
         defmt::assert!(duty < self.get_max_duty());
-        unsafe { T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty)) }
+        T::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(duty))
     }
 }
diff --git a/examples/stm32h7/src/bin/wdg.rs b/examples/stm32h7/src/bin/wdg.rs
index 2b0301aad..9181dfd67 100644
--- a/examples/stm32h7/src/bin/wdg.rs
+++ b/examples/stm32h7/src/bin/wdg.rs
@@ -15,10 +15,10 @@ async fn main(_spawner: Spawner) {
 
     let mut wdg = IndependentWatchdog::new(p.IWDG1, 20_000_000);
 
-    unsafe { wdg.unleash() };
+    wdg.unleash();
 
     loop {
         Timer::after(Duration::from_secs(1)).await;
-        unsafe { wdg.pet() };
+        wdg.pet();
     }
 }
diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs
index 281346e5f..1771e5202 100644
--- a/examples/stm32l4/src/bin/adc.rs
+++ b/examples/stm32l4/src/bin/adc.rs
@@ -12,12 +12,10 @@ use {defmt_rtt as _, panic_probe as _};
 fn main() -> ! {
     info!("Hello World!");
 
-    unsafe {
-        pac::RCC.ccipr().modify(|w| {
-            w.set_adcsel(0b11);
-        });
-        pac::RCC.ahb2enr().modify(|w| w.set_adcen(true));
-    }
+    pac::RCC.ccipr().modify(|w| {
+        w.set_adcsel(0b11);
+    });
+    pac::RCC.ahb2enr().modify(|w| w.set_adcen(true));
 
     let p = embassy_stm32::init(Default::default());
 
diff --git a/examples/stm32l4/src/bin/dac.rs b/examples/stm32l4/src/bin/dac.rs
index d6e744aa6..a36ed5d90 100644
--- a/examples/stm32l4/src/bin/dac.rs
+++ b/examples/stm32l4/src/bin/dac.rs
@@ -11,11 +11,9 @@ use {defmt_rtt as _, panic_probe as _};
 fn main() -> ! {
     info!("Hello World!");
 
-    unsafe {
-        pac::RCC.apb1enr1().modify(|w| {
-            w.set_dac1en(true);
-        });
-    }
+    pac::RCC.apb1enr1().modify(|w| {
+        w.set_dac1en(true);
+    });
 
     let p = embassy_stm32::init(Default::default());
 
diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs
index ae36a7e79..9fc4b8aac 100644
--- a/examples/stm32wb/src/bin/tl_mbox.rs
+++ b/examples/stm32wb/src/bin/tl_mbox.rs
@@ -5,14 +5,14 @@
 use defmt::*;
 use embassy_executor::Spawner;
 use embassy_stm32::bind_interrupts;
-use embassy_stm32::ipcc::Config;
+use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
 use embassy_stm32_wpan::TlMbox;
 use embassy_time::{Duration, Timer};
 use {defmt_rtt as _, panic_probe as _};
 
 bind_interrupts!(struct Irqs{
-    IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler;
-    IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler;
+    IPCC_C1_RX => ReceiveInterruptHandler;
+    IPCC_C1_TX => TransmitInterruptHandler;
 });
 
 #[embassy_executor::main]
@@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) {
     let mbox = TlMbox::init(p.IPCC, Irqs, config);
 
     loop {
-        let wireless_fw_info = mbox.wireless_fw_info();
+        let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info();
         match wireless_fw_info {
             None => info!("not yet initialized"),
             Some(fw_info) => {
diff --git a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
index 3132ab3e4..439bd01ac 100644
--- a/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
+++ b/examples/stm32wb/src/bin/tl_mbox_tx_rx.rs
@@ -5,14 +5,13 @@
 use defmt::*;
 use embassy_executor::Spawner;
 use embassy_stm32::bind_interrupts;
-use embassy_stm32::ipcc::Config;
-use embassy_stm32_wpan::rc::RadioCoprocessor;
+use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
 use embassy_stm32_wpan::TlMbox;
 use {defmt_rtt as _, panic_probe as _};
 
 bind_interrupts!(struct Irqs{
-    IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler;
-    IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler;
+    IPCC_C1_RX => ReceiveInterruptHandler;
+    IPCC_C1_TX => TransmitInterruptHandler;
 });
 
 #[embassy_executor::main]
@@ -47,14 +46,18 @@ async fn main(_spawner: Spawner) {
     let config = Config::default();
     let mbox = TlMbox::init(p.IPCC, Irqs, config);
 
-    let mut rc = RadioCoprocessor::new(mbox);
+    let sys_event = mbox.sys_subsystem.read().await;
+    info!("sys event: {}", sys_event.payload());
 
-    let response = rc.read().await;
-    info!("coprocessor ready {}", response);
+    mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await;
 
-    rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]);
-    let response = rc.read().await;
-    info!("ble reset rsp {}", response);
+    info!("starting ble...");
+    mbox.ble_subsystem.write(0x0c, &[]).await;
+
+    info!("waiting for ble...");
+    let ble_event = mbox.ble_subsystem.read().await;
+
+    info!("ble event: {}", ble_event.payload());
 
     info!("Test OK");
     cortex_m::asm::bkpt();
diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs
index e179c5ca1..805d21418 100644
--- a/examples/stm32wl/src/bin/lora_lorawan.rs
+++ b/examples/stm32wl/src/bin/lora_lorawan.rs
@@ -35,7 +35,7 @@ async fn main(_spawner: Spawner) {
     config.rcc.enable_lsi = true; // enable RNG
     let p = embassy_stm32::init(config);
 
-    unsafe { pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01)) }
+    pac::RCC.ccipr().modify(|w| w.set_rngsel(0b01));
 
     let spi = Spi::new_subghz(p.SUBGHZSPI, p.DMA1_CH1, p.DMA1_CH2);
 
diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs
index 182c607f9..d8562fca5 100644
--- a/examples/stm32wl/src/bin/random.rs
+++ b/examples/stm32wl/src/bin/random.rs
@@ -15,11 +15,9 @@ async fn main(_spawner: Spawner) {
     config.rcc.enable_lsi = true; //Needed for RNG to work
 
     let p = embassy_stm32::init(config);
-    unsafe {
-        pac::RCC.ccipr().modify(|w| {
-            w.set_rngsel(0b01);
-        });
-    }
+    pac::RCC.ccipr().modify(|w| {
+        w.set_rngsel(0b01);
+    });
 
     info!("Hello World!");
 
diff --git a/tests/stm32/src/bin/rtc.rs b/tests/stm32/src/bin/rtc.rs
index 32d35c42c..582df5753 100644
--- a/tests/stm32/src/bin/rtc.rs
+++ b/tests/stm32/src/bin/rtc.rs
@@ -24,10 +24,8 @@ async fn main(_spawner: Spawner) {
 
     info!("Starting LSI");
 
-    unsafe {
-        pac::RCC.csr().modify(|w| w.set_lsion(true));
-        while !pac::RCC.csr().read().lsirdy() {}
-    }
+    pac::RCC.csr().modify(|w| w.set_lsion(true));
+    while !pac::RCC.csr().read().lsirdy() {}
 
     info!("Started LSI");
 
diff --git a/tests/stm32/src/bin/tl_mbox.rs b/tests/stm32/src/bin/tl_mbox.rs
index 4669cbc62..f6641ae31 100644
--- a/tests/stm32/src/bin/tl_mbox.rs
+++ b/tests/stm32/src/bin/tl_mbox.rs
@@ -6,60 +6,70 @@
 #[path = "../common.rs"]
 mod common;
 
+use core::mem;
+
 use common::*;
 use embassy_executor::Spawner;
+use embassy_futures::poll_once;
 use embassy_stm32::bind_interrupts;
-use embassy_stm32::ipcc::Config;
-use embassy_stm32_wpan::rc::RadioCoprocessor;
-use embassy_stm32_wpan::TlMbox;
+use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler};
+use embassy_stm32_wpan::{mm, TlMbox};
 use embassy_time::{Duration, Timer};
 
 bind_interrupts!(struct Irqs{
-    IPCC_C1_RX => embassy_stm32_wpan::ReceiveInterruptHandler;
-    IPCC_C1_TX => embassy_stm32_wpan::TransmitInterruptHandler;
+    IPCC_C1_RX => ReceiveInterruptHandler;
+    IPCC_C1_TX => TransmitInterruptHandler;
 });
 
+#[embassy_executor::task]
+async fn run_mm_queue(memory_manager: mm::MemoryManager) {
+    memory_manager.run_queue().await;
+}
+
 #[embassy_executor::main]
-async fn main(_spawner: Spawner) {
+async fn main(spawner: Spawner) {
     let p = embassy_stm32::init(config());
     info!("Hello World!");
 
     let config = Config::default();
     let mbox = TlMbox::init(p.IPCC, Irqs, config);
 
-    loop {
-        let wireless_fw_info = mbox.wireless_fw_info();
-        match wireless_fw_info {
-            None => {}
-            Some(fw_info) => {
-                let version_major = fw_info.version_major();
-                let version_minor = fw_info.version_minor();
-                let subversion = fw_info.subversion();
+    spawner.spawn(run_mm_queue(mbox.mm_subsystem)).unwrap();
 
-                let sram2a_size = fw_info.sram2a_size();
-                let sram2b_size = fw_info.sram2b_size();
+    let ready_event = mbox.sys_subsystem.read().await;
+    let _ = poll_once(mbox.sys_subsystem.read()); // clear rx not
 
-                info!(
-                    "version {}.{}.{} - SRAM2a {} - SRAM2b {}",
-                    version_major, version_minor, subversion, sram2a_size, sram2b_size
-                );
+    info!("coprocessor ready {}", ready_event.payload());
 
-                break;
-            }
-        }
+    // test memory manager
+    mem::drop(ready_event);
 
-        Timer::after(Duration::from_millis(50)).await;
-    }
+    let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap();
+    let version_major = fw_info.version_major();
+    let version_minor = fw_info.version_minor();
+    let subversion = fw_info.subversion();
 
-    let mut rc = RadioCoprocessor::new(mbox);
+    let sram2a_size = fw_info.sram2a_size();
+    let sram2b_size = fw_info.sram2b_size();
 
-    let response = rc.read().await;
-    info!("coprocessor ready {}", response);
+    info!(
+        "version {}.{}.{} - SRAM2a {} - SRAM2b {}",
+        version_major, version_minor, subversion, sram2a_size, sram2b_size
+    );
 
-    rc.write(&[0x01, 0x03, 0x0c, 0x00, 0x00]);
-    let response = rc.read().await;
-    info!("ble reset rsp {}", response);
+    Timer::after(Duration::from_millis(50)).await;
 
+    mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await;
+
+    info!("starting ble...");
+    mbox.ble_subsystem.write(0x0c, &[]).await;
+
+    info!("waiting for ble...");
+    let ble_event = mbox.ble_subsystem.read().await;
+
+    info!("ble event: {}", ble_event.payload());
+
+    Timer::after(Duration::from_millis(150)).await;
     info!("Test OK");
     cortex_m::asm::bkpt();
 }