//! ARM GICv2 CPU registers
use libk_mm::device::DeviceMemoryIo;
use tock_registers::{
    interfaces::{Readable, Writeable},
    register_bitfields, register_structs,
    registers::ReadWrite,
};

register_bitfields! {
    u32,
    CTLR [
        EnableGrp0 OFFSET(0) NUMBITS(1) [],
        EnableGrp1 OFFSET(1) NUMBITS(1) [],
    ],
    PMR [
        Priority OFFSET(0) NUMBITS(8) []
    ],
    IAR [
        InterruptID OFFSET(0) NUMBITS(10) []
    ],
    EOIR [
        EOINTID OFFSET(0) NUMBITS(10) []
    ]
}

register_structs! {
    #[allow(non_snake_case)]
    pub(super) GiccRegs {
        (0x00 => CTLR: ReadWrite<u32, CTLR::Register>),
        (0x04 => PMR: ReadWrite<u32, PMR::Register>),
        (0x08 => _0),
        (0x0C => IAR: ReadWrite<u32, IAR::Register>),
        (0x10 => EOIR: ReadWrite<u32, EOIR::Register>),
        (0x14 => @END),
    }
}

pub(super) struct Gicc {
    regs: DeviceMemoryIo<'static, GiccRegs>,
}

impl Gicc {
    pub const fn new(regs: DeviceMemoryIo<'static, GiccRegs>) -> Self {
        Self { regs }
    }

    pub unsafe fn init(&self) {
        log::debug!("Enabling GICv2 GICC");
        self.regs
            .CTLR
            .write(CTLR::EnableGrp0::SET + CTLR::EnableGrp1::SET);
        self.regs.PMR.write(PMR::Priority.val(0xFF));
    }

    pub fn pending_irq_number(&self) -> usize {
        self.regs.IAR.read(IAR::InterruptID) as usize
    }

    pub fn clear_irq(&self, irq: usize) {
        self.regs.EOIR.write(EOIR::EOINTID.val(irq as u32));
    }
}
