#![no_std]
use core::num::NonZeroU32;
use core::ptr::NonNull;
use tock_registers::{
fields::Field,
interfaces::{ReadWriteable, Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite},
};
const SOURCE_NUM: usize = 1024;
const CONTEXT_NUM: usize = 15872;
const U32_BITS: usize = u32::BITS as usize;
register_structs! {
ContextLocal {
(0x0000 => priority_threshold: ReadWrite<u32>),
(0x0004 => interrupt_claim_complete: ReadWrite<u32>),
(0x0008 => _reserved_0),
(0x1000 => @END),
}
}
register_structs! {
pub PLICRegs {
(0x000000 => interrupt_priority: [ReadWrite<u32>; SOURCE_NUM]),
(0x001000 => interrupt_pending: [ReadOnly<u32>; SOURCE_NUM / U32_BITS]),
(0x001080 => _reserved_0),
(0x002000 => interrupt_enable: [[ReadWrite<u32>; SOURCE_NUM / U32_BITS]; CONTEXT_NUM]),
(0x1F2000 => _reserved_1),
(0x200000 => contexts: [ContextLocal; CONTEXT_NUM]),
(0x4000000 => @END),
}
}
pub struct Plic {
base: NonNull<PLICRegs>,
}
unsafe impl Send for Plic {}
unsafe impl Sync for Plic {}
impl Plic {
#[inline]
pub const unsafe fn new(base: NonNull<PLICRegs>) -> Self {
Self { base }
}
pub fn init_by_context(&mut self, ctx: usize) {
self.regs().contexts[ctx].priority_threshold.set(0);
}
const fn regs(&self) -> &PLICRegs {
unsafe { self.base.as_ref() }
}
#[inline]
pub fn set_priority(&mut self, source: NonZeroU32, value: u32) {
self.regs().interrupt_priority[source.get() as usize].set(value);
}
#[inline]
pub fn get_priority(&self, source: NonZeroU32) -> u32 {
self.regs().interrupt_priority[source.get() as usize].get()
}
#[inline]
pub fn probe_priority_bits(&mut self, source: NonZeroU32) -> u32 {
self.regs().interrupt_priority[source.get() as usize].set(!0);
self.regs().interrupt_priority[source.get() as usize].get()
}
#[inline]
pub fn is_pending(&self, source: NonZeroU32) -> bool {
let (group, field) = parse_group_and_field(source.get() as usize);
self.regs().interrupt_pending[group].read(field) != 0
}
#[inline]
pub fn enable(&mut self, source: NonZeroU32, ctx: usize) {
let (group, field) = parse_group_and_field(source.get() as usize);
self.regs().interrupt_enable[ctx][group].modify(field.val(1));
}
#[inline]
pub fn disable(&mut self, source: NonZeroU32, ctx: usize) {
let (group, field) = parse_group_and_field(source.get() as usize);
self.regs().interrupt_enable[ctx][group].modify(field.val(0));
}
#[inline]
pub fn is_enabled(&self, source: NonZeroU32, ctx: usize) -> bool {
let (group, field) = parse_group_and_field(source.get() as usize);
self.regs().interrupt_enable[ctx][group].read(field) != 0
}
#[inline]
pub fn get_threshold(&self, ctx: usize) -> u32 {
self.regs().contexts[ctx].priority_threshold.get()
}
#[inline]
pub fn set_threshold(&mut self, ctx: usize, value: u32) {
self.regs().contexts[ctx].priority_threshold.set(value);
}
#[inline]
pub fn probe_threshold_bits(&mut self, ctx: usize) -> u32 {
self.regs().contexts[ctx].priority_threshold.set(!0);
self.regs().contexts[ctx].priority_threshold.get()
}
#[inline]
pub fn claim(&mut self, ctx: usize) -> Option<NonZeroU32> {
NonZeroU32::new(self.regs().contexts[ctx].interrupt_claim_complete.get())
}
#[inline]
pub fn complete(&mut self, ctx: usize, source: NonZeroU32) {
self.regs().contexts[ctx]
.interrupt_claim_complete
.set(source.get());
}
}
fn parse_group_and_field(source: usize) -> (usize, Field<u32, ()>) {
let group = source / U32_BITS;
let index = source % U32_BITS;
let field = Field::<u32, ()>::new(0b1, index);
(group, field)
}