#![allow(missing_debug_implementations)]
use regs::RegisterValue;
#[cfg(trustzone)]
use crate::security::WithSecurity;
use super::*;
#[repr(usize)]
#[derive(Clone, Copy)]
pub enum Reg {
Cfg,
CfgIn,
Slew,
#[cfg(mxs40ioss)]
DriveStrength,
#[cfg(mxs40sioss)]
DriveStrength0,
#[cfg(mxs40sioss)]
DriveStrength1,
IntrCfg,
IntrMask,
Sel0,
Sel1,
#[cfg(trustzone)]
NonsecureMask,
In,
Out,
}
const NUM_PORT_REGS: usize = Reg::Out as usize + 1;
pub trait PortAccess {
unsafe fn read(&self, reg: Reg) -> u32;
unsafe fn write(&mut self, reg: Reg, v: u32);
}
impl<T: PortAccess> PortAccess for &mut T {
unsafe fn read(&self, reg: Reg) -> u32 {
unsafe { (**self).read(reg) }
}
unsafe fn write(&mut self, reg: Reg, v: u32) {
unsafe { (**self).write(reg, v) }
}
}
pub struct PortRegAccess(pub u8, pub SecurityAttribute);
impl PortAccess for PortRegAccess {
unsafe fn read(&self, reg: Reg) -> u32 {
unsafe {
let port = regs::GPIO
.prt()
.get_unchecked(self.0 as usize)
.with_security(self.1);
let hsiom = regs::HSIOM
.prt()
.get_unchecked(self.0 as usize)
.with_security(self.1);
#[cfg(trustzone)]
let hsiom_secure = regs::HSIOM
.secure_prt()
.get_unchecked(self.0 as usize)
.with_security(self.1);
match reg {
Reg::Cfg => port.cfg().read().get_raw(),
Reg::CfgIn => port.cfg_in().read().get_raw(),
#[cfg(mxs40ioss)]
Reg::Slew => port.cfg_out().read().get_raw() & 0xFFFF,
#[cfg(mxs40sioss)]
Reg::Slew => port.cfg_slew_ext().read().get_raw(),
#[cfg(mxs40ioss)]
Reg::DriveStrength => port.cfg_out().read().get_raw() >> 16,
#[cfg(mxs40sioss)]
Reg::DriveStrength0 => port.cfg_drive_ext0().read().get_raw(),
#[cfg(mxs40sioss)]
Reg::DriveStrength1 => port.cfg_drive_ext1().read().get_raw(),
Reg::IntrCfg => port.intr_cfg().read().get_raw(),
Reg::IntrMask => port.intr_mask().read().get_raw(),
Reg::Sel0 => hsiom.port_sel0().read().get_raw(),
Reg::Sel1 => hsiom.port_sel1().read().get_raw(),
#[cfg(trustzone)]
Reg::NonsecureMask => hsiom_secure.nonsecure_mask().read().get_raw(),
Reg::In => port.r#in().read().get_raw(),
Reg::Out => port.out().read().get_raw(),
}
}
}
unsafe fn write(&mut self, reg: Reg, v: u32) {
unsafe {
let port = regs::GPIO
.prt()
.get_unchecked(self.0 as usize)
.with_security(self.1);
let hsiom = regs::HSIOM
.prt()
.get_unchecked(self.0 as usize)
.with_security(self.1);
#[cfg(trustzone)]
let hsiom_secure = regs::HSIOM
.secure_prt()
.get_unchecked(self.0 as usize)
.with_security(self.1);
match reg {
Reg::Cfg => port.cfg().write_raw(v),
Reg::CfgIn => port.cfg_in().write_raw(v),
#[cfg(mxs40ioss)]
Reg::Slew => port
.cfg_out()
.modify(|reg| reg.set_raw((reg.get_raw() & !0xFFFF) | (v & 0xFFFF))),
#[cfg(mxs40sioss)]
Reg::Slew => port.cfg_slew_ext().write_raw(v),
#[cfg(mxs40ioss)]
Reg::DriveStrength => port
.cfg_out()
.modify(|reg| reg.set_raw((reg.get_raw() & 0xFFFF) | (v << 16))),
#[cfg(mxs40sioss)]
Reg::DriveStrength0 => port.cfg_drive_ext0().write_raw(v),
#[cfg(mxs40sioss)]
Reg::DriveStrength1 => port.cfg_drive_ext1().write_raw(v),
Reg::IntrCfg => port.intr_cfg().write_raw(v),
Reg::IntrMask => port.intr_mask().write_raw(v),
Reg::Sel0 => hsiom.port_sel0().write_raw(v),
Reg::Sel1 => hsiom.port_sel1().write_raw(v),
#[cfg(trustzone)]
Reg::NonsecureMask => hsiom_secure.nonsecure_mask().write_raw(v),
Reg::In => {}
Reg::Out => port.out().write_raw(v),
}
}
}
}
pub struct InitPortAccess<P: PortAccess> {
port: P,
regs: [u32; NUM_PORT_REGS],
}
impl<P: PortAccess> InitPortAccess<P> {
pub fn new(port: P) -> Self {
#[cfg_attr(not(trustzone), expect(unused_mut))]
let mut this = Self {
port,
regs: Default::default(),
};
#[cfg(trustzone)]
unsafe {
this.write(Reg::NonsecureMask, 0xff)
};
this
}
fn write_reg(&mut self, reg: Reg) {
unsafe {
self.port.write(reg, self.regs[reg as usize]);
}
}
#[inline(always)]
pub fn finish(mut self) {
#[cfg(trustzone)]
self.write_reg(Reg::NonsecureMask);
self.write_reg(Reg::Cfg);
self.write_reg(Reg::CfgIn);
self.write_reg(Reg::Slew);
#[cfg(mxs40ioss)]
self.write_reg(Reg::DriveStrength);
#[cfg(mxs40sioss)]
self.write_reg(Reg::DriveStrength0);
#[cfg(mxs40sioss)]
self.write_reg(Reg::DriveStrength1);
self.write_reg(Reg::IntrCfg);
self.write_reg(Reg::IntrMask);
self.write_reg(Reg::Sel0);
self.write_reg(Reg::Sel1);
self.write_reg(Reg::In);
self.write_reg(Reg::Out);
}
}
impl<P: PortAccess> PortAccess for InitPortAccess<P> {
unsafe fn read(&self, reg: Reg) -> u32 {
self.regs[reg as usize]
}
unsafe fn write(&mut self, reg: Reg, v: u32) {
self.regs[reg as usize] = v;
}
}
pub struct PinAccess<'a, P, AccessType> {
pub port: P,
pub pin: u8,
_access_type: AccessType,
_p: PhantomData<&'a ()>,
}
pub struct Read;
pub struct Modify;
#[derive(Clone, Copy)]
pub struct Field<const N: usize = 1> {
regs: [Reg; N],
width: u8,
}
impl<const N: usize> Field<N> {
#[inline(always)]
fn reg(&self, pin: u8) -> Reg {
self.regs[((pin * self.width) / 32) as usize]
}
#[inline(always)]
fn shift(&self, pin: u8) -> u8 {
(self.width * pin) & 31
}
#[inline(always)]
fn mask(&self) -> u32 {
(1 << self.width) - 1
}
}
const fn field(reg: Reg, width: u8) -> Field {
Field { regs: [reg], width }
}
const MODE: Field = field(Reg::Cfg, 4);
const THRESHOLD: Field = field(Reg::CfgIn, 1);
const SLEW: Field = field(Reg::Slew, 1);
#[cfg(mxs40ioss)]
const DRIVE_STRENGTH: Field = field(Reg::DriveStrength, 2);
#[cfg(mxs40sioss)]
const DRIVE_STRENGTH: Field<2> = Field {
regs: [Reg::DriveStrength0, Reg::DriveStrength1],
width: 8,
};
const INTERRUPT_MASK: Field = field(Reg::IntrMask, 1);
const INTERRUPT_EDGE: Field = field(Reg::IntrCfg, 2);
const IO_SEL: Field<2> = Field {
regs: [Reg::Sel0, Reg::Sel1],
width: 8,
};
#[cfg(trustzone)]
const NONSECURE_MASK: Field = field(Reg::NonsecureMask, 1);
const IN: Field = field(Reg::In, 1);
const OUT: Field = field(Reg::Out, 1);
impl<P: PortAccess> PinAccess<'_, P, Read> {
pub unsafe fn steal_read(port: P, pin: u8) -> Self {
PinAccess {
port,
pin,
_access_type: Read,
_p: PhantomData,
}
}
}
impl<P: PortAccess, AccessType> PinAccess<'_, P, AccessType> {
fn read<const N: usize>(&self, field: Field<N>) -> u32 {
unsafe { (self.port.read(field.reg(self.pin)) >> field.shift(self.pin)) & field.mask() }
}
pub fn mode(&self) -> (DriveMode, bool) {
let mode = self.read(MODE);
let drive_mode = unsafe { core::mem::transmute::<u32, DriveMode>(mode & 0x7) };
let input_buffer = (mode & 0x8) != 0;
(drive_mode, input_buffer)
}
pub fn threshold(&self) -> VtripMode {
unsafe { core::mem::transmute::<u32, VtripMode>(self.read(THRESHOLD)) }
}
pub fn slew_rate(&self) -> SlewRate {
unsafe { core::mem::transmute::<u32, SlewRate>(self.read(SLEW)) }
}
pub fn drive_strength(&self) -> DriveStrength {
unsafe { core::mem::transmute::<u32, DriveStrength>(self.read(DRIVE_STRENGTH)) }
}
pub fn interrupt_mask(&self) -> bool {
self.read(INTERRUPT_MASK) != 0
}
pub fn interrupt_edge(&self) -> InterruptEdge {
unsafe { core::mem::transmute::<u32, InterruptEdge>(self.read(INTERRUPT_EDGE)) }
}
pub fn io_sel(&self) -> IoSelector {
unsafe { core::mem::transmute::<u32, IoSelector>(self.read(IO_SEL)) }
}
#[cfg(trustzone)]
pub fn nonsecure_mask(&self) -> bool {
self.read(NONSECURE_MASK) != 0
}
pub fn input(&self) -> PinState {
(self.read(IN) != 0).into()
}
pub fn out(&self) -> PinState {
(self.read(OUT) != 0).into()
}
}
impl<P: PortAccess> PinAccess<'_, P, Modify> {
pub unsafe fn steal_modify(port: P, pin: u8) -> Self {
PinAccess {
port,
pin,
_access_type: Modify,
_p: PhantomData,
}
}
#[inline(always)]
fn write<const N: usize>(&mut self, field: Field<N>, v: u32) {
let v = v & field.mask();
unsafe {
let old_value =
self.port.read(field.reg(self.pin)) & !(field.mask() << field.shift(self.pin));
self.port
.write(field.reg(self.pin), old_value | v << field.shift(self.pin))
}
}
pub fn set_mode(&mut self, drive_mode: DriveMode, input_buffer: bool) {
let mode = (input_buffer as u32) << 3 | (drive_mode as u32);
self.write(MODE, mode);
}
pub fn set_drive_mode(&mut self, drive_mode: DriveMode) {
unsafe {
let mut cfg = self.port.read(MODE.reg(self.pin));
cfg &= !(0x7 << MODE.shift(self.pin));
cfg |= (drive_mode as u32) << MODE.shift(self.pin);
self.port.write(MODE.reg(self.pin), cfg);
}
}
pub fn set_input_buffer(&mut self, enabled: bool) {
unsafe {
let mut cfg = self.port.read(MODE.reg(self.pin));
cfg &= !(0x8 << MODE.shift(self.pin));
cfg |= ((enabled as u32) << 3) << MODE.shift(self.pin);
self.port.write(MODE.reg(self.pin), cfg);
}
}
pub fn set_threshold(&mut self, v: VtripMode) {
self.write(THRESHOLD, v as u32);
}
pub fn set_slew_rate(&mut self, v: SlewRate) {
self.write(SLEW, v as u32);
}
pub fn set_drive_strength(&mut self, v: DriveStrength) {
self.write(DRIVE_STRENGTH, v as u32);
}
pub fn set_interrupt_mask(&mut self, v: bool) {
self.write(INTERRUPT_MASK, v as u32);
}
pub fn set_interrupt_edge(&mut self, v: InterruptEdge) {
self.write(INTERRUPT_EDGE, v as u32);
}
pub fn set_io_sel(&mut self, v: IoSelector) {
self.write(IO_SEL, v as u32);
}
#[cfg(trustzone)]
pub fn set_nonsecure_mask(&mut self, v: bool) {
self.write(NONSECURE_MASK, v as u32);
}
pub fn set_out(&mut self, v: PinState) {
self.write(OUT, v as u32);
}
#[inline(always)]
pub fn initialize<Mode: PinMode>(
&mut self,
_mode: Mode,
config: PinConfig,
security: security::SecurityAttribute,
) {
_ = security;
#[cfg(trustzone)]
self.set_nonsecure_mask(security.is_nonsecure());
self.set_mode(Mode::DRIVE_MODE, Mode::INPUT_BUFFER);
self.set_io_sel(Mode::IO_SELECTOR);
self.set_threshold(config.threshold);
self.set_slew_rate(config.slew_rate);
self.set_drive_strength(config.drive_strength);
self.set_interrupt_edge(config.interrupt_edge);
self.set_interrupt_mask(config.interrupt_enabled);
self.set_out(config.initial_output);
}
}