Robin Mueller 39641ab3b6
Some checks failed
Rust/va416xx-rs/pipeline/pr-main There was a failure building this commit
Bootloader and Flashloader App
2024-09-12 12:40:34 +02:00

268 lines
7.9 KiB
Rust

use embedded_hal::spi::MODE_0;
use crate::clock::{Clocks, SyscfgExt};
use crate::pac;
use crate::spi::{
mode_to_cpo_cph_bit, spi_clk_config_from_div, Instance, WordProvider, BMSTART_BMSTOP_MASK,
};
const NVM_CLOCK_DIV: u16 = 2;
// Commands. The internal FRAM is based on the Cypress FM25V20A device.
/// Write enable register.
pub const FRAM_WREN: u8 = 0x06;
pub const FRAM_WRDI: u8 = 0x04;
pub const FRAM_RDSR: u8 = 0x05;
/// Write single status register
pub const FRAM_WRSR: u8 = 0x01;
pub const FRAM_READ: u8 = 0x03;
pub const FRAM_WRITE: u8 = 0x02;
pub const FRAM_RDID: u8 = 0x9F;
pub const FRAM_SLEEP: u8 = 0xB9;
/* Address Masks */
const ADDR_MSB_MASK: u32 = 0xFF0000;
const ADDR_MID_MASK: u32 = 0x00FF00;
const ADDR_LSB_MASK: u32 = 0x0000FF;
#[inline(always)]
const fn msb_addr_byte(addr: u32) -> u8 {
((addr & ADDR_MSB_MASK) >> 16) as u8
}
#[inline(always)]
const fn mid_addr_byte(addr: u32) -> u8 {
((addr & ADDR_MID_MASK) >> 8) as u8
}
#[inline(always)]
const fn lsb_addr_byte(addr: u32) -> u8 {
(addr & ADDR_LSB_MASK) as u8
}
pub const WPEN_ENABLE_MASK: u8 = 1 << 7;
pub const BP_0_ENABLE_MASK: u8 = 1 << 2;
pub const BP_1_ENABLE_MASK: u8 = 1 << 3;
pub struct Nvm {
spi: Option<pac::Spi3>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", defmt::Format)]
pub struct VerifyError {
addr: u32,
found: u8,
expected: u8,
}
impl Nvm {
pub fn new(syscfg: &mut pac::Sysconfig, spi: pac::Spi3, _clocks: &Clocks) -> Self {
crate::clock::enable_peripheral_clock(syscfg, pac::Spi3::PERIPH_SEL);
// This is done in the C HAL.
syscfg.assert_periph_reset_for_two_cycles(pac::Spi3::PERIPH_SEL);
let spi_clk_cfg = spi_clk_config_from_div(NVM_CLOCK_DIV).unwrap();
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(MODE_0);
spi.ctrl0().write(|w| {
unsafe {
w.size().bits(u8::word_reg());
w.scrdv().bits(spi_clk_cfg.scrdv());
// Clear clock phase and polarity. Will be set to correct value for each
// transfer
w.spo().bit(cpo_bit);
w.sph().bit(cph_bit)
}
});
spi.ctrl1().write(|w| {
w.blockmode().set_bit();
unsafe { w.ss().bits(0) };
w.bmstart().set_bit();
w.bmstall().set_bit()
});
spi.clkprescale()
.write(|w| unsafe { w.bits(spi_clk_cfg.prescale_val() as u32) });
spi.fifo_clr().write(|w| {
w.rxfifo().set_bit();
w.txfifo().set_bit()
});
// Enable the peripheral as the last step as recommended in the
// programmers guide
spi.ctrl1().modify(|_, w| w.enable().set_bit());
let mut nvm = Self { spi: Some(spi) };
nvm.disable_write_prot();
nvm
}
pub fn disable_write_prot(&mut self) {
self.wait_for_tx_idle();
self.write_with_bmstop(FRAM_WREN);
self.wait_for_tx_idle();
self.write_single(FRAM_WRSR);
self.write_with_bmstop(0x00);
self.wait_for_tx_idle();
}
pub fn read_rdsr(&self) -> u8 {
self.write_single(FRAM_RDSR);
self.write_with_bmstop(0x00);
self.wait_for_rx_available();
self.read_single_word();
self.wait_for_rx_available();
(self.read_single_word() & 0xff) as u8
}
pub fn enable_write_prot(&mut self) {
self.wait_for_tx_idle();
self.write_with_bmstop(FRAM_WREN);
self.wait_for_tx_idle();
self.write_single(FRAM_WRSR);
self.write_with_bmstop(0x00);
}
#[inline(always)]
pub fn spi(&self) -> &pac::Spi3 {
self.spi.as_ref().unwrap()
}
#[inline(always)]
pub fn write_single(&self, word: u8) {
self.spi().data().write(|w| unsafe { w.bits(word as u32) })
}
#[inline(always)]
pub fn write_with_bmstop(&self, word: u8) {
self.spi()
.data()
.write(|w| unsafe { w.bits(BMSTART_BMSTOP_MASK | word as u32) })
}
#[inline(always)]
pub fn wait_for_tx_idle(&self) {
while self.spi().status().read().tfe().bit_is_clear() {
cortex_m::asm::nop();
}
while self.spi().status().read().busy().bit_is_set() {
cortex_m::asm::nop();
}
self.clear_fifos()
}
#[inline(always)]
pub fn clear_fifos(&self) {
self.spi().fifo_clr().write(|w| {
w.rxfifo().set_bit();
w.txfifo().set_bit()
})
}
#[inline(always)]
pub fn wait_for_rx_available(&self) {
while !self.spi().status().read().rne().bit_is_set() {
cortex_m::asm::nop();
}
}
#[inline(always)]
pub fn read_single_word(&self) -> u32 {
self.spi().data().read().bits()
}
pub fn write_data(&self, addr: u32, data: &[u8]) {
self.wait_for_tx_idle();
self.write_with_bmstop(FRAM_WREN);
self.wait_for_tx_idle();
self.write_single(FRAM_WRITE);
self.write_single(msb_addr_byte(addr));
self.write_single(mid_addr_byte(addr));
self.write_single(lsb_addr_byte(addr));
for byte in data.iter().take(data.len() - 1) {
while self.spi().status().read().tnf().bit_is_clear() {
cortex_m::asm::nop();
}
self.write_single(*byte);
self.read_single_word();
}
while self.spi().status().read().tnf().bit_is_clear() {
cortex_m::asm::nop();
}
self.write_with_bmstop(*data.last().unwrap());
self.wait_for_tx_idle();
}
pub fn read_data(&self, addr: u32, buf: &mut [u8]) {
self.common_read_start(addr);
for byte in buf {
// Pump the SPI.
self.write_single(0);
self.wait_for_rx_available();
*byte = self.read_single_word() as u8;
}
self.write_with_bmstop(0);
self.wait_for_tx_idle();
}
pub fn verify_data(&self, addr: u32, comp_buf: &[u8]) -> Result<(), VerifyError> {
self.common_read_start(addr);
for (idx, byte) in comp_buf.iter().enumerate() {
// Pump the SPI.
self.write_single(0);
self.wait_for_rx_available();
let next_word = self.read_single_word() as u8;
if next_word != *byte {
self.write_with_bmstop(0);
self.wait_for_tx_idle();
return Err(VerifyError {
addr: addr + idx as u32,
found: next_word,
expected: *byte,
});
}
}
self.write_with_bmstop(0);
self.wait_for_tx_idle();
Ok(())
}
/// Enable write-protection and disables the peripheral clock.
pub fn shutdown(&mut self, sys_cfg: &mut pac::Sysconfig) {
self.wait_for_tx_idle();
self.write_with_bmstop(FRAM_WREN);
self.wait_for_tx_idle();
self.write_single(WPEN_ENABLE_MASK | BP_0_ENABLE_MASK | BP_1_ENABLE_MASK);
crate::clock::disable_peripheral_clock(sys_cfg, pac::Spi3::PERIPH_SEL);
}
/// This function calls [Self::shutdown] and gives back the peripheral structure.
pub fn release(mut self, sys_cfg: &mut pac::Sysconfig) -> pac::Spi3 {
self.shutdown(sys_cfg);
self.spi.take().unwrap()
}
fn common_read_start(&self, addr: u32) {
self.wait_for_tx_idle();
self.write_single(FRAM_READ);
self.write_single(msb_addr_byte(addr));
self.write_single(mid_addr_byte(addr));
self.write_single(lsb_addr_byte(addr));
for _ in 0..4 {
// Pump the SPI.
self.write_single(0);
self.wait_for_rx_available();
// The first 4 data bytes received need to be ignored.
self.read_single_word();
}
}
}
/// Call [Self::shutdown] on drop.
impl Drop for Nvm {
fn drop(&mut self) {
if self.spi.is_some() {
self.shutdown(unsafe { &mut pac::Sysconfig::steal() });
}
}
}