use crate::gcr::clocks::{Clock, SystemClock};
pub const FLASH_BASE: u32 = 0x1000_0000;
pub const FLASH_SIZE: u32 = 0x0008_0000;
pub const FLASH_END: u32 = FLASH_BASE + FLASH_SIZE;
pub const FLASH_PAGE_COUNT: u32 = 64;
pub const FLASH_PAGE_SIZE: u32 = 0x2000;
#[derive(Debug, PartialEq)]
pub enum FlashError {
InvalidAddress,
AccessViolation,
NeedsErase,
}
pub struct Flc {
flc: crate::pac::Flc,
sys_clk: Clock<SystemClock>,
}
impl Flc {
pub fn new(flc: crate::pac::Flc, sys_clk: Clock<SystemClock>) -> Self {
let s = Self { flc, sys_clk };
s.config();
s
}
#[inline]
fn config(&self) {
while self.is_busy() {}
let flc_div = self.sys_clk.frequency / 1_000_000;
self.flc
.clkdiv()
.modify(|_, w| unsafe { w.clkdiv().bits(flc_div as u8) });
if self.flc.intr().read().af().bit_is_set() {
self.flc.intr().write(|w| w.af().clear_bit());
}
}
#[inline]
pub fn is_busy(&self) -> bool {
let ctrl = self.flc.ctrl().read();
ctrl.pend().is_busy()
|| ctrl.pge().bit_is_set()
|| ctrl.me().bit_is_set()
|| ctrl.wr().bit_is_set()
}
#[inline]
pub fn check_address(&self, address: u32) -> Result<(), FlashError> {
if address < FLASH_BASE || address >= FLASH_END {
return Err(FlashError::InvalidAddress);
}
Ok(())
}
#[inline]
pub fn check_page_number(&self, page_number: u32) -> Result<(), FlashError> {
if page_number >= FLASH_PAGE_COUNT {
return Err(FlashError::InvalidAddress);
}
Ok(())
}
#[inline]
pub fn get_address(&self, page_number: u32) -> Result<u32, FlashError> {
self.check_page_number(page_number)?;
let address = FLASH_BASE + FLASH_PAGE_SIZE * page_number;
Ok(address)
}
#[inline]
pub fn get_page_number(&self, address: u32) -> Result<u32, FlashError> {
self.check_address(address)?;
let page_num = (address >> 13) & (FLASH_PAGE_COUNT - 1);
if page_num >= FLASH_PAGE_COUNT {
return Err(FlashError::InvalidAddress);
}
Ok(page_num)
}
#[inline]
fn set_address(&self, address: u32) -> Result<(), FlashError> {
self.check_address(address)?;
let phys_addr = address & (FLASH_SIZE - 1);
self.flc
.addr()
.write(|w| unsafe { w.addr().bits(phys_addr) });
Ok(())
}
#[inline]
fn unlock_flash(&self) {
self.flc.ctrl().modify(|_, w| w.unlock().unlocked());
while self.flc.ctrl().read().unlock().is_locked() {}
}
#[inline]
fn lock_flash(&self) {
self.flc.ctrl().modify(|_, w| w.unlock().locked());
while self.flc.ctrl().read().unlock().is_unlocked() {}
}
#[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
#[inline]
fn commit_write(&self) {
self.flc.ctrl().modify(|_, w| w.wr().start());
while !self.flc.ctrl().read().wr().is_complete() {}
while self.is_busy() {}
}
#[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
#[inline]
fn commit_erase(&self) {
self.flc.ctrl().modify(|_, w| w.pge().start());
while !self.flc.ctrl().read().pge().is_complete() {}
while self.is_busy() {}
}
#[doc(hidden)]
#[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
#[inline(never)]
fn _write_128(&self, address: u32, data: &[u32; 4]) -> Result<(), FlashError> {
if address & 0b1111 != 0 {
return Err(FlashError::InvalidAddress);
}
self.check_address(address)?;
self.config();
for i in 0..4 {
let old_data = unsafe { core::ptr::read_volatile((address + i * 4) as *const u32) };
if (old_data & data[i as usize]) != data[i as usize] {
return Err(FlashError::NeedsErase);
}
}
self.set_address(address)?;
unsafe {
self.flc.data(0).write(|w| w.data().bits(data[0]));
self.flc.data(1).write(|w| w.data().bits(data[1]));
self.flc.data(2).write(|w| w.data().bits(data[2]));
self.flc.data(3).write(|w| w.data().bits(data[3]));
}
self.unlock_flash();
self.commit_write();
self.lock_flash();
if self.flc.intr().read().af().bit_is_set() {
self.flc.intr().write(|w| w.af().clear_bit());
return Err(FlashError::AccessViolation);
}
Ok(())
}
#[doc(hidden)]
#[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
#[inline(never)]
fn _erase_page(&self, address: u32) -> Result<(), FlashError> {
while self.is_busy() {}
self.set_address(address)?;
self.unlock_flash();
self.flc.ctrl().modify(|_, w| w.erase_code().erase_page());
self.commit_erase();
self.lock_flash();
if self.flc.intr().read().af().bit_is_set() {
self.flc.intr().write(|w| w.af().clear_bit());
return Err(FlashError::AccessViolation);
}
Ok(())
}
pub fn write_128(&self, address: u32, data: &[u32; 4]) -> Result<(), FlashError> {
self._write_128(address, &data)
}
pub fn write_32(&self, address: u32, data: u32) -> Result<(), FlashError> {
if address & 0b11 != 0 {
return Err(FlashError::InvalidAddress);
}
self.check_address(address)?;
let addr_128 = address & !0b1111;
self.check_address(addr_128)?;
let addr_128_ptr = addr_128 as *const u32;
let mut prev_data: [u32; 4] = [0xFFFF_FFFF; 4];
unsafe {
prev_data[0] = core::ptr::read_volatile(addr_128_ptr);
prev_data[1] = core::ptr::read_volatile(addr_128_ptr.offset(1));
prev_data[2] = core::ptr::read_volatile(addr_128_ptr.offset(2));
prev_data[3] = core::ptr::read_volatile(addr_128_ptr.offset(3));
}
let data_idx = (address & 0b1100) >> 2;
prev_data[data_idx as usize] = data;
self._write_128(addr_128, &prev_data)
}
pub fn read_128(&self, address: u32) -> Result<[u32; 4], FlashError> {
if address & 0b1111 != 0 {
return Err(FlashError::InvalidAddress);
}
self.check_address(address)?;
let addr_128_ptr = address as *const u32;
unsafe {
Ok([
core::ptr::read_volatile(addr_128_ptr),
core::ptr::read_volatile(addr_128_ptr.offset(1)),
core::ptr::read_volatile(addr_128_ptr.offset(2)),
core::ptr::read_volatile(addr_128_ptr.offset(3)),
])
}
}
pub fn read_32(&self, address: u32) -> Result<u32, FlashError> {
if address & 0b11 != 0 {
return Err(FlashError::InvalidAddress);
}
self.check_address(address)?;
let addr_32_ptr = address as *const u32;
unsafe { Ok(core::ptr::read_volatile(addr_32_ptr)) }
}
pub unsafe fn erase_page(&self, address: u32) -> Result<(), FlashError> {
self._erase_page(address)
}
pub fn disable_page_write(&self, address: u32) -> Result<(), FlashError> {
while self.is_busy() {}
let page_num = self.get_page_number(address)?;
if page_num < 32 {
let write_lock_bit = 1 << page_num;
self.flc
.welr0()
.write(|w| unsafe { w.bits(write_lock_bit) });
while self.flc.welr0().read().bits() & write_lock_bit == write_lock_bit {}
} else {
let write_lock_bit = 1 << (page_num - 32);
self.flc
.welr1()
.write(|w| unsafe { w.bits(write_lock_bit) });
while self.flc.welr1().read().bits() & write_lock_bit == write_lock_bit {}
}
Ok(())
}
pub fn disable_page_read(&self, address: u32) -> Result<(), FlashError> {
while self.is_busy() {}
let page_num = self.get_page_number(address)?;
if page_num < 32 {
let read_lock_bit = 1 << page_num;
self.flc.rlr0().write(|w| unsafe { w.bits(read_lock_bit) });
while self.flc.rlr0().read().bits() & read_lock_bit == read_lock_bit {}
} else {
let read_lock_bit = 1 << (page_num - 32);
self.flc.rlr1().write(|w| unsafe { w.bits(read_lock_bit) });
while self.flc.rlr1().read().bits() & read_lock_bit == read_lock_bit {}
}
Ok(())
}
}