//! The Arm GICv2 interrupt controller (QEMU virt's default). Two parts: //! //! - the **distributor** (GICD) decides which interrupts are on and where //! they go; //! - the **CPU interface** (GICC) is per CPU: `IAR` says which interrupt //! fired (and marks it "being handled"), `EOIR` says we're done. //! //! Interrupt numbers ("INTIDs"): 0-15 are software interrupts between //! CPUs, 16-31 are per-CPU ("PPIs", like each CPU's timer), 32 and up are //! shared devices ("SPIs"). 1020-1023 mean "nothing" (spurious). use super::board::IrqController; use super::mmio; use core::sync::atomic::{AtomicUsize, Ordering}; const GICD_CTLR: usize = 0x000; const GICD_ISENABLER: usize = 0x100; const GICD_ICENABLER: usize = 0x180; const GICD_IPRIORITYR: usize = 0x400; const GICC_CTLR: usize = 0x000; const GICC_PMR: usize = 0x004; const GICC_IAR: usize = 0x00c; const GICC_EOIR: usize = 0x010; pub struct GicV2 { dist: AtomicUsize, cpu: AtomicUsize, } impl GicV2 { pub const fn new(dist: usize, cpu: usize) -> Self { GicV2 { dist: AtomicUsize::new(dist), cpu: AtomicUsize::new(cpu) } } pub fn set_bases(&self, dist: usize, cpu: usize) { self.dist.store(dist, Ordering::Relaxed); self.cpu.store(cpu, Ordering::Relaxed); } pub fn bases(&self) -> (usize, usize) { (self.dist.load(Ordering::Relaxed), self.cpu.load(Ordering::Relaxed)) } } impl IrqController for GicV2 { fn init(&self) { let (d, c) = self.bases(); mmio::write32(d + GICD_CTLR, 0); for i in 0..32 { mmio::write32(d + GICD_ICENABLER + 4 * i, 0xffff_ffff); // everything off } mmio::write32(d + GICD_CTLR, 1); // distributor on mmio::write32(c + GICC_PMR, 0xf0); // let through every priority below 0xf0 mmio::write32(c + GICC_CTLR, 1); // CPU interface on } fn enable(&self, irq: u32) { let d = self.dist.load(Ordering::Relaxed); mmio::write8(d + GICD_IPRIORITYR + irq as usize, 0xa0); mmio::write32(d + GICD_ISENABLER + 4 * (irq as usize / 32), 1 << (irq % 32)); } fn ack(&self) -> Option { let irq = mmio::read32(self.cpu.load(Ordering::Relaxed) + GICC_IAR) & 0x3ff; if irq >= 1020 { None } else { Some(irq) } } fn eoi(&self, irq: u32) { mmio::write32(self.cpu.load(Ordering::Relaxed) + GICC_EOIR, irq); } }