pub mod registers;
pub use self::registers::Typer;
use self::registers::{Gicc, Gicd, GicdCtlr};
use crate::{IntId, InterruptGroup, Trigger, modify_bit};
use core::ptr::NonNull;
use safe_mmio::{UniqueMmioPointer, field, field_shared};
use thiserror::Error;
#[derive(Error, Debug, Clone, Copy, Eq, PartialEq)]
pub enum Error {
#[error("Cannot enable interrupt {0:?}")]
CannotEnable(IntId),
}
#[derive(Debug)]
pub struct GicV2<'a> {
gicd: UniqueMmioPointer<'a, Gicd>,
gicc: UniqueMmioPointer<'a, Gicc>,
}
impl GicV2<'_> {
pub unsafe fn new(gicd: *mut Gicd, gicc: *mut Gicc) -> Self {
unsafe {
Self {
gicd: UniqueMmioPointer::new(NonNull::new(gicd).unwrap()),
gicc: UniqueMmioPointer::new(NonNull::new(gicc).unwrap()),
}
}
}
pub fn typer(&self) -> Typer {
field_shared!(self.gicd, typer).read()
}
pub fn enable_group0(&mut self, enable: bool) {
self.modify_control(GicdCtlr::EnableGrp0, enable);
}
pub fn enable_group1(&mut self, enable: bool) {
self.modify_control(GicdCtlr::EnableGrp1, enable);
}
fn modify_control(&mut self, bits: GicdCtlr, enable: bool) {
field!(self.gicd, ctlr).modify_mut(|v| v.set(bits, enable));
}
pub fn setup(&mut self) {
if self.typer().has_security_extension() {
self.enable_group1(true);
for i in 0..32 {
field!(self.gicd, igroupr).get(i).unwrap().write(0xffffffff);
}
} else {
self.enable_group0(true);
for i in 0..32 {
field!(self.gicd, igroupr).get(i).unwrap().write(0x0);
}
}
field!(self.gicc, ctlr).write(0b1);
field!(self.gicc, pmr).write(0xff);
}
pub fn set_group(&mut self, intid: IntId, group: InterruptGroup) {
modify_bit(
field!(self.gicd, igroupr),
intid.0 as usize,
matches!(group, InterruptGroup::Group1),
);
}
pub fn enable_interrupt(&mut self, intid: IntId, enable: bool) -> Result<(), Error> {
let index = (intid.0 / 32) as usize;
let bit = 1 << (intid.0 % 32);
if enable {
field!(self.gicd, isenabler).get(index).unwrap().write(bit);
if (field_shared!(self.gicd, isenabler)
.get(index)
.unwrap()
.read()
& bit)
== 0
{
return Err(Error::CannotEnable(intid));
}
} else {
field!(self.gicd, icenabler).get(index).unwrap().write(bit);
}
Ok(())
}
pub fn enable_all_interrupts(&mut self, enable: bool) {
for i in 0..32 {
if enable {
field!(self.gicd, isenabler)
.get(i)
.unwrap()
.write(0xffffffff);
} else {
field!(self.gicd, icenabler)
.get(i)
.unwrap()
.write(0xffffffff);
}
}
}
pub fn set_priority_mask(&mut self, min_priority: u8) {
field!(self.gicc, pmr).write(min_priority as u32);
}
pub fn get_priority_mask(&mut self) -> u8 {
field!(self.gicc, pmr).read() as u8
}
pub fn set_interrupt_priority(&mut self, intid: IntId, priority: u8) {
let idx = intid.0 as usize / 4;
let priority = (priority as u32) << (8 * (intid.0 % 4));
field!(self.gicd, ipriorityr)
.get(idx)
.unwrap()
.write(priority);
}
pub fn set_trigger(&mut self, intid: IntId, trigger: Trigger) {
let index = (intid.0 / 16) as usize;
let bit = 1 << (((intid.0 % 16) * 2) + 1);
let mut icfgr = field!(self.gicd, icfgr);
let mut register = icfgr.get(index).unwrap();
let v = register.read();
register.write(match trigger {
Trigger::Edge => v | bit,
Trigger::Level => v & !bit,
});
}
pub fn send_sgi(&mut self, intid: IntId, target: SgiTarget) {
assert!(intid.is_sgi());
let sgi_value = match target {
SgiTarget::All => (intid.0 & 0x0f) | (0xff << 16),
SgiTarget::List {
target_list_filter,
target_list,
} => {
(intid.0 & 0xf)
| (match target_list_filter {
SgiTargetListFilter::CPUTargetList => 0b00,
SgiTargetListFilter::ForwardOthersOnly => 0b01,
SgiTargetListFilter::ForwardSelfOnly => 0b10,
} << 24)
| (u32::from(target_list & 0xff) << 16)
| (1u32 << 15)
}
};
field!(self.gicd, sgir).write(sgi_value);
}
pub fn get_and_acknowledge_interrupt(&mut self, group: InterruptGroup) -> Option<IntId> {
let intid = match group {
InterruptGroup::Group0 => IntId(field!(self.gicc, iar).read()),
InterruptGroup::Group1 => IntId(field!(self.gicc, aiar).read()),
};
if intid == IntId::SPECIAL_NONE {
None
} else {
Some(intid)
}
}
pub fn end_interrupt(&mut self, intid: IntId, group: InterruptGroup) {
match group {
InterruptGroup::Group0 => field!(self.gicc, eoir).write(intid.0),
InterruptGroup::Group1 => field!(self.gicc, aeoir).write(intid.0),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SgiTarget {
All,
List {
target_list_filter: SgiTargetListFilter,
target_list: u16,
},
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SgiTargetListFilter {
CPUTargetList,
ForwardOthersOnly,
ForwardSelfOnly,
}