use crate::common::{Reg, RW};
use riscv::ExternalInterruptNumber;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct ENABLES {
ptr: *mut u32,
}
impl ENABLES {
#[inline]
pub(crate) const unsafe fn new(address: usize) -> Self {
Self { ptr: address as _ }
}
#[cfg(test)]
#[inline]
pub(crate) fn address(self) -> usize {
self.ptr as _
}
#[inline]
pub fn is_enabled<I: ExternalInterruptNumber>(self, source: I) -> bool {
let source = source.number();
let offset = (source / u32::BITS as usize) as _;
let reg: Reg<u32, RW> = unsafe { Reg::new(self.ptr.offset(offset)) };
reg.read_bit(source % u32::BITS as usize)
}
#[inline]
pub unsafe fn enable<I: ExternalInterruptNumber>(self, source: I) {
let source = source.number();
let offset = (source / u32::BITS as usize) as _;
let reg: Reg<u32, RW> = unsafe { Reg::new(self.ptr.offset(offset)) };
reg.set_bit(source % u32::BITS as usize);
}
#[cfg(target_has_atomic = "32")]
#[inline]
pub unsafe fn atomic_enable<I: ExternalInterruptNumber>(
self,
source: I,
order: core::sync::atomic::Ordering,
) {
let source = source.number();
let offset = (source / u32::BITS as usize) as _;
let reg: Reg<u32, RW> = unsafe { Reg::new(self.ptr.offset(offset)) };
reg.atomic_set_bit(source % u32::BITS as usize, order);
}
#[inline]
pub fn disable<I: ExternalInterruptNumber>(self, source: I) {
let source = source.number();
let offset = (source / u32::BITS as usize) as _;
let reg: Reg<u32, RW> = unsafe { Reg::new(self.ptr.offset(offset)) };
reg.clear_bit(source % u32::BITS as usize);
}
#[cfg(target_has_atomic = "32")]
#[inline]
pub unsafe fn atomic_disable<I: ExternalInterruptNumber>(
self,
source: I,
order: core::sync::atomic::Ordering,
) {
let source = source.number();
let offset = (source / u32::BITS as usize) as _;
let reg: Reg<u32, RW> = unsafe { Reg::new(self.ptr.offset(offset)) };
reg.atomic_clear_bit(source % u32::BITS as usize, order);
}
#[inline]
pub unsafe fn enable_all<I: ExternalInterruptNumber>(self) {
for offset in 0..=(I::MAX_INTERRUPT_NUMBER as u32 / u32::BITS) as isize {
let reg: Reg<u32, RW> = unsafe { Reg::new(self.ptr.offset(offset)) };
reg.write(0xFFFF_FFFF);
}
}
#[inline]
pub fn disable_all<I: ExternalInterruptNumber>(self) {
for offset in 0..=(I::MAX_INTERRUPT_NUMBER as u32 / u32::BITS) as _ {
let reg: Reg<u32, RW> = unsafe { Reg::new(self.ptr.offset(offset)) };
reg.write(0);
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test::Interrupt;
#[test]
fn test_enables() {
let mut raw_reg = [0u32; 32];
let enables = unsafe { ENABLES::new(raw_reg.as_mut_ptr() as _) };
for i in 0..255 {
if i & 0x2 != 0 {
unsafe { enables.enable(Interrupt::I1) };
} else {
enables.disable(Interrupt::I1);
}
if i & 0x4 != 0 {
unsafe { enables.enable(Interrupt::I2) };
} else {
enables.disable(Interrupt::I2);
}
if i & 0x8 != 0 {
unsafe { enables.enable(Interrupt::I3) };
} else {
enables.disable(Interrupt::I3);
}
if i & 0x10 != 0 {
unsafe { enables.enable(Interrupt::I4) };
} else {
enables.disable(Interrupt::I4);
}
assert_eq!(enables.is_enabled(Interrupt::I1), i & 0x2 != 0);
assert_eq!(enables.is_enabled(Interrupt::I2), i & 0x4 != 0);
assert_eq!(enables.is_enabled(Interrupt::I3), i & 0x8 != 0);
assert_eq!(enables.is_enabled(Interrupt::I4), i & 0x10 != 0);
enables.disable_all::<Interrupt>();
assert!(!enables.is_enabled(Interrupt::I1));
assert!(!enables.is_enabled(Interrupt::I2));
assert!(!enables.is_enabled(Interrupt::I3));
assert!(!enables.is_enabled(Interrupt::I4));
unsafe { enables.enable_all::<Interrupt>() };
assert!(enables.is_enabled(Interrupt::I1));
assert!(enables.is_enabled(Interrupt::I2));
assert!(enables.is_enabled(Interrupt::I3));
assert!(enables.is_enabled(Interrupt::I4));
}
}
#[cfg(target_has_atomic = "32")]
#[test]
fn test_atomic_enables() {
use core::sync::atomic::Ordering;
let mut raw_reg = [0u32; 32];
let enables = unsafe { ENABLES::new(raw_reg.as_mut_ptr() as _) };
for i in 0..255 {
if i & 0x2 != 0 {
unsafe { enables.atomic_enable(Interrupt::I1, Ordering::Relaxed) };
} else {
unsafe { enables.atomic_disable(Interrupt::I1, Ordering::Relaxed) };
}
if i & 0x4 != 0 {
unsafe { enables.atomic_enable(Interrupt::I2, Ordering::Relaxed) };
} else {
unsafe { enables.atomic_disable(Interrupt::I2, Ordering::Relaxed) };
}
if i & 0x8 != 0 {
unsafe { enables.atomic_enable(Interrupt::I3, Ordering::Relaxed) };
} else {
unsafe { enables.atomic_disable(Interrupt::I3, Ordering::Relaxed) };
}
if i & 0x10 != 0 {
unsafe { enables.atomic_enable(Interrupt::I4, Ordering::Relaxed) };
} else {
unsafe { enables.atomic_disable(Interrupt::I4, Ordering::Relaxed) };
}
assert_eq!(enables.is_enabled(Interrupt::I1), i & 0x2 != 0);
assert_eq!(enables.is_enabled(Interrupt::I2), i & 0x4 != 0);
assert_eq!(enables.is_enabled(Interrupt::I3), i & 0x8 != 0);
assert_eq!(enables.is_enabled(Interrupt::I4), i & 0x10 != 0);
}
}
}