You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
5.8 KiB
Rust
179 lines
5.8 KiB
Rust
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
//
|
|
// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
|
|
|
|
//! Peripheral Interrupt Controller Driver.
|
|
//!
|
|
//! # Resources
|
|
//!
|
|
//! - <https://github.com/raspberrypi/documentation/files/1888662/BCM2837-ARM-Peripherals.-.Revised.-.V2-1.pdf>
|
|
|
|
use super::{PendingIRQs, PeripheralIRQ};
|
|
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),
|
|
(0x10 => ENABLE_1: WriteOnly<u32>),
|
|
(0x14 => ENABLE_2: WriteOnly<u32>),
|
|
(0x18 => @END),
|
|
}
|
|
}
|
|
|
|
register_structs! {
|
|
#[allow(non_snake_case)]
|
|
RORegisterBlock {
|
|
(0x00 => _reserved1),
|
|
(0x04 => PENDING_1: ReadOnly<u32>),
|
|
(0x08 => PENDING_2: ReadOnly<u32>),
|
|
(0x0c => @END),
|
|
}
|
|
}
|
|
|
|
/// Abstraction for the WriteOnly parts of the associated MMIO registers.
|
|
type WriteOnlyRegisters = MMIODerefWrapper<WORegisterBlock>;
|
|
|
|
/// Abstraction for the ReadOnly parts of the associated MMIO registers.
|
|
type ReadOnlyRegisters = MMIODerefWrapper<RORegisterBlock>;
|
|
|
|
type HandlerTable = Vec<Option<exception::asynchronous::IRQHandlerDescriptor<PeripheralIRQ>>>;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Public Definitions
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
/// Representation of the peripheral interrupt controller.
|
|
pub struct PeripheralIC {
|
|
/// Access to write registers is guarded with a lock.
|
|
wo_registers: IRQSafeNullLock<WriteOnlyRegisters>,
|
|
|
|
/// Register read access is unguarded.
|
|
ro_registers: ReadOnlyRegisters,
|
|
|
|
/// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards.
|
|
handler_table: InitStateLock<HandlerTable>,
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Public Code
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
impl PeripheralIC {
|
|
/// Create an instance.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// - The user must ensure to provide a correct MMIO start address.
|
|
pub const unsafe fn new(mmio_start_addr: Address<Virtual>) -> 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(PeripheralIRQ::MAX_INCLUSIVE + 1, None));
|
|
}
|
|
|
|
/// Query the list of pending IRQs.
|
|
fn pending_irqs(&self) -> PendingIRQs {
|
|
let pending_mask: u64 = (u64::from(self.ro_registers.PENDING_2.get()) << 32)
|
|
| u64::from(self.ro_registers.PENDING_1.get());
|
|
|
|
PendingIRQs::new(pending_mask)
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// OS Interface Code
|
|
//------------------------------------------------------------------------------
|
|
use synchronization::interface::{Mutex, ReadWriteEx};
|
|
|
|
impl exception::asynchronous::interface::IRQManager for PeripheralIC {
|
|
type IRQNumberType = PeripheralIRQ;
|
|
|
|
fn register_handler(
|
|
&self,
|
|
irq_handler_descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
|
|
) -> 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_reg = if irq.get() <= 31 {
|
|
®s.ENABLE_1
|
|
} else {
|
|
®s.ENABLE_2
|
|
};
|
|
|
|
let enable_bit: u32 = 1 << (irq.get() % 32);
|
|
|
|
// 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.
|
|
enable_reg.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!(" Peripheral handler:");
|
|
|
|
self.handler_table.read(|table| {
|
|
for (i, opt) in table.iter().enumerate() {
|
|
if let Some(handler) = opt {
|
|
info!(" {: >3}. {}", i, handler.name());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|