//! ARM GICv2 Distributor registers use device_api::interrupt::{IpiDeliveryTarget, IrqLevel, IrqOptions, IrqTrigger}; use libk_mm::device::DeviceMemoryIo; use spinning_top::Spinlock; use tock_registers::{ interfaces::{ReadWriteable, Readable, Writeable}, register_bitfields, register_structs, registers::{ReadOnly, ReadWrite, WriteOnly}, }; register_bitfields! { u32, CTLR [ Enable OFFSET(0) NUMBITS(1) [] ], TYPER [ ITLinesNumber OFFSET(0) NUMBITS(5) [] ], ITARGETSR [ Offset3 OFFSET(24) NUMBITS(8) [], Offset2 OFFSET(16) NUMBITS(8) [], Offset1 OFFSET(8) NUMBITS(8) [], Offset0 OFFSET(0) NUMBITS(8) [] ], SGIR [ TargetListFilter OFFSET(24) NUMBITS(2) [ SpecifiedOnly = 0, AllExceptLocal = 1, LocalOnly = 2, ], CPUTargetList OFFSET(16) NUMBITS(8) [], INTID OFFSET(0) NUMBITS(4) [] ], } register_structs! { #[allow(non_snake_case)] pub(super) GicdSharedRegs { (0x000 => CTLR: ReadWrite), (0x004 => TYPER: ReadWrite), (0x008 => _0), (0x104 => ISENABLER: [ReadWrite; 31]), (0x180 => _1), (0x820 => ITARGETSR: [ReadWrite; 248]), (0xC00 => _2), (0xC08 => ICFGR: [ReadWrite; 62]), (0xD00 => _3), (0xF00 => SGIR: WriteOnly), (0xF04 => @END), } } register_structs! { #[allow(non_snake_case)] pub(super) GicdBankedRegs { (0x000 => _0), (0x100 => ISENABLER: ReadWrite), (0x104 => _1), (0x800 => ITARGETSR: [ReadOnly; 8]), (0x820 => _2), (0xC00 => ICFGR: [ReadWrite; 2]), (0xC08 => @END), } } pub(super) struct Gicd { shared_regs: Spinlock>, banked_regs: DeviceMemoryIo<'static, GicdBankedRegs>, } impl GicdSharedRegs { #[inline(always)] fn num_irqs(&self) -> usize { ((self.TYPER.read(TYPER::ITLinesNumber) as usize) + 1) * 32 } #[inline(always)] fn itargets_slice(&self) -> &[ReadWrite] { assert!(self.num_irqs() >= 36); let itargetsr_max_index = ((self.num_irqs() - 32) >> 2) - 1; &self.ITARGETSR[0..itargetsr_max_index] } } impl Gicd { pub const fn new( shared_regs: DeviceMemoryIo<'static, GicdSharedRegs>, banked_regs: DeviceMemoryIo<'static, GicdBankedRegs>, ) -> Self { let shared_regs = Spinlock::new(shared_regs); Self { shared_regs, banked_regs, } } #[allow(dead_code)] pub unsafe fn set_sgir(&self, target: IpiDeliveryTarget, interrupt_id: u64) { assert_eq!(interrupt_id & !0xF, 0); let value = match target { IpiDeliveryTarget::OtherCpus => SGIR::TargetListFilter::AllExceptLocal, IpiDeliveryTarget::Specific(mask) => { assert_eq!(mask & !0xFFFF, 0); SGIR::TargetListFilter::SpecifiedOnly + SGIR::CPUTargetList.val(mask as _) } IpiDeliveryTarget::ThisCpu => SGIR::TargetListFilter::LocalOnly, } + SGIR::INTID.val(interrupt_id as u32); self.shared_regs.lock().SGIR.write(value); } fn local_gic_target_mask(&self) -> u32 { self.banked_regs.ITARGETSR[0].read(ITARGETSR::Offset0) } pub fn configure_irq(&self, irq: usize, options: IrqOptions) { // 2 bits per IRQ, 16 entries per register let reg = irq / 16; let shift = (irq % 16) * 2; let cfgr_value = match (options.trigger, options.level) { (IrqTrigger::Level, IrqLevel::ActiveLow) => 0, (IrqTrigger::Level, _) => 0, (_, IrqLevel::ActiveLow) => 1, (_, _) => 1, }; match reg { // Private IRQs 0..=1 => { todo!(); } // Shared IRQs _ => { let regs = self.shared_regs.lock(); let reg = ®s.ICFGR[reg - 2]; let v = reg.get() & !(0x3 << shift); reg.set(v | (cfgr_value << shift)); } } } pub fn enable_irq(&self, irq: usize) { let reg = irq >> 5; let bit = 1u32 << (irq & 0x1F); match reg { // Private IRQs 0 => { let reg = &self.banked_regs.ISENABLER; reg.set(reg.get() | bit); } // Shared IRQs _ => { let regs = self.shared_regs.lock(); let reg = ®s.ISENABLER[reg - 1]; reg.set(reg.get() | bit); } } } pub unsafe fn init(&self) { let mask = self.local_gic_target_mask(); let regs = self.shared_regs.lock(); debugln!("Enabling GICv2 GICD, max IRQ number: {}", regs.num_irqs()); regs.CTLR.modify(CTLR::Enable::SET); for reg in regs.itargets_slice().iter() { // Redirect all IRQs to cpu0 (this CPU) reg.write( ITARGETSR::Offset0.val(mask) + ITARGETSR::Offset1.val(mask) + ITARGETSR::Offset2.val(mask) + ITARGETSR::Offset3.val(mask), ); } } }