#[cfg(any(feature = "atsam4e", feature = "atsam4n"))]
use crate::pac::efc;
#[cfg(any(feature = "atsam4e", feature = "atsam4n"))]
use crate::pac::EFC;
#[cfg(feature = "atsam4s")]
use crate::pac::efc0 as efc;
#[cfg(feature = "atsam4s")]
use crate::pac::EFC0 as EFC;
#[cfg(feature = "atsam4sd")]
use crate::pac::EFC1;
use cortex_m::interrupt;
use embedded_storage::nor_flash::{
ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash,
};
const FLASH_PAGE_SIZE: u32 = 512;
const USER_SIG_FLASH_SIZE: u32 = 512;
const FLASH_LOCK_REGION_SIZE: u32 = 8192;
const FLASH_READ_SIZE: u32 = 4;
const FLASH_WRITE_SIZE: u32 = 4;
struct FlashParameters {
gpnvm_num_max: u8,
flash0_addr: u32,
flash0_size: u32,
#[cfg(feature = "atsam4sd")]
flash1_addr: u32,
#[cfg(feature = "atsam4sd")]
flash1_size: u32,
}
#[cfg(any(feature = "atsam4e8c", feature = "atsam4e8e"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x00080000,
};
#[cfg(any(feature = "atsam4e16c", feature = "atsam4e16e"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x00100000,
};
#[cfg(any(feature = "atsam4n8a", feature = "atsam4n8b", feature = "atsam4n8c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x00080000,
};
#[cfg(any(feature = "atsam4n16b", feature = "atsam4n16c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x00100000,
};
#[cfg(any(feature = "atsam4s2a", feature = "atsam4s2b", feature = "atsam4s2c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x00020000,
};
#[cfg(any(feature = "atsam4s4a", feature = "atsam4s4b", feature = "atsam4s4c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x00040000,
};
#[cfg(any(feature = "atsam4s8b", feature = "atsam4s8c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x0080000,
};
#[cfg(any(feature = "atsam4sa16b", feature = "atsam4sa16c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 2,
flash0_addr: 0x00400000,
flash0_size: 0x00100000,
};
#[cfg(any(feature = "atsam4sd16b", feature = "atsam4sd16c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 3,
flash0_addr: 0x00400000,
flash0_size: 0x00080000,
flash1_addr: 0x00480000,
flash1_size: 0x00080000,
};
#[cfg(any(feature = "atsam4sd32b", feature = "atsam4sd32c"))]
const FLASH_PARAMS: FlashParameters = FlashParameters {
gpnvm_num_max: 3,
flash0_addr: 0x00400000,
flash0_size: 0x00100000,
flash1_addr: 0x00500000,
flash1_size: 0x00100000,
};
extern "C" {
fn efc_perform_read_sequence(
efc: *const u32,
cmd_st: u32,
cmd_sp: u32,
buf: *mut u32,
size: u32,
flash_addr: *mut u32,
) -> u32;
fn efc_perform_fcr(efc: *const u32, fcr: u32) -> u32;
}
pub struct Efc {
#[cfg(any(feature = "atsam4e", feature = "atsam4n", feature = "atsam4s"))]
efc: EFC,
#[cfg(feature = "atsam4sd")]
efc1: EFC1,
storage: &'static mut [u32],
}
impl Efc {
#[cfg(any(
feature = "atsam4e",
feature = "atsam4n",
feature = "atsam4s_",
feature = "atsam4sa"
))]
pub fn new(efc: EFC, storage: &'static mut [u32]) -> Efc {
Self { efc, storage }
}
#[cfg(feature = "atsam4sd")]
pub fn new(efc0: EFC, efc1: EFC1, storage: &'static mut [u32]) -> Efc {
Self {
efc: efc0,
efc1,
storage,
}
}
#[cfg(any(feature = "atsam4e", feature = "atsam4n", feature = "atsam4s_"))]
pub fn free(self) -> (EFC, &'static mut [u32]) {
(self.efc, self.storage)
}
#[cfg(feature = "atsam4sd")]
pub fn free(self) -> (EFC, EFC1, &'static mut [u32]) {
(self.efc, self.efc1, self.storage)
}
#[inline]
fn wait_ready(&self) {
while !self.efc.fsr.read().frdy().bit() {}
}
#[cfg(feature = "atsam4sd")]
fn translate_address(&self, address: u32) -> Result<(u16, u16, u8), EfcError> {
if address < FLASH_PARAMS.flash0_addr
|| address > FLASH_PARAMS.flash1_addr + FLASH_PARAMS.flash1_size
{
return Err(EfcError::AddressBoundsError);
}
let gpnvm2 = self.is_gpnvm_set(2)?;
if address >= FLASH_PARAMS.flash1_addr {
let bank = if gpnvm2 { 0 } else { 1 }; let page = (address - FLASH_PARAMS.flash1_addr) / FLASH_PAGE_SIZE;
let offset = (address - FLASH_PARAMS.flash1_addr) % FLASH_PAGE_SIZE;
Ok((page as u16, offset as u16, bank))
} else {
let bank = u8::from(gpnvm2);
let page = (address - FLASH_PARAMS.flash0_addr) / FLASH_PAGE_SIZE;
let offset = (address - FLASH_PARAMS.flash0_addr) % FLASH_PAGE_SIZE;
Ok((page as u16, offset as u16, bank))
}
}
#[cfg(not(feature = "atsam4sd"))]
fn translate_address(&self, address: u32) -> Result<(u16, u16, u8), EfcError> {
if address < FLASH_PARAMS.flash0_addr
|| address > FLASH_PARAMS.flash0_addr + FLASH_PARAMS.flash0_size
{
return Err(EfcError::AddressBoundsError);
}
let page = (address - FLASH_PARAMS.flash0_addr) / FLASH_PAGE_SIZE;
let offset = (address - FLASH_PARAMS.flash0_addr) % FLASH_PAGE_SIZE;
Ok((page as u16, offset as u16, 0))
}
fn compute_lock_range(&self, start: u32, end: u32) -> (u32, u32) {
let actual_start = start - (start % FLASH_LOCK_REGION_SIZE);
let actual_end = end - (end % FLASH_LOCK_REGION_SIZE) + FLASH_LOCK_REGION_SIZE - 1;
(actual_start, actual_end)
}
pub fn lock(&self, start: u32, end: u32) -> Result<(u32, u32), EfcError> {
let num_pages_in_region = (FLASH_LOCK_REGION_SIZE / FLASH_PAGE_SIZE) as u16;
let (actual_start, actual_end) = self.compute_lock_range(start, end);
let (mut start_page, _, bank) = self.translate_address(actual_start)?;
let (_, end_page, _) = self.translate_address(actual_end)?;
while start_page < end_page {
self.efc_perform_command(bank, efc::fcr::FCMD_AW::SLB, start_page)?;
start_page += num_pages_in_region;
}
Ok((actual_start, actual_end))
}
pub fn unlock(&self, start: u32, end: u32) -> Result<(u32, u32), EfcError> {
let num_pages_in_region = (FLASH_LOCK_REGION_SIZE / FLASH_PAGE_SIZE) as u16;
let (actual_start, actual_end) = self.compute_lock_range(start, end);
let (mut start_page, _, bank) = self.translate_address(actual_start)?;
let (_, end_page, _) = self.translate_address(actual_end)?;
while start_page < end_page {
self.efc_perform_command(bank, efc::fcr::FCMD_AW::CLB, start_page)?;
start_page += num_pages_in_region;
}
Ok((actual_start, actual_end))
}
pub fn is_locked(&self, start: u32, end: u32) -> Result<u32, EfcError> {
if end < start
|| start < FLASH_PARAMS.flash0_addr
|| end > FLASH_PARAMS.flash0_addr + FLASH_PARAMS.flash0_size
{
return Err(EfcError::AddressBoundsError);
}
let (start_page, _, bank) = self.translate_address(start)?;
let (_, end_page, _) = self.translate_address(end)?;
let num_pages_in_region = FLASH_LOCK_REGION_SIZE / FLASH_PAGE_SIZE;
let start_region = start_page as u32 / num_pages_in_region;
let end_region = end_page as u32 / num_pages_in_region;
self.efc_perform_command(bank, efc::fcr::FCMD_AW::GLB, 0)?;
let mut count = 0;
let mut status = self.efc_get_result(bank);
while count <= start_region && start_region < count + 32 {
status = self.efc_get_result(bank);
count += 32;
}
let mut bit = start_region - count;
count = end_region - start_region + 1;
let mut num_locked_regions = 0;
while count > 0 {
if status & (1 << bit) != 0 {
num_locked_regions += 1;
}
count -= 1;
bit += 1;
if bit == 32 {
status = self.efc_get_result(bank);
bit = 0;
}
}
Ok(num_locked_regions)
}
pub fn set_gpnvm(&self, gpnvm: u8) -> Result<(), EfcError> {
if gpnvm >= FLASH_PARAMS.gpnvm_num_max {
return Err(EfcError::InvalidGpnvmBitError);
}
if self.is_gpnvm_set(gpnvm)? {
return Ok(());
}
self.efc_perform_command(0, efc::fcr::FCMD_AW::SGPB, gpnvm as u16)
}
pub fn clear_gpnvm(&self, gpnvm: u8) -> Result<(), EfcError> {
if gpnvm >= FLASH_PARAMS.gpnvm_num_max {
return Err(EfcError::InvalidGpnvmBitError);
}
if !self.is_gpnvm_set(gpnvm)? {
return Ok(());
}
self.efc_perform_command(0, efc::fcr::FCMD_AW::CGPB, gpnvm as u16)
}
pub fn is_gpnvm_set(&self, gpnvm: u8) -> Result<bool, EfcError> {
if gpnvm >= FLASH_PARAMS.gpnvm_num_max {
return Err(EfcError::InvalidGpnvmBitError);
}
self.efc_perform_command(0, efc::fcr::FCMD_AW::GGPB, gpnvm as u16)?;
let gpnvm_bits = self.efc_get_result(0);
Ok(gpnvm_bits & (1 << gpnvm) != 0)
}
pub fn enable_security_bit(&self) -> Result<(), EfcError> {
self.set_gpnvm(0)
}
pub fn is_security_bit_enabled(&self) -> Result<bool, EfcError> {
self.is_gpnvm_set(0)
}
pub fn read_unique_id(&self) -> Result<[u32; 4], EfcError> {
let mut uid: [u32; 4] = [0; 4];
self.efc_perform_read_sequence(
0,
efc::fcr::FCMD_AW::STUI,
efc::fcr::FCMD_AW::SPUI,
&mut uid,
4,
)?;
Ok(uid)
}
pub fn read_user_signature(&self, data: &mut [u32], len: usize) -> Result<(), EfcError> {
if len > USER_SIG_FLASH_SIZE as usize / core::mem::size_of::<u32>() {
return Err(EfcError::InvalidUserSignatureSizeError);
}
self.efc_perform_read_sequence(
0,
efc::fcr::FCMD_AW::STUS,
efc::fcr::FCMD_AW::SPUS,
data,
len,
)
}
pub fn write_user_signature(&mut self, data: &[u32]) -> Result<(), EfcError> {
if data.len() > USER_SIG_FLASH_SIZE as usize / core::mem::size_of::<u32>() {
return Err(EfcError::InvalidUserSignatureSizeError);
}
self.storage[..data.len()].clone_from_slice(data);
self.efc_perform_command(0, efc::fcr::FCMD_AW::WUS, 0)
}
pub fn erase_user_signature(&self) -> Result<(), EfcError> {
self.efc_perform_command(0, efc::fcr::FCMD_AW::EUS, 0)
}
#[cfg(not(feature = "atsam4sd"))]
fn efc_get_result(&self, _bank: u8) -> u32 {
self.efc.frr.read().fvalue().bits()
}
#[cfg(feature = "atsam4sd")]
fn efc_get_result(&self, bank: u8) -> u32 {
if bank == 0 {
self.efc.frr.read().fvalue().bits()
} else {
self.efc1.frr.read().fvalue().bits()
}
}
fn efc_perform_command(
&self,
bank: u8,
command: efc::fcr::FCMD_AW,
argument: u16,
) -> Result<(), EfcError> {
match command {
efc::fcr::FCMD_AW::STUI | efc::fcr::FCMD_AW::SPUI => {
return Err(EfcError::UnsupportedCommandError);
}
_ => {}
}
self.efc_fcr_command(bank, command, argument)
}
fn efc_fcr_command(
&self,
bank: u8,
command: efc::fcr::FCMD_AW,
argument: u16,
) -> Result<(), EfcError> {
let fcr_cmd: u32 = ((efc::fcr::FKEY_AW::PASSWD as u32) << 24)
| ((argument as u32) << 8)
| (command as u32);
#[cfg(not(feature = "atsam4sd"))]
let efc_ptr = {
let _ = bank;
EFC::PTR as *const _
};
#[cfg(feature = "atsam4sd")]
let efc_ptr = if bank == 0 {
EFC::PTR as *const _
} else if bank == 1 {
EFC1::PTR as *const _
} else {
return Err(EfcError::InvalidFlashBank);
};
cortex_m::asm::dsb();
cortex_m::asm::isb();
let status = interrupt::free(|_| unsafe { efc_perform_fcr(efc_ptr, fcr_cmd) });
if status & (1 << 1) != 0 {
Err(EfcError::CommandError)
} else if status & (1 << 2) != 0 {
Err(EfcError::LockError)
} else if status & (1 << 3) != 0 {
Err(EfcError::FlashError)
} else {
Ok(())
}
}
fn efc_perform_read_sequence(
&self,
bank: u8,
start_cmd: efc::fcr::FCMD_AW,
stop_cmd: efc::fcr::FCMD_AW,
bytes: &mut [u32],
len: usize,
) -> Result<(), EfcError> {
if bytes.len() < len {
return Err(EfcError::InvalidBufferSizeError);
}
#[cfg(not(feature = "atsam4sd"))]
let status = {
let _ = bank;
unsafe {
efc_perform_read_sequence(
EFC::PTR as *const _,
start_cmd as u32,
stop_cmd as u32,
bytes.as_mut_ptr(),
len as u32,
FLASH_PARAMS.flash0_addr as *mut _,
)
}
};
#[cfg(feature = "atsam4sd")]
let status = {
unsafe {
if bank == 0 {
efc_perform_read_sequence(
EFC::PTR as *const _,
start_cmd as u32,
stop_cmd as u32,
bytes.as_mut_ptr(),
len as u32,
FLASH_PARAMS.flash0_addr as *mut _,
)
} else if bank == 1 {
efc_perform_read_sequence(
EFC1::PTR as *const _,
start_cmd as u32,
stop_cmd as u32,
bytes.as_mut_ptr(),
len as u32,
FLASH_PARAMS.flash1_addr as *mut _,
)
} else {
return Err(EfcError::InvalidFlashBank);
}
}
};
if status != 0 {
Err(EfcError::InvalidBufferSizeError)
} else {
Ok(())
}
}
}
impl ErrorType for Efc {
type Error = EfcError;
}
impl ReadNorFlash for Efc {
const READ_SIZE: usize = FLASH_READ_SIZE as usize;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
let offset = offset as usize;
let bytes_len = bytes.len();
let read_len = bytes_len + (Self::READ_SIZE - (bytes_len % Self::READ_SIZE));
let target_offset = offset + read_len;
if offset % Self::READ_SIZE == 0 && target_offset <= self.capacity() {
self.wait_ready();
let last_offset = target_offset - Self::READ_SIZE;
for offset in (offset..last_offset).step_by(Self::READ_SIZE) {
let word = self.storage[offset >> 2];
bytes[offset] = (word >> 24) as u8;
bytes[offset + 1] = (word >> 16) as u8;
bytes[offset + 2] = (word >> 8) as u8;
bytes[offset + 3] = (word) as u8;
}
let offset = last_offset;
let word = self.storage[offset >> 2];
let mut bytes_offset = offset;
if bytes_offset < bytes_len {
bytes[bytes_offset] = (word >> 24) as u8;
bytes_offset += 1;
if bytes_offset < bytes_len {
bytes[bytes_offset] = (word >> 16) as u8;
bytes_offset += 1;
if bytes_offset < bytes_len {
bytes[bytes_offset] = (word >> 8) as u8;
bytes_offset += 1;
if bytes_offset < bytes_len {
bytes[bytes_offset] = (word) as u8;
}
}
}
}
Ok(())
} else {
Err(EfcError::Unaligned)
}
}
#[cfg(not(feature = "atsam4sd"))]
fn capacity(&self) -> usize {
FLASH_PARAMS.flash0_size as usize
}
#[cfg(feature = "atsam4sd")]
fn capacity(&self) -> usize {
(FLASH_PARAMS.flash0_size + FLASH_PARAMS.flash1_size) as usize
}
}
impl NorFlash for Efc {
const WRITE_SIZE: usize = FLASH_WRITE_SIZE as usize;
const ERASE_SIZE: usize = 8 * FLASH_PAGE_SIZE as usize;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
if from == to {
return Ok(());
}
if from > to {
return Err(EfcError::AddressBoundsError);
}
if to >= FLASH_PARAMS.flash0_size {
return Err(EfcError::AddressBoundsError);
}
if from == 0 && to == FLASH_PARAMS.flash0_size {
return self.efc_perform_command(0, efc::fcr::FCMD_AW::EA, 0);
}
#[cfg(feature = "atsam4sd")]
if from == FLASH_PARAMS.flash1_addr && to == FLASH_PARAMS.flash1_size {
return self.efc_perform_command(1, efc::fcr::FCMD_AW::EA, 0);
}
#[cfg(feature = "atsam4sd")]
if from == 0 && to == FLASH_PARAMS.flash0_size + FLASH_PARAMS.flash1_size {
self.efc_perform_command(0, efc::fcr::FCMD_AW::EA, 0)?;
return self.efc_perform_command(1, efc::fcr::FCMD_AW::EA, 0);
}
if from % Self::ERASE_SIZE as u32 != 0 || to % Self::ERASE_SIZE as u32 != 0 {
return Err(EfcError::NotWithinFlashPageBoundsError);
}
for address in (from..to).step_by(Self::ERASE_SIZE) {
let (page, _, bank) = self.translate_address(address)?;
let farg = 1;
self.efc_perform_command(bank, efc::fcr::FCMD_AW::EPA, farg | page)?;
}
Ok(())
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
let offset = offset as usize;
if offset % Self::WRITE_SIZE == 0 && bytes.len() % Self::WRITE_SIZE == 0 {
if bytes.len() + offset > FLASH_PARAMS.flash0_size as usize {
return Err(EfcError::AddressBoundsError);
}
for offset in (offset..(offset + bytes.len())).step_by(Self::WRITE_SIZE) {
let word = ((bytes[offset] as u32) << 24)
| ((bytes[offset + 1] as u32) << 16)
| ((bytes[offset + 2] as u32) << 8)
| (bytes[offset + 3] as u32);
self.storage[offset >> 2] = word;
if (offset + Self::WRITE_SIZE) % FLASH_PAGE_SIZE as usize == 0
|| offset + Self::WRITE_SIZE == offset + bytes.len()
{
let (page, _, bank) =
self.translate_address(FLASH_PARAMS.flash0_addr + offset as u32)?;
self.efc_perform_command(bank, efc::fcr::FCMD_AW::WP, page)?;
}
}
Ok(())
} else {
Err(EfcError::Unaligned)
}
}
}
#[derive(Debug, defmt::Format)]
pub enum EfcError {
Unaligned,
CommandError,
LockError,
FlashError,
AddressBoundsError,
UnsupportedCommandError,
InvalidGpnvmBitError,
InvalidBufferSizeError,
InvalidUserSignatureSizeError,
NotWithinFlashPageBoundsError,
InvalidFlashBank,
}
impl NorFlashError for EfcError {
fn kind(&self) -> NorFlashErrorKind {
match self {
EfcError::AddressBoundsError => NorFlashErrorKind::OutOfBounds,
EfcError::CommandError => NorFlashErrorKind::Other,
EfcError::FlashError => NorFlashErrorKind::Other,
EfcError::InvalidBufferSizeError => NorFlashErrorKind::Other,
EfcError::InvalidFlashBank => NorFlashErrorKind::Other,
EfcError::InvalidGpnvmBitError => NorFlashErrorKind::Other,
EfcError::InvalidUserSignatureSizeError => NorFlashErrorKind::Other,
EfcError::LockError => NorFlashErrorKind::Other,
EfcError::NotWithinFlashPageBoundsError => NorFlashErrorKind::Other,
EfcError::Unaligned => NorFlashErrorKind::NotAligned,
EfcError::UnsupportedCommandError => NorFlashErrorKind::Other,
}
}
}