blob: 5febb8d8e0017d1194e9fb6db0ca030323f61265 [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! AST10x0 SPI monitor (SPIPF) low-level register access.
//!
use ast1060_pac as device;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
/// SPI monitor controller identifier.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SpiMonitorController {
Spim0 = 0,
Spim1 = 1,
Spim2 = 2,
Spim3 = 3,
}
impl SpiMonitorController {
/// Returns the SPIPF register block base address for the controller.
#[must_use]
pub const fn base_address(self) -> usize {
match self {
Self::Spim0 => SPIPF1_BASE,
Self::Spim1 => SPIPF2_BASE,
Self::Spim2 => SPIPF3_BASE,
Self::Spim3 => SPIPF4_BASE,
}
}
/// Returns the PAC register block pointer for this controller.
#[must_use]
pub const fn ptr(self) -> *const device::spipf::RegisterBlock {
match self {
Self::Spim0 => device::Spipf::ptr(),
Self::Spim1 => device::Spipf1::ptr(),
Self::Spim2 => device::Spipf2::ptr(),
Self::Spim3 => device::Spipf3::ptr(),
}
}
}
/// SPIPF1 base address (used by SPIM0).
pub const SPIPF1_BASE: usize = 0x7E79_1000;
/// SPIPF2 base address (used by SPIM1).
pub const SPIPF2_BASE: usize = 0x7E79_2000;
/// SPIPF3 base address (used by SPIM2).
pub const SPIPF3_BASE: usize = 0x7E79_3000;
/// SPIPF4 base address (used by SPIM3).
pub const SPIPF4_BASE: usize = 0x7E79_4000;
/// Size of each SPIPF register block.
pub const SPIPF_REG_SIZE: usize = 0x1000;
/// Spin iterations for the `sw_reset` pulse hold. Targets ~5 us at 200 MHz;
/// exact timing is uncritical for the reset to latch.
const SW_RESET_SPIN_COUNT: u32 = 5 * 200;
/// Safe wrapper around a SPI monitor register block.
pub struct SpiMonitorRegisters {
base: *const device::spipf::RegisterBlock,
_not_sync: PhantomData<UnsafeCell<()>>, // Prevent Sync, allow Send.
}
impl SpiMonitorRegisters {
/// Create a register accessor from a raw register block pointer.
///
/// # Safety
/// Caller must ensure:
/// - `base` points to a valid SPIPF register block.
/// - Only one mutable owner accesses this hardware instance.
pub const unsafe fn new(base: *const device::spipf::RegisterBlock) -> Self {
Self {
base,
_not_sync: PhantomData,
}
}
/// Create a register accessor for a specific SPI monitor controller.
///
/// # Safety
/// Caller must ensure exclusive ownership of the selected controller.
pub const unsafe fn new_for_controller(controller: SpiMonitorController) -> Self {
// SAFETY: Caller upholds exclusive-access requirement.
unsafe { Self::new(controller.ptr()) }
}
#[inline]
fn regs(&self) -> &device::spipf::RegisterBlock {
// SAFETY: Constructor ensures pointer validity and ownership discipline.
unsafe { &*self.base }
}
/// SPIPF000: Common control register.
pub fn read_ctrl(&self) -> u32 {
self.regs().spipf000().read().bits()
}
pub fn write_ctrl(&self, value: u32) {
self.regs().spipf000().write(|w| unsafe { w.bits(value) });
}
pub fn modify_ctrl<F>(&self, f: F)
where
F: FnOnce(&mut u32),
{
self.regs().spipf000().modify(|r, w| {
let mut bits = r.bits();
f(&mut bits);
// SAFETY: Callback is responsible for valid register image.
unsafe { w.bits(bits) }
});
}
/// Enable or disable the monitor filter (SPIPF000 `enbl_filter_fn`, bit 2).
pub fn set_filter_enable(&self, enable: bool) {
self.regs()
.spipf000()
.modify(|_, w| w.enbl_filter_fn().bit(enable));
}
/// Enable or disable single-bit passthrough (SPIPF000
/// `enbl_single_bit_passthrough`, bit 0); passthrough bypasses the filter.
pub fn set_single_passthrough(&self, enable: bool) {
self.regs()
.spipf000()
.modify(|_, w| w.enbl_single_bit_passthrough().bit(enable));
}
/// Software-reset the monitor: pulse SPIPF000 `sweng_rst` (bit 15) high then
/// low. Clears status and logs while preserving the programmed policy.
/// `sweng_rst` is not self-clearing, so it is driven low explicitly after a
/// brief hold.
pub fn sw_reset(&self) {
self.regs()
.spipf000()
.modify(|_, w| w.sweng_rst().bit(true));
for _ in 0..SW_RESET_SPIN_COUNT {
core::hint::spin_loop();
}
self.regs()
.spipf000()
.modify(|_, w| w.sweng_rst().bit(false));
}
/// SPIPF004: Secondary control/config register.
pub fn read_ctrl2(&self) -> u32 {
self.regs().spipf004().read().bits()
}
pub fn write_ctrl2(&self, value: u32) {
self.regs().spipf004().write(|w| unsafe { w.bits(value) });
}
/// SPIPF07C: Lock/status register.
pub fn read_lock_status(&self) -> u32 {
self.regs().spipf07c().read().bits()
}
pub fn write_lock_status(&self, value: u32) {
self.regs().spipf07c().write(|w| unsafe { w.bits(value) });
}
/// SPIPFWT[n]: Allow-command table entry.
pub fn read_allow_cmd_slot(&self, index: usize) -> u32 {
self.regs().spipfwt(index).read().bits()
}
pub fn write_allow_cmd_slot(&self, index: usize, value: u32) {
self.regs()
.spipfwt(index)
.write(|w| unsafe { w.bits(value) });
}
/// SPIPFWA[n]: Address filter table entry.
pub fn read_addr_filter_slot(&self, index: usize) -> u32 {
self.regs().spipfwa(index).read().bits()
}
pub fn write_addr_filter_slot(&self, index: usize, value: u32) {
self.regs()
.spipfwa(index)
.write(|w| unsafe { w.bits(value) });
}
// -----------------------------------------------------------------------
// Violation log registers
//
// TODO: confirm SPIPF register offsets for log control from the AST10x0
// datasheet once available. Offsets below are placeholders consistent with
// known SPIPF register map patterns.
// -----------------------------------------------------------------------
/// Current violation log write index (number of entries written so far).
///
/// Maps to the SPIPF log index register (placeholder offset 0x080).
pub fn read_log_idx_reg(&self) -> u32 {
// SAFETY: raw offset read within the known SPIPF register block page.
unsafe {
let ptr = (self.base as *const u8).add(0x080) as *const u32;
core::ptr::read_volatile(ptr)
}
}
/// Maximum violation log capacity in bytes.
///
/// Maps to the SPIPF log size register (placeholder offset 0x084).
pub fn read_log_max_sz(&self) -> u32 {
// SAFETY: same as above.
unsafe {
let ptr = (self.base as *const u8).add(0x084) as *const u32;
core::ptr::read_volatile(ptr)
}
}
/// Base address of the violation log RAM region.
///
/// Returns a `usize` suitable for casting to `*const u32` by the caller.
/// Maps to the SPIPF log RAM address register (placeholder offset 0x088).
pub fn log_ram_base_addr(&self) -> usize {
// SAFETY: same as above.
unsafe {
let ptr = (self.base as *const u8).add(0x088) as *const u32;
core::ptr::read_volatile(ptr) as usize
}
}
}