// SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2022 Andre Richter //! Local Interrupt Controller Driver. //! //! # Resources //! //! - use super::{LocalIRQ, PendingIRQs}; use crate::{ bsp::device_driver::common::MMIODerefWrapper, exception, memory::{Address, Virtual}, synchronization, synchronization::{IRQSafeNullLock, InitStateLock}, }; use alloc::vec::Vec; use tock_registers::{ interfaces::{Readable, Writeable}, register_structs, registers::{ReadOnly, WriteOnly}, }; //-------------------------------------------------------------------------------------------------- // Private Definitions //-------------------------------------------------------------------------------------------------- register_structs! { #[allow(non_snake_case)] WORegisterBlock { (0x00 => _reserved1), (0x40 => CORE0_TIMER_INTERRUPT_CONTROL: WriteOnly), (0x44 => @END), } } register_structs! { #[allow(non_snake_case)] RORegisterBlock { (0x00 => _reserved1), (0x60 => CORE0_INTERRUPT_SOURCE: ReadOnly), (0x64 => @END), } } /// Abstraction for the WriteOnly parts of the associated MMIO registers. type WriteOnlyRegisters = MMIODerefWrapper; /// Abstraction for the ReadOnly parts of the associated MMIO registers. type ReadOnlyRegisters = MMIODerefWrapper; type HandlerTable = Vec>>; //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- /// Representation of the peripheral interrupt controller. pub struct LocalIC { /// Access to write registers is guarded with a lock. wo_registers: IRQSafeNullLock, /// Register read access is unguarded. ro_registers: ReadOnlyRegisters, /// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards. handler_table: InitStateLock, } //-------------------------------------------------------------------------------------------------- // Public Code //-------------------------------------------------------------------------------------------------- impl LocalIC { // See datasheet. const PERIPH_IRQ_MASK: u32 = (1 << 8); /// Create an instance. /// /// # Safety /// /// - The user must ensure to provide a correct MMIO start address. pub const unsafe fn new(mmio_start_addr: Address) -> Self { Self { wo_registers: IRQSafeNullLock::new(WriteOnlyRegisters::new(mmio_start_addr)), ro_registers: ReadOnlyRegisters::new(mmio_start_addr), handler_table: InitStateLock::new(Vec::new()), } } /// Called by the kernel to bring up the device. pub fn init(&self) { self.handler_table .write(|table| table.resize(LocalIRQ::MAX_INCLUSIVE + 1, None)); } /// Query the list of pending IRQs. fn pending_irqs(&self) -> PendingIRQs { // Ignore the indicator bit for a peripheral IRQ. PendingIRQs::new( (self.ro_registers.CORE0_INTERRUPT_SOURCE.get() & !Self::PERIPH_IRQ_MASK).into(), ) } } //------------------------------------------------------------------------------ // OS Interface Code //------------------------------------------------------------------------------ use synchronization::interface::{Mutex, ReadWriteEx}; impl exception::asynchronous::interface::IRQManager for LocalIC { type IRQNumberType = LocalIRQ; fn register_handler( &self, irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor, ) -> Result<(), &'static str> { self.handler_table.write(|table| { let irq_number = irq_handler_descriptor.number().get(); if table[irq_number].is_some() { return Err("IRQ handler already registered"); } table[irq_number] = Some(irq_handler_descriptor); Ok(()) }) } fn enable(&self, irq: &Self::IRQNumberType) { self.wo_registers.lock(|regs| { let enable_bit: u32 = 1 << (irq.get()); // Writing a 1 to a bit will set the corresponding IRQ enable bit. All other IRQ enable // bits are unaffected. So we don't need read and OR'ing here. regs.CORE0_TIMER_INTERRUPT_CONTROL.set(enable_bit); }); } fn handle_pending_irqs<'irq_context>( &'irq_context self, _ic: &exception::asynchronous::IRQContext<'irq_context>, ) { self.handler_table.read(|table| { for irq_number in self.pending_irqs() { match table[irq_number] { None => panic!("No handler registered for IRQ {}", irq_number), Some(descriptor) => { // Call the IRQ handler. Panics on failure. descriptor.handler().handle().expect("Error handling IRQ"); } } } }) } fn print_handler(&self) { use crate::info; info!(" Local handler:"); self.handler_table.read(|table| { for (i, opt) in table.iter().enumerate() { if let Some(handler) = opt { info!(" {: >3}. {}", i, handler.name()); } } }); } }