Add embedded-storage trait impls for QSPI
* Adds implementations of embedded-storage and embedded-storage-async for QSPI * Add blocking implementations of QSPI * Use blocking implementation in new() and embedded-storage impls * Use async implementation in embedded-storage-async impls * Add FLASH_SIZE const generic parameter * Own IRQ in Qspi to disable it on drop
This commit is contained in:
		| @@ -60,16 +60,18 @@ impl Default for Config { | ||||
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||||
| #[non_exhaustive] | ||||
| pub enum Error { | ||||
|     OutOfBounds, | ||||
|     // TODO add "not in data memory" error and check for it | ||||
| } | ||||
|  | ||||
| pub struct Qspi<'d, T: Instance> { | ||||
| pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { | ||||
|     irq: T::Interrupt, | ||||
|     dpm_enabled: bool, | ||||
|     phantom: PhantomData<&'d mut T>, | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> Qspi<'d, T> { | ||||
|     pub async fn new( | ||||
| impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { | ||||
|     pub fn new( | ||||
|         _qspi: impl Unborrow<Target = T> + 'd, | ||||
|         irq: impl Unborrow<Target = T::Interrupt> + 'd, | ||||
|         sck: impl Unborrow<Target = impl GpioPin> + 'd, | ||||
| @@ -79,7 +81,7 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|         io2: impl Unborrow<Target = impl GpioPin> + 'd, | ||||
|         io3: impl Unborrow<Target = impl GpioPin> + 'd, | ||||
|         config: Config, | ||||
|     ) -> Qspi<'d, T> { | ||||
|     ) -> Qspi<'d, T, FLASH_SIZE> { | ||||
|         unborrow!(irq, sck, csn, io0, io1, io2, io3); | ||||
|  | ||||
|         let r = T::regs(); | ||||
| @@ -142,6 +144,7 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|  | ||||
|         let mut res = Self { | ||||
|             dpm_enabled: config.deep_power_down.is_some(), | ||||
|             irq, | ||||
|             phantom: PhantomData, | ||||
|         }; | ||||
|  | ||||
| @@ -150,7 +153,7 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|  | ||||
|         r.tasks_activate.write(|w| w.tasks_activate().bit(true)); | ||||
|  | ||||
|         res.wait_ready().await; | ||||
|         res.blocking_wait_ready(); | ||||
|  | ||||
|         res | ||||
|     } | ||||
| @@ -173,8 +176,36 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|     ) -> Result<(), Error> { | ||||
|         let bomb = DropBomb::new(); | ||||
|  | ||||
|         let len = core::cmp::max(req.len(), resp.len()) as u8; | ||||
|         self.custom_instruction_start(opcode, req, len)?; | ||||
|  | ||||
|         self.wait_ready().await; | ||||
|  | ||||
|         self.custom_instruction_finish(resp)?; | ||||
|  | ||||
|         bomb.defuse(); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn blocking_custom_instruction( | ||||
|         &mut self, | ||||
|         opcode: u8, | ||||
|         req: &[u8], | ||||
|         resp: &mut [u8], | ||||
|     ) -> Result<(), Error> { | ||||
|         let len = core::cmp::max(req.len(), resp.len()) as u8; | ||||
|         self.custom_instruction_start(opcode, req, len)?; | ||||
|  | ||||
|         self.blocking_wait_ready(); | ||||
|  | ||||
|         self.custom_instruction_finish(resp)?; | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn custom_instruction_start(&mut self, opcode: u8, req: &[u8], len: u8) -> Result<(), Error> { | ||||
|         assert!(req.len() <= 8); | ||||
|         assert!(resp.len() <= 8); | ||||
|  | ||||
|         let mut dat0: u32 = 0; | ||||
|         let mut dat1: u32 = 0; | ||||
| @@ -190,8 +221,6 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let len = core::cmp::max(req.len(), resp.len()) as u8; | ||||
|  | ||||
|         let r = T::regs(); | ||||
|         r.cinstrdat0.write(|w| unsafe { w.bits(dat0) }); | ||||
|         r.cinstrdat1.write(|w| unsafe { w.bits(dat1) }); | ||||
| @@ -210,9 +239,10 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|             let w = w.lfstop().bit(false); | ||||
|             w | ||||
|         }); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|         self.wait_ready().await; | ||||
|  | ||||
|     fn custom_instruction_finish(&mut self, resp: &mut [u8]) -> Result<(), Error> { | ||||
|         let r = T::regs(); | ||||
|  | ||||
|         let dat0 = r.cinstrdat0.read().bits(); | ||||
| @@ -227,9 +257,6 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|                 resp[i] = (dat1 >> (i * 8)) as u8; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         bomb.defuse(); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @@ -246,12 +273,22 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|         .await | ||||
|     } | ||||
|  | ||||
|     pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { | ||||
|         let bomb = DropBomb::new(); | ||||
|     fn blocking_wait_ready(&mut self) { | ||||
|         loop { | ||||
|             let r = T::regs(); | ||||
|             if r.events_ready.read().bits() != 0 { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn start_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { | ||||
|         assert_eq!(data.as_ptr() as u32 % 4, 0); | ||||
|         assert_eq!(data.len() as u32 % 4, 0); | ||||
|         assert_eq!(address as u32 % 4, 0); | ||||
|         if address > FLASH_SIZE { | ||||
|             return Err(Error::OutOfBounds); | ||||
|         } | ||||
|  | ||||
|         let r = T::regs(); | ||||
|  | ||||
| @@ -269,19 +306,20 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|         r.intenset.write(|w| w.ready().set()); | ||||
|         r.tasks_readstart.write(|w| w.tasks_readstart().bit(true)); | ||||
|  | ||||
|         self.wait_ready().await; | ||||
|  | ||||
|         bomb.defuse(); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { | ||||
|         let bomb = DropBomb::new(); | ||||
|  | ||||
|     fn start_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { | ||||
|         //info!("start_write ptr {}", data.as_ptr() as u32); | ||||
|         assert_eq!(data.as_ptr() as u32 % 4, 0); | ||||
|         //info!("start_write OK ptr"); | ||||
|         assert_eq!(data.len() as u32 % 4, 0); | ||||
|         //info!("start_write OK len"); | ||||
|         assert_eq!(address as u32 % 4, 0); | ||||
|         //info!("start_write OK addr"); | ||||
|         if address > FLASH_SIZE { | ||||
|             return Err(Error::OutOfBounds); | ||||
|         } | ||||
|  | ||||
|         let r = T::regs(); | ||||
|         r.write | ||||
| @@ -298,17 +336,14 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|         r.intenset.write(|w| w.ready().set()); | ||||
|         r.tasks_writestart.write(|w| w.tasks_writestart().bit(true)); | ||||
|  | ||||
|         self.wait_ready().await; | ||||
|  | ||||
|         bomb.defuse(); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn erase(&mut self, address: usize) -> Result<(), Error> { | ||||
|         let bomb = DropBomb::new(); | ||||
|  | ||||
|     fn start_erase(&mut self, address: usize) -> Result<(), Error> { | ||||
|         assert_eq!(address as u32 % 4096, 0); | ||||
|         if address > FLASH_SIZE { | ||||
|             return Err(Error::OutOfBounds); | ||||
|         } | ||||
|  | ||||
|         let r = T::regs(); | ||||
|         r.erase | ||||
| @@ -320,15 +355,65 @@ impl<'d, T: Instance> Qspi<'d, T> { | ||||
|         r.intenset.write(|w| w.ready().set()); | ||||
|         r.tasks_erasestart.write(|w| w.tasks_erasestart().bit(true)); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { | ||||
|         let bomb = DropBomb::new(); | ||||
|  | ||||
|         self.start_read(address, data)?; | ||||
|         self.wait_ready().await; | ||||
|  | ||||
|         bomb.defuse(); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { | ||||
|         let bomb = DropBomb::new(); | ||||
|  | ||||
|         //info!("WRITE {} bytes at {}", data.len(), address); | ||||
|         self.start_write(address, data)?; | ||||
|         //info!("STARTED"); | ||||
|         self.wait_ready().await; | ||||
|         //info!("WRITE DONE"); | ||||
|  | ||||
|         bomb.defuse(); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub async fn erase(&mut self, address: usize) -> Result<(), Error> { | ||||
|         let bomb = DropBomb::new(); | ||||
|  | ||||
|         self.start_erase(address)?; | ||||
|         self.wait_ready().await; | ||||
|  | ||||
|         bomb.defuse(); | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn blocking_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> { | ||||
|         self.start_read(address, data)?; | ||||
|         self.blocking_wait_ready(); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn blocking_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> { | ||||
|         self.start_write(address, data)?; | ||||
|         self.blocking_wait_ready(); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> { | ||||
|         self.start_erase(address)?; | ||||
|         self.blocking_wait_ready(); | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance> Drop for Qspi<'d, T> { | ||||
| impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE> { | ||||
|     fn drop(&mut self) { | ||||
|         let r = T::regs(); | ||||
|  | ||||
| @@ -358,6 +443,8 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { | ||||
|  | ||||
|         r.enable.write(|w| w.enable().disabled()); | ||||
|  | ||||
|         self.irq.disable(); | ||||
|  | ||||
|         // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, | ||||
|         // leaving it floating, the flash chip might read it as zero which would cause it to | ||||
|         // spuriously exit DPM. | ||||
| @@ -371,6 +458,90 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| use embedded_storage::nor_flash::{ | ||||
|     ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, | ||||
| }; | ||||
|  | ||||
| impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Qspi<'d, T, FLASH_SIZE> { | ||||
|     type Error = Error; | ||||
| } | ||||
|  | ||||
| impl NorFlashError for Error { | ||||
|     fn kind(&self) -> NorFlashErrorKind { | ||||
|         NorFlashErrorKind::Other | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> { | ||||
|     const READ_SIZE: usize = 4; | ||||
|  | ||||
|     fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { | ||||
|         self.blocking_read(offset as usize, bytes)?; | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn capacity(&self) -> usize { | ||||
|         FLASH_SIZE | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SIZE> { | ||||
|     const WRITE_SIZE: usize = 4; | ||||
|     const ERASE_SIZE: usize = 4096; | ||||
|  | ||||
|     fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { | ||||
|         for address in (from as usize..to as usize).step_by(<Self as NorFlash>::ERASE_SIZE) { | ||||
|             self.blocking_erase(address)?; | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { | ||||
|         self.blocking_write(offset as usize, bytes)?; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| cfg_if::cfg_if! { | ||||
|     if #[cfg(feature = "nightly")] | ||||
|     { | ||||
|         use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash}; | ||||
|         use core::future::Future; | ||||
|  | ||||
|         impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncNorFlash for Qspi<'d, T, FLASH_SIZE> { | ||||
|             const WRITE_SIZE: usize = <Self as NorFlash>::WRITE_SIZE; | ||||
|             const ERASE_SIZE: usize = <Self as NorFlash>::ERASE_SIZE; | ||||
|  | ||||
|             type WriteFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a; | ||||
|             fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> { | ||||
|                 async move { self.write(offset as usize, data).await } | ||||
|             } | ||||
|  | ||||
|             type EraseFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a; | ||||
|             fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> { | ||||
|                 async move { | ||||
|                     for address in (from as usize..to as usize).step_by(<Self as AsyncNorFlash>::ERASE_SIZE) { | ||||
|                         self.erase(address).await? | ||||
|                     } | ||||
|                     Ok(()) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         impl<'d, T: Instance, const FLASH_SIZE: usize> AsyncReadNorFlash for Qspi<'d, T, FLASH_SIZE> { | ||||
|             const READ_SIZE: usize = 4; | ||||
|             type ReadFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a; | ||||
|             fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||||
|                 async move { self.read(address as usize, data).await } | ||||
|             } | ||||
|  | ||||
|             fn capacity(&self) -> usize { | ||||
|                 FLASH_SIZE | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub(crate) mod sealed { | ||||
|     use embassy::waitqueue::AtomicWaker; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user