use core::marker::PhantomData;
use super::Nvm;
use crate::pac::{Nvmctrl, nvmctrl::ctrlb::Cmdselect};
use crate::typelevel::Sealed;
pub struct SmartEeprom<'a, T: SmartEepromState> {
nvm: &'a mut Nvm,
virtual_size: usize,
__: PhantomData<T>,
}
pub trait SmartEepromState: Sealed {}
pub enum Locked {}
impl SmartEepromState for Locked {}
impl Sealed for Locked {}
pub enum Unlocked {}
impl SmartEepromState for Unlocked {}
impl Sealed for Unlocked {}
#[derive(Debug)]
pub enum SmartEepromRetrievalFailure {
Disabled,
DisabledAutomaticPageReallocationNotSupported,
BufferedWritesNotSupported,
InvalidBlockCount {
sblk: u32,
},
}
pub enum SmartEepromMode<'a> {
Locked(SmartEeprom<'a, Locked>),
Unlocked(SmartEeprom<'a, Unlocked>),
}
pub type Result<'a> = core::result::Result<SmartEepromMode<'a>, SmartEepromRetrievalFailure>;
#[inline]
fn wait_if_busy() {
let nvmctrl = unsafe { &*Nvmctrl::ptr() };
while nvmctrl.seestat().read().busy().bit_is_set() {}
}
impl<'a> SmartEepromMode<'a> {
pub(super) fn retrieve(nvm: &'a mut Nvm) -> Result<'a> {
use SmartEepromMode as Mode;
use SmartEepromRetrievalFailure::*;
if nvm.nvm.seecfg().read().aprdis().bit_is_set() {
return Err(DisabledAutomaticPageReallocationNotSupported);
}
if nvm.nvm.seecfg().read().wmode().is_buffered() {
return Err(BufferedWritesNotSupported);
}
let sblk = nvm.nvm.seestat().read().sblk().bits() as u32;
let psz = nvm.nvm.seestat().read().psz().bits() as u32;
let virtual_size = match (sblk, psz) {
(0, _) => return Err(Disabled),
(sblk @ 11..=u32::MAX, _) => return Err(InvalidBlockCount { sblk }),
(_, 8..=u32::MAX) => {
unreachable!("`Nvmctrl.SEESTAT.PSZ` value is represented with 3 bits in user page")
}
others => Self::map_sblk_psz_to_virtual_size(others),
};
if nvm.nvm.seestat().read().lock().bit_is_set() {
Ok(Mode::Locked(SmartEeprom {
nvm,
virtual_size,
__: PhantomData::<Locked>,
}))
} else {
Ok(Mode::Unlocked(SmartEeprom {
nvm,
virtual_size,
__: PhantomData::<Unlocked>,
}))
}
}
fn map_sblk_psz_to_virtual_size(sblk_psz_pair: (u32, u32)) -> usize {
match sblk_psz_pair {
(_, 0) => 512,
(_, 1) => 1024,
(_, 2) => 2048,
(_, 3) => 4096,
(1, _) => 4096,
(_, 4) => 8192,
(2, _) => 8192,
(_, 5) => 16384,
(3 | 4, _) => 16384,
(_, 6) => 32768,
(5..=8, _) => 32768,
(_, 7) => 65536,
_ => unreachable!(),
}
}
}
impl<'a, T: SmartEepromState> SmartEeprom<'a, T> {
const SEEPROM_ADDR: *mut usize = 0x44000000 as _;
pub unsafe fn get_slice<TP: SmartEepromPointableSize>(&self) -> &[TP] {
unsafe {
core::slice::from_raw_parts_mut(
Self::SEEPROM_ADDR as _,
self.virtual_size / core::mem::size_of::<TP>(),
)
}
}
pub fn get<TP: SmartEepromPointableSize>(&self, offset: usize, buffer: &mut [TP]) {
let slice = unsafe { self.get_slice() };
buffer
.iter_mut()
.zip(slice.iter().skip(offset))
.for_each(|(target, source)| {
wait_if_busy();
*target = *source
});
}
pub fn iter<TP: SmartEepromPointableSize>(&'a self) -> SmartEepromIter<'a, TP> {
SmartEepromIter {
iter: unsafe { self.get_slice().iter() },
}
}
}
pub trait SmartEepromPointableSize: Sealed + Copy {}
impl SmartEepromPointableSize for u8 {}
impl SmartEepromPointableSize for u16 {}
impl SmartEepromPointableSize for u32 {}
impl<'a> SmartEeprom<'a, Unlocked> {
pub unsafe fn get_mut_slice<TP: SmartEepromPointableSize>(&mut self) -> &mut [TP] {
unsafe {
core::slice::from_raw_parts_mut(
Self::SEEPROM_ADDR as _,
self.virtual_size / core::mem::size_of::<TP>(),
)
}
}
pub fn set<TP: SmartEepromPointableSize>(&mut self, offset: usize, buffer: &[TP]) {
let slice = unsafe { self.get_mut_slice() };
buffer
.iter()
.zip(slice.iter_mut().skip(offset))
.for_each(|(source, target)| {
wait_if_busy();
*target = *source
});
}
pub fn iter_mut<TP: SmartEepromPointableSize>(&'a mut self) -> SmartEepromIterMut<'a, TP> {
SmartEepromIterMut {
iter: unsafe { self.get_mut_slice().iter_mut() },
}
}
pub fn lock(self) -> SmartEeprom<'a, Locked> {
self.nvm
.command_sync(Cmdselect::Lsee)
.expect("SmartEEPROM locking failed");
let Self {
nvm, virtual_size, ..
} = self;
SmartEeprom {
nvm,
virtual_size,
__: PhantomData,
}
}
}
impl<'a> SmartEeprom<'a, Locked> {
pub fn unlock(self) -> SmartEeprom<'a, Unlocked> {
self.nvm
.command_sync(Cmdselect::Usee)
.expect("SmartEEPROM unlocking failed");
let Self {
nvm, virtual_size, ..
} = self;
SmartEeprom {
nvm,
virtual_size,
__: PhantomData,
}
}
}
pub struct SmartEepromIter<'a, TP: SmartEepromPointableSize> {
iter: core::slice::Iter<'a, TP>,
}
impl<'a, TP: SmartEepromPointableSize> Iterator for SmartEepromIter<'a, TP> {
type Item = &'a TP;
fn next(&mut self) -> Option<Self::Item> {
wait_if_busy();
self.iter.next()
}
}
impl<TP: SmartEepromPointableSize> DoubleEndedIterator for SmartEepromIter<'_, TP> {
fn next_back(&mut self) -> Option<Self::Item> {
wait_if_busy();
self.iter.next_back()
}
}
pub struct SmartEepromIterMut<'a, TP: SmartEepromPointableSize> {
iter: core::slice::IterMut<'a, TP>,
}
impl<'a, TP: SmartEepromPointableSize> Iterator for SmartEepromIterMut<'a, TP> {
type Item = &'a mut TP;
fn next(&mut self) -> Option<Self::Item> {
wait_if_busy();
self.iter.next()
}
}
impl<TP: SmartEepromPointableSize> DoubleEndedIterator for SmartEepromIterMut<'_, TP> {
fn next_back(&mut self) -> Option<Self::Item> {
wait_if_busy();
self.iter.next_back()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_if_virtual_size_is_mapped_correctly() {
fn f(sblk: u32, psz: u32) -> usize {
SmartEepromMode::map_sblk_psz_to_virtual_size((sblk, psz))
}
assert_eq!(f(1, 0), 512);
assert_eq!(f(1, 1), 1024);
assert_eq!(f(1, 2), 2048);
assert_eq!(f(1, 3), 4096);
assert_eq!(f(1, 4), 4096);
assert_eq!(f(1, 5), 4096);
assert_eq!(f(1, 6), 4096);
assert_eq!(f(1, 7), 4096);
assert_eq!(f(2, 0), 512);
assert_eq!(f(2, 1), 1024);
assert_eq!(f(2, 2), 2048);
assert_eq!(f(2, 3), 4096);
assert_eq!(f(2, 4), 8192);
assert_eq!(f(2, 5), 8192);
assert_eq!(f(2, 6), 8192);
assert_eq!(f(2, 7), 8192);
assert_eq!(f(3, 0), 512);
for i in 3..=4 {
assert_eq!(f(i, 1), 1024);
assert_eq!(f(i, 2), 2048);
assert_eq!(f(i, 3), 4096);
assert_eq!(f(i, 4), 8192);
assert_eq!(f(i, 5), 16384);
assert_eq!(f(i, 6), 16384);
assert_eq!(f(i, 7), 16384);
}
for i in 5..=8 {
assert_eq!(f(i, 0), 512);
assert_eq!(f(i, 1), 1024);
assert_eq!(f(i, 2), 2048);
assert_eq!(f(i, 3), 4096);
assert_eq!(f(i, 4), 8192);
assert_eq!(f(i, 5), 16384);
assert_eq!(f(i, 6), 32768);
assert_eq!(f(i, 7), 32768);
}
for i in 9..=10 {
assert_eq!(f(i, 0), 512);
assert_eq!(f(i, 1), 1024);
assert_eq!(f(i, 2), 2048);
assert_eq!(f(i, 3), 4096);
assert_eq!(f(i, 4), 8192);
assert_eq!(f(i, 5), 16384);
assert_eq!(f(i, 6), 32768);
assert_eq!(f(i, 7), 65536);
}
}
}