diff --git a/embassy-embedded-hal/src/flash/mod.rs b/embassy-embedded-hal/src/flash/mod.rs
index 0210b198d..7e4ef290f 100644
--- a/embassy-embedded-hal/src/flash/mod.rs
+++ b/embassy-embedded-hal/src/flash/mod.rs
@@ -3,9 +3,6 @@
 mod concat_flash;
 #[cfg(test)]
 pub(crate) mod mem_flash;
-#[cfg(feature = "nightly")]
-mod partition;
+pub mod partition;
 
 pub use concat_flash::ConcatFlash;
-#[cfg(feature = "nightly")]
-pub use partition::Partition;
diff --git a/embassy-embedded-hal/src/flash/partition.rs b/embassy-embedded-hal/src/flash/partition/asynch.rs
similarity index 89%
rename from embassy-embedded-hal/src/flash/partition.rs
rename to embassy-embedded-hal/src/flash/partition/asynch.rs
index 66d93c0ea..141e0d9fc 100644
--- a/embassy-embedded-hal/src/flash/partition.rs
+++ b/embassy-embedded-hal/src/flash/partition/asynch.rs
@@ -1,8 +1,10 @@
 use embassy_sync::blocking_mutex::raw::RawMutex;
 use embassy_sync::mutex::Mutex;
-use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind};
+use embedded_storage::nor_flash::ErrorType;
 use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash};
 
+use super::Error;
+
 /// A logical partition of an underlying shared flash
 ///
 /// A partition holds an offset and a size of the flash,
@@ -16,13 +18,6 @@ pub struct Partition<'a, M: RawMutex, T: NorFlash> {
     size: u32,
 }
 
-#[derive(Debug)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-pub enum Error<T> {
-    OutOfBounds,
-    Flash(T),
-}
-
 impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> {
     /// Create a new partition
     pub const fn new(flash: &'a Mutex<M, T>, offset: u32, size: u32) -> Self {
@@ -37,20 +32,10 @@ impl<'a, M: RawMutex, T: NorFlash> Partition<'a, M, T> {
     }
 }
 
-impl<T: NorFlashError> NorFlashError for Error<T> {
-    fn kind(&self) -> NorFlashErrorKind {
-        match self {
-            Error::OutOfBounds => NorFlashErrorKind::OutOfBounds,
-            Error::Flash(f) => f.kind(),
-        }
-    }
-}
-
 impl<M: RawMutex, T: NorFlash> ErrorType for Partition<'_, M, T> {
     type Error = Error<T::Error>;
 }
 
-#[cfg(feature = "nightly")]
 impl<M: RawMutex, T: NorFlash> ReadNorFlash for Partition<'_, M, T> {
     const READ_SIZE: usize = T::READ_SIZE;
 
@@ -68,7 +53,6 @@ impl<M: RawMutex, T: NorFlash> ReadNorFlash for Partition<'_, M, T> {
     }
 }
 
-#[cfg(feature = "nightly")]
 impl<M: RawMutex, T: NorFlash> NorFlash for Partition<'_, M, T> {
     const WRITE_SIZE: usize = T::WRITE_SIZE;
     const ERASE_SIZE: usize = T::ERASE_SIZE;
diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs
new file mode 100644
index 000000000..1a4c80f92
--- /dev/null
+++ b/embassy-embedded-hal/src/flash/partition/blocking.rs
@@ -0,0 +1,125 @@
+use embassy_sync::blocking_mutex::raw::RawMutex;
+use embassy_sync::blocking_mutex::Mutex;
+use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
+
+use super::Error;
+
+/// A logical partition of an underlying shared flash
+///
+/// A partition holds an offset and a size of the flash,
+/// and is restricted to operate with that range.
+/// There is no guarantee that muliple partitions on the same flash
+/// operate on mutually exclusive ranges - such a separation is up to
+/// the user to guarantee.
+pub struct BlockingPartition<'a, M: RawMutex, T: NorFlash> {
+    flash: &'a Mutex<M, T>,
+    offset: u32,
+    size: u32,
+}
+
+impl<'a, M: RawMutex, T: NorFlash> BlockingPartition<'a, M, T> {
+    /// Create a new partition
+    pub const fn new(flash: &'a Mutex<M, T>, offset: u32, size: u32) -> Self {
+        if offset % T::READ_SIZE as u32 != 0 || offset % T::WRITE_SIZE as u32 != 0 || offset % T::ERASE_SIZE as u32 != 0
+        {
+            panic!("Partition offset must be a multiple of read, write and erase size");
+        }
+        if size % T::READ_SIZE as u32 != 0 || size % T::WRITE_SIZE as u32 != 0 || size % T::ERASE_SIZE as u32 != 0 {
+            panic!("Partition size must be a multiple of read, write and erase size");
+        }
+        Self { flash, offset, size }
+    }
+}
+
+impl<M: RawMutex, T: NorFlash> ErrorType for BlockingPartition<'_, M, T> {
+    type Error = Error<T::Error>;
+}
+
+impl<M: RawMutex, T: NorFlash> ReadNorFlash for BlockingPartition<'_, M, T> {
+    const READ_SIZE: usize = T::READ_SIZE;
+
+    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
+        if offset + bytes.len() as u32 > self.size {
+            return Err(Error::OutOfBounds);
+        }
+
+        self.flash
+            .lock(|flash| flash.read(self.offset + offset, bytes).map_err(Error::Flash))
+    }
+
+    fn capacity(&self) -> usize {
+        self.size as usize
+    }
+}
+
+impl<M: RawMutex, T: NorFlash> NorFlash for BlockingPartition<'_, M, T> {
+    const WRITE_SIZE: usize = T::WRITE_SIZE;
+    const ERASE_SIZE: usize = T::ERASE_SIZE;
+
+    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
+        if offset + bytes.len() as u32 > self.size {
+            return Err(Error::OutOfBounds);
+        }
+
+        self.flash
+            .lock(|flash| flash.write(self.offset + offset, bytes).map_err(Error::Flash))
+    }
+
+    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
+        if to > self.size {
+            return Err(Error::OutOfBounds);
+        }
+
+        self.flash
+            .lock(|flash| flash.erase(self.offset + from, self.offset + to).map_err(Error::Flash))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use embassy_sync::blocking_mutex::raw::NoopRawMutex;
+
+    use super::*;
+    use crate::flash::mem_flash::MemFlash;
+
+    #[test]
+    fn can_read() {
+        let mut flash = MemFlash::<1024, 128, 4>::default();
+        flash.mem[132..132 + 8].fill(0xAA);
+
+        let flash = Mutex::<NoopRawMutex, _>::new(flash);
+        let mut partition = BlockingPartition::new(&flash, 128, 256);
+
+        let mut read_buf = [0; 8];
+        partition.read(4, &mut read_buf).unwrap();
+
+        assert!(read_buf.iter().position(|&x| x != 0xAA).is_none());
+    }
+
+    #[test]
+    fn can_write() {
+        let flash = MemFlash::<1024, 128, 4>::default();
+
+        let flash = Mutex::<NoopRawMutex, _>::new(flash);
+        let mut partition = BlockingPartition::new(&flash, 128, 256);
+
+        let write_buf = [0xAA; 8];
+        partition.write(4, &write_buf).unwrap();
+
+        let flash = flash.into_inner();
+        assert!(flash.mem[132..132 + 8].iter().position(|&x| x != 0xAA).is_none());
+    }
+
+    #[test]
+    fn can_erase() {
+        let flash = MemFlash::<1024, 128, 4>::new(0x00);
+
+        let flash = Mutex::<NoopRawMutex, _>::new(flash);
+        let mut partition = BlockingPartition::new(&flash, 128, 256);
+
+        partition.erase(0, 128).unwrap();
+
+        let flash = flash.into_inner();
+        assert!(flash.mem[128..256].iter().position(|&x| x != 0xFF).is_none());
+    }
+}
diff --git a/embassy-embedded-hal/src/flash/partition/mod.rs b/embassy-embedded-hal/src/flash/partition/mod.rs
new file mode 100644
index 000000000..a12e49ce1
--- /dev/null
+++ b/embassy-embedded-hal/src/flash/partition/mod.rs
@@ -0,0 +1,30 @@
+//! Flash Partition utilities
+
+use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
+
+#[cfg(feature = "nightly")]
+mod asynch;
+mod blocking;
+
+#[cfg(feature = "nightly")]
+pub use asynch::Partition;
+pub use blocking::BlockingPartition;
+
+/// Partition error
+#[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Error<T> {
+    /// The requested flash area is outside the partition
+    OutOfBounds,
+    /// Underlying flash error
+    Flash(T),
+}
+
+impl<T: NorFlashError> NorFlashError for Error<T> {
+    fn kind(&self) -> NorFlashErrorKind {
+        match self {
+            Error::OutOfBounds => NorFlashErrorKind::OutOfBounds,
+            Error::Flash(f) => f.kind(),
+        }
+    }
+}