diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
index 44824e42a..547de65d9 100644
--- a/embassy-stm32/src/ospi/mod.rs
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -3,12 +3,17 @@
 
 #![macro_use]
 
+pub mod enums;
+
+use core::ops::Add;
 use core::ptr;
 
 use embassy_embedded_hal::SetConfig;
 use embassy_futures::join::join;
 use embassy_hal_internal::{into_ref, PeripheralRef};
+use embedded_hal_02::blocking::i2c::Operation;
 pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
+use enums::*;
 
 use crate::dma::{slice_ptr_parts, word, Transfer};
 use crate::gpio::sealed::{AFType, Pin as _};
@@ -18,8 +23,109 @@ use crate::rcc::RccPeripheral;
 use crate::time::Hertz;
 use crate::{peripherals, Peripheral};
 
+/// OPSI driver config.
 pub struct Config;
 
+/// OSPI transfer configuration.
+pub struct TransferConfig {
+    /// Instruction width (IMODE)
+    pub iwidth: OspiWidth,
+    /// Instruction Id
+    pub instruction: Option<u32>,
+    /// Number of Instruction Bytes
+    pub isize: AddressSize,
+    /// Instruction Double Transfer rate enable
+    pub idtr: bool,
+
+    /// Address width (ADMODE)
+    pub adwidth: OspiWidth,
+    /// Device memory address
+    pub address: Option<u32>,
+    /// Number of Address Bytes
+    pub adsize: AddressSize,
+    /// Address Double Transfer rate enable
+    pub addtr: bool,
+
+    /// Alternate bytes width (ABMODE)
+    pub abwidth: OspiWidth,
+    /// Alternate Bytes
+    pub alternate_bytes: Option<u32>,
+    /// Number of Alternate Bytes
+    pub absize: AddressSize,
+    /// Alternate Bytes Double Transfer rate enable
+    pub abdtr: bool,
+
+    /// Data width (DMODE)
+    pub dwidth: OspiWidth,
+    /// Length of data
+    pub data_len: Option<usize>,
+    /// Data buffer
+    pub ddtr: bool,
+
+    /// Number of dummy cycles (DCYC)
+    pub dummy: DummyCycles,
+}
+
+impl Default for TransferConfig {
+    fn default() -> Self {
+        Self {
+            iwidth: OspiWidth::NONE,
+            instruction: None,
+            isize: AddressSize::_8Bit,
+            idtr: false,
+
+            adwidth: OspiWidth::NONE,
+            address: None,
+            adsize: AddressSize::_8Bit,
+            addtr: false,
+
+            abwidth: OspiWidth::NONE,
+            alternate_bytes: None,
+            absize: AddressSize::_8Bit,
+            abdtr: false,
+
+            dwidth: OspiWidth::NONE,
+            data_len: None,
+            ddtr: false,
+
+            dummy: DummyCycles::_0,
+        }
+    }
+}
+
+pub enum OspiError {
+    Test,
+}
+
+pub trait Error {}
+
+pub trait ErrorType {
+    type Error: Error;
+}
+
+impl<T: ErrorType + ?Sized> ErrorType for &mut T {
+    type Error = T::Error;
+}
+
+/// MultiSpi interface trait
+pub trait MultiSpi: ErrorType {
+    /// Transaction configuration for specific multispi implementation
+    type Config;
+
+    /// Command function used for a configuration operation, when no user data is
+    /// supplied to or read from the target device.
+    async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error>;
+
+    /// Read function used to read data from the target device following the supplied transaction
+    /// configuration.
+    async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>;
+
+    /// Write function used to send data to the target device following the supplied transaction
+    /// configuration.
+    async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>;
+}
+
+/// OSPI driver.
 pub struct Ospi<'d, T: Instance, Dma> {
     _peri: PeripheralRef<'d, T>,
     sck: Option<PeripheralRef<'d, AnyPin>>,
@@ -37,6 +143,28 @@ pub struct Ospi<'d, T: Instance, Dma> {
     config: Config,
 }
 
+impl Error for OspiError {}
+
+impl<'d, T: Instance, Dma> ErrorType for Ospi<'d, T, Dma> {
+    type Error = OspiError;
+}
+
+impl<'d, T: Instance, Dma: OctoDma<T>> MultiSpi for Ospi<'d, T, Dma> {
+    type Config = TransferConfig;
+
+    async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> {
+        Ok(())
+    }
+
+    async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> {
+        Ok(())
+    }
+
+    async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> {
+        Ok(())
+    }
+}
+
 impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> {
     /// Create new OSPI driver for a dualspi external chip
     pub fn new_dualspi(
@@ -54,11 +182,29 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> {
         sck.set_speed(crate::gpio::Speed::VeryHigh);
         nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
         nss.set_speed(crate::gpio::Speed::VeryHigh);
+        // nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Down);
+        // nss.set_speed(crate::gpio::Speed::VeryHigh);
         d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
         d0.set_speed(crate::gpio::Speed::VeryHigh);
         d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None);
         d1.set_speed(crate::gpio::Speed::VeryHigh);
 
+        #[cfg(octospi_v1)]
+        {
+            T::REGS.ccr().modify(|w| {
+                w.set_imode(vals::PhaseMode::TWOLINES);
+                w.set_admode(vals::PhaseMode::TWOLINES);
+                w.set_abmode(vals::PhaseMode::TWOLINES);
+                w.set_dmode(vals::PhaseMode::TWOLINES);
+            });
+            T::REGS.wccr().modify(|w| {
+                w.set_imode(vals::PhaseMode::TWOLINES);
+                w.set_admode(vals::PhaseMode::TWOLINES);
+                w.set_abmode(vals::PhaseMode::TWOLINES);
+                w.set_dmode(vals::PhaseMode::TWOLINES);
+            });
+        }
+
         Self::new_inner(
             peri,
             Some(d0.map_into()),
@@ -96,31 +242,18 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> {
         into_ref!(peri, dma);
 
         T::enable_and_reset();
-        T::REGS.sr().read().busy();
+        while T::REGS.sr().read().busy() {}
 
         T::REGS.cr().modify(|w| {
             w.set_en(true);
         });
 
-        #[cfg(octospi_v1)]
-        {
-            T::REGS.ccr().modify(|w| {
-                w.set_imode(vals::PhaseMode::TWOLINES);
-                w.set_admode(vals::PhaseMode::TWOLINES);
-                w.set_abmode(vals::PhaseMode::TWOLINES);
-                w.set_dmode(vals::PhaseMode::TWOLINES);
-            });
-            T::REGS.wccr().modify(|w| {
-                w.set_imode(vals::PhaseMode::TWOLINES);
-                w.set_admode(vals::PhaseMode::TWOLINES);
-                w.set_abmode(vals::PhaseMode::TWOLINES);
-                w.set_dmode(vals::PhaseMode::TWOLINES);
-            });
-        }
-
-        //
-
-        // while T::REGS::sr().read().busy() {}
+        T::REGS.dcr1().modify(|w| {
+            w.set_devsize(23);
+            w.set_mtyp(vals::MemType::MACRONIX);
+            w.set_ckmode(false);
+            // w.se
+        });
 
         Self {
             _peri: peri,
@@ -139,6 +272,51 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> {
             config,
         }
     }
+
+    pub fn blocking_read(&mut self, transaction: TransferConfig) -> Result<(), ()> {
+        Ok(())
+    }
+
+    fn configure_command(&mut self, command: &TransferConfig) -> Result<(), ()> {
+        Ok(())
+    }
+
+    /// Poor attempt to read data from memory
+    pub fn receive(&mut self, buf: &mut [u8], intruction: u8, data_len: usize) -> Result<(), ()> {
+        T::REGS.cr().modify(|w| {
+            w.set_fmode(vals::FunctionalMode::INDIRECTREAD);
+        });
+
+        T::REGS.ccr().modify(|w| {
+            w.set_imode(vals::PhaseMode::ONELINE);
+            w.set_admode(vals::PhaseMode::NONE);
+            w.set_abmode(vals::PhaseMode::NONE);
+
+            w.set_dmode(vals::PhaseMode::ONELINE);
+        });
+
+        T::REGS.dlr().modify(|w| {
+            w.set_dl((data_len - 1) as u32);
+        });
+
+        // set instruction
+        T::REGS.ir().modify(|w| w.set_instruction(intruction as u32));
+
+        // read bytes
+        // for idx in 0..data_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() };
+        // }
+        // wait for finish
+        while !T::REGS.sr().read().tcf() {}
+
+        let fifo_count = T::REGS.sr().read().flevel();
+        for idx in 0..(fifo_count as usize) {
+            buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
+        }
+
+        Ok(())
+    }
 }
 
 pub(crate) mod sealed {