diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 2e1c7f384..9c7b738e6 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -250,6 +250,9 @@ embassy_hal_common::peripherals! { TIMER1, TIMER2, + // QSPI + QSPI, + // GPIOTE GPIOTE_CH0, GPIOTE_CH1, @@ -393,6 +396,8 @@ impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); impl_timer!(TIMER2, TIMER2, TIMER2); +impl_qspi!(QSPI, QSPI, QSPI); + impl_pin!(P0_00, 0, 0); impl_pin!(P0_01, 0, 1); #[cfg(feature = "nfc-pins-as-gpio")] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 6b7dc7791..3c5db5c7c 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -57,7 +57,7 @@ pub mod ppi; pub mod pwm; #[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340")))] pub mod qdec; -#[cfg(feature = "nrf52840")] +#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod rng; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d434327fc..d514e0274 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -8,6 +8,7 @@ use core::task::Poll; use embassy_hal_common::drop::OnDrop; use embassy_hal_common::{into_ref, PeripheralRef}; +use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::{Interrupt, InterruptExt}; @@ -82,6 +83,8 @@ pub struct Config { pub spi_mode: SpiMode, /// Addressing mode (24-bit or 32-bit) pub address_mode: AddressMode, + /// Flash memory capacity in bytes. This is the value reported by the `embedded-storage` traits. + pub capacity: u32, } impl Default for Config { @@ -96,6 +99,7 @@ impl Default for Config { sck_delay: 80, spi_mode: SpiMode::MODE0, address_mode: AddressMode::_24BIT, + capacity: 0, } } } @@ -111,12 +115,13 @@ pub enum Error { } /// QSPI flash driver. -pub struct Qspi<'d, T: Instance, const FLASH_SIZE: usize> { +pub struct Qspi<'d, T: Instance> { irq: PeripheralRef<'d, T::Interrupt>, dpm_enabled: bool, + capacity: u32, } -impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { +impl<'d, T: Instance> Qspi<'d, T> { /// Create a new QSPI driver. pub fn new( _qspi: impl Peripheral
+ 'd, @@ -128,30 +133,31 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> { io2: impl Peripheral
+ 'd, io3: impl Peripheral
+ 'd,
config: Config,
- ) -> Qspi<'d, T, FLASH_SIZE> {
+ ) -> Self {
into_ref!(irq, sck, csn, io0, io1, io2, io3);
let r = T::regs();
- sck.set_high();
- csn.set_high();
- io0.set_high();
- io1.set_high();
- io2.set_high();
- io3.set_high();
- sck.conf().write(|w| w.dir().output().drive().h0h1());
- csn.conf().write(|w| w.dir().output().drive().h0h1());
- io0.conf().write(|w| w.dir().output().drive().h0h1());
- io1.conf().write(|w| w.dir().output().drive().h0h1());
- io2.conf().write(|w| w.dir().output().drive().h0h1());
- io3.conf().write(|w| w.dir().output().drive().h0h1());
+ macro_rules! config_pin {
+ ($pin:ident) => {
+ $pin.set_high();
+ $pin.conf().write(|w| {
+ w.dir().output();
+ w.drive().h0h1();
+ #[cfg(feature = "_nrf5340-s")]
+ w.mcusel().peripheral();
+ w
+ });
+ r.psel.$pin.write(|w| unsafe { w.bits($pin.psel_bits()) });
+ };
+ }
- r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) });
- r.psel.csn.write(|w| unsafe { w.bits(csn.psel_bits()) });
- r.psel.io0.write(|w| unsafe { w.bits(io0.psel_bits()) });
- r.psel.io1.write(|w| unsafe { w.bits(io1.psel_bits()) });
- r.psel.io2.write(|w| unsafe { w.bits(io2.psel_bits()) });
- r.psel.io3.write(|w| unsafe { w.bits(io3.psel_bits()) });
+ config_pin!(sck);
+ config_pin!(csn);
+ config_pin!(io0);
+ config_pin!(io1);
+ config_pin!(io2);
+ config_pin!(io3);
r.ifconfig0.write(|w| {
w.addrmode().variant(config.address_mode);
@@ -193,6 +199,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
let res = Self {
dpm_enabled: config.deep_power_down.is_some(),
irq,
+ capacity: config.capacity,
};
r.events_ready.reset();
@@ -321,17 +328,15 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
}
}
- fn start_read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> {
+ fn start_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
+ // TODO: Return these as errors instead.
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);
- }
+ assert_eq!(address % 4, 0);
let r = T::regs();
- r.read.src.write(|w| unsafe { w.src().bits(address as u32) });
+ r.read.src.write(|w| unsafe { w.src().bits(address) });
r.read.dst.write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) });
r.read.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) });
@@ -342,18 +347,15 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
- fn start_write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> {
+ fn start_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
+ // TODO: Return these as errors instead.
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);
- }
+ assert_eq!(address % 4, 0);
let r = T::regs();
r.write.src.write(|w| unsafe { w.src().bits(data.as_ptr() as u32) });
- r.write.dst.write(|w| unsafe { w.dst().bits(address as u32) });
+ r.write.dst.write(|w| unsafe { w.dst().bits(address) });
r.write.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) });
r.events_ready.reset();
@@ -363,14 +365,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
- fn start_erase(&mut self, address: usize) -> Result<(), Error> {
- assert_eq!(address as u32 % 4096, 0);
- if address > FLASH_SIZE {
- return Err(Error::OutOfBounds);
- }
+ fn start_erase(&mut self, address: u32) -> Result<(), Error> {
+ // TODO: Return these as errors instead.
+ assert_eq!(address % 4096, 0);
let r = T::regs();
- r.erase.ptr.write(|w| unsafe { w.ptr().bits(address as u32) });
+ r.erase.ptr.write(|w| unsafe { w.ptr().bits(address) });
r.erase.len.write(|w| w.len()._4kb());
r.events_ready.reset();
@@ -380,8 +380,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
- /// Read data from the flash memory.
- pub async fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), Error> {
+ /// Raw QSPI read.
+ ///
+ /// The difference with `read` is that this does not do bounds checks
+ /// against the flash capacity. It is intended for use when QSPI is used as
+ /// a raw bus, not with flash memory.
+ pub async fn read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
let ondrop = OnDrop::new(Self::blocking_wait_ready);
self.start_read(address, data)?;
@@ -392,8 +396,12 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
- /// Write data to the flash memory.
- pub async fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Error> {
+ /// Raw QSPI write.
+ ///
+ /// The difference with `write` is that this does not do bounds checks
+ /// against the flash capacity. It is intended for use when QSPI is used as
+ /// a raw bus, not with flash memory.
+ pub async fn write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
let ondrop = OnDrop::new(Self::blocking_wait_ready);
self.start_write(address, data)?;
@@ -404,8 +412,46 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
Ok(())
}
+ /// Raw QSPI read, blocking version.
+ ///
+ /// The difference with `blocking_read` is that this does not do bounds checks
+ /// against the flash capacity. It is intended for use when QSPI is used as
+ /// a raw bus, not with flash memory.
+ pub fn blocking_read_raw(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
+ self.start_read(address, data)?;
+ Self::blocking_wait_ready();
+ Ok(())
+ }
+
+ /// Raw QSPI write, blocking version.
+ ///
+ /// The difference with `blocking_write` is that this does not do bounds checks
+ /// against the flash capacity. It is intended for use when QSPI is used as
+ /// a raw bus, not with flash memory.
+ pub fn blocking_write_raw(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
+ self.start_write(address, data)?;
+ Self::blocking_wait_ready();
+ Ok(())
+ }
+
+ /// Read data from the flash memory.
+ pub async fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
+ self.bounds_check(address, data.len())?;
+ self.read_raw(address, data).await
+ }
+
+ /// Write data to the flash memory.
+ pub async fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
+ self.bounds_check(address, data.len())?;
+ self.write_raw(address, data).await
+ }
+
/// Erase a sector on the flash memory.
- pub async fn erase(&mut self, address: usize) -> Result<(), Error> {
+ pub async fn erase(&mut self, address: u32) -> Result<(), Error> {
+ if address >= self.capacity {
+ return Err(Error::OutOfBounds);
+ }
+
let ondrop = OnDrop::new(Self::blocking_wait_ready);
self.start_erase(address)?;
@@ -417,28 +463,39 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Qspi<'d, T, FLASH_SIZE> {
}
/// Read data from the flash memory, blocking version.
- 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_read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> {
+ self.bounds_check(address, data.len())?;
+ self.blocking_read_raw(address, data)
}
/// Write data to the flash memory, blocking version.
- 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_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> {
+ self.bounds_check(address, data.len())?;
+ self.blocking_write_raw(address, data)
}
/// Erase a sector on the flash memory, blocking version.
- pub fn blocking_erase(&mut self, address: usize) -> Result<(), Error> {
+ pub fn blocking_erase(&mut self, address: u32) -> Result<(), Error> {
+ if address >= self.capacity {
+ return Err(Error::OutOfBounds);
+ }
+
self.start_erase(address)?;
Self::blocking_wait_ready();
Ok(())
}
+
+ fn bounds_check(&self, address: u32, len: usize) -> Result<(), Error> {
+ let len_u32: u32 = len.try_into().map_err(|_| Error::OutOfBounds)?;
+ let end_address = address.checked_add(len_u32).ok_or(Error::OutOfBounds)?;
+ if end_address > self.capacity {
+ return Err(Error::OutOfBounds);
+ }
+ Ok(())
+ }
}
-impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE> {
+impl<'d, T: Instance> Drop for Qspi<'d, T> {
fn drop(&mut self) {
let r = T::regs();
@@ -483,9 +540,7 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Drop for Qspi<'d, T, FLASH_SIZE>
}
}
-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> {
+impl<'d, T: Instance> ErrorType for Qspi<'d, T> {
type Error = Error;
}
@@ -495,72 +550,73 @@ impl NorFlashError for Error {
}
}
-impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Qspi<'d, T, FLASH_SIZE> {
+impl<'d, T: Instance> ReadNorFlash for Qspi<'d, T> {
const READ_SIZE: usize = 4;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
- self.blocking_read(offset as usize, bytes)?;
+ self.blocking_read(offset, bytes)?;
Ok(())
}
fn capacity(&self) -> usize {
- FLASH_SIZE
+ self.capacity as usize
}
}
-impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Qspi<'d, T, FLASH_SIZE> {
+impl<'d, T: Instance> NorFlash for Qspi<'d, T> {
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(