From cae8c11722f2933520a90bf8a16df0d0c1dff445 Mon Sep 17 00:00:00 2001 From: Artur Kowalski Date: Tue, 29 Mar 2022 14:38:09 +0200 Subject: [PATCH] Add support for partial flash erase Partial erase is a feature that allows to reduce time CPU is halted when using NVMC. It works by dividing page erase operation into multiple operations, in-between each operation CPU is resumed allowing interrupt handling. --- nrf-hal-common/src/nvmc.rs | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/nrf-hal-common/src/nvmc.rs b/nrf-hal-common/src/nvmc.rs index b16202b6..621e0ee9 100644 --- a/nrf-hal-common/src/nvmc.rs +++ b/nrf-hal-common/src/nvmc.rs @@ -19,6 +19,8 @@ use embedded_storage::nor_flash::{ type WORD = u32; const WORD_SIZE: usize = core::mem::size_of::(); const PAGE_SIZE: usize = 4 * 1024; +#[cfg(not(any(feature = "9160", feature = "5340-app")))] +const PAGE_ERASE_TIME: u32 = 85; /// Interface to an NVMC instance. pub struct Nvmc { @@ -44,6 +46,49 @@ where (self.nvmc, self.storage) } + #[cfg(not(any(feature = "9160", feature = "5340-app")))] + /// Erases the given storage range using partial erase feature. This allows + /// application to handle interrupts while erasing memory by dividing the + /// time CPU is halted in smaller periods. + /// + /// # Errors + /// + /// Returns an error if the arguments are not aligned, out of bounds or when + /// period is not in range from 1 to 127 inclusive. + pub fn partial_erase( + &mut self, + from: u32, + to: u32, + period: u32, + ) -> Result<(), ::Error> { + let (from, to) = (from as usize, to as usize); + if from > to || to > self.capacity() { + return Err(NvmcError::OutOfBounds); + } + if from % PAGE_SIZE != 0 || to % PAGE_SIZE != 0 { + return Err(NvmcError::Unaligned); + } + let (page_from, page_to) = (from / PAGE_SIZE, to / PAGE_SIZE); + + if period & !0x7F != 0 || period == 0 { + return Err(NvmcError::OutOfBounds); + } + + self.nvmc + .erasepagepartialcfg + .write(|w| unsafe { w.bits(period) }); + for page_offset in page_from..page_to { + // According to nRF52840 manual (section 4.3.9.9) CONFIG.WEN must be + // enabled before every partial erase and disabled after every + // partial erase + self.enable_erase(); + self.partial_erase_page(page_offset, period); + self.enable_read(); + } + + Ok(()) + } + fn enable_erase(&self) { #[cfg(not(any(feature = "9160", feature = "5340-app")))] self.nvmc.config.write(|w| w.wen().een()); @@ -91,6 +136,21 @@ where self.wait_ready(); } + #[cfg(not(any(feature = "9160", feature = "5340-app")))] + #[inline] + fn partial_erase_page(&mut self, page_offset: usize, period: u32) { + let bits = &mut (self.storage[page_offset * PAGE_SIZE]) as *mut _ as u32; + let mut time_left = PAGE_ERASE_TIME; + while time_left > 0 { + self.nvmc + .erasepagepartial + .write(|w| unsafe { w.bits(bits) }); + self.wait_ready(); + + time_left = time_left.saturating_sub(period); + } + } + #[inline] fn write_word(&mut self, word_offset: usize, word: u32) { #[cfg(not(any(feature = "9160", feature = "5340-app")))]