use crate::interrupt;
use crate::peripheral::SAU;
use bitfield::bitfield;
use volatile_register::{RO, RW};
#[repr(C)]
pub struct RegisterBlock {
pub ctrl: RW<Ctrl>,
pub _type: RO<Type>,
pub rnr: RW<Rnr>,
pub rbar: RW<Rbar>,
pub rlar: RW<Rlar>,
pub sfsr: RO<Sfsr>,
pub sfar: RO<Sfar>,
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Ctrl(u32);
get_enable, set_enable: 0;
get_allns, set_allns: 1;
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Type(u32);
u8;
sregion, _: 7, 0;
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Rnr(u32);
u8;
get_region, set_region: 7, 0;
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Rbar(u32);
u32;
get_baddr, set_baddr: 31, 5;
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Rlar(u32);
u32;
get_laddr, set_laddr: 31, 5;
get_nsc, set_nsc: 1;
get_enable, set_enable: 0;
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Sfsr(u32);
invep, _: 0;
invis, _: 1;
inver, _: 2;
auviol, _: 3;
invtran, _: 4;
lsperr, _: 5;
sfarvalid, _: 6;
lserr, _: 7;
}
bitfield! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Sfar(u32);
u32;
address, _: 31, 0;
}
#[derive(Debug)]
pub enum SauRegionAttribute {
Secure,
NonSecureCallable,
NonSecure,
}
#[derive(Debug)]
pub struct SauRegion {
pub base_address: u32,
pub limit_address: u32,
pub attribute: SauRegionAttribute,
}
#[derive(Debug)]
pub enum SauError {
RegionNumberTooBig,
WrongBaseAddress,
WrongLimitAddress,
}
impl SAU {
#[inline]
pub fn region_numbers(&self) -> u8 {
self._type.read().sregion()
}
#[inline]
pub fn enable(&mut self) {
unsafe {
self.ctrl.modify(|mut ctrl| {
ctrl.set_enable(true);
ctrl
});
}
}
#[inline]
pub fn set_region(&mut self, region_number: u8, region: SauRegion) -> Result<(), SauError> {
interrupt::free(|_| {
let base_address = region.base_address;
let limit_address = region.limit_address;
let attribute = region.attribute;
if region_number >= self.region_numbers() {
Err(SauError::RegionNumberTooBig)
} else if base_address & 0x1F != 0 {
Err(SauError::WrongBaseAddress)
} else if limit_address & 0x1F != 0x1F {
Err(SauError::WrongLimitAddress)
} else {
let mut rnr = Rnr(0);
let mut rbar = Rbar(0);
let mut rlar = Rlar(0);
rnr.set_region(region_number);
rbar.set_baddr(base_address >> 5);
rlar.set_laddr(limit_address >> 5);
match attribute {
SauRegionAttribute::Secure => {
rlar.set_nsc(false);
rlar.set_enable(false);
}
SauRegionAttribute::NonSecureCallable => {
rlar.set_nsc(true);
rlar.set_enable(true);
}
SauRegionAttribute::NonSecure => {
rlar.set_nsc(false);
rlar.set_enable(true);
}
}
unsafe {
self.rnr.write(rnr);
self.rbar.write(rbar);
self.rlar.write(rlar);
}
Ok(())
}
})
}
#[inline]
pub fn get_region(&mut self, region_number: u8) -> Result<SauRegion, SauError> {
interrupt::free(|_| {
if region_number >= self.region_numbers() {
Err(SauError::RegionNumberTooBig)
} else {
unsafe {
self.rnr.write(Rnr(region_number.into()));
}
let rbar = self.rbar.read();
let rlar = self.rlar.read();
let attribute = match (rlar.get_enable(), rlar.get_nsc()) {
(false, _) => SauRegionAttribute::Secure,
(true, false) => SauRegionAttribute::NonSecure,
(true, true) => SauRegionAttribute::NonSecureCallable,
};
Ok(SauRegion {
base_address: rbar.get_baddr() << 5,
limit_address: (rlar.get_laddr() << 5) | 0x1F,
attribute,
})
}
})
}
}