blob: 84abce70f76993199b1b60135a3b2b220b36cc5a [file] [log] [blame]
//! Platform Level Interrupt Control peripheral driver.
use reg_constants::plic::*;
use kernel::common::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
use kernel::common::StaticRef;
pub const PLIC_ADDR: u32 = 0x4800_0000;
pub const PLIC_EN0: u32 = PLIC_ADDR + RV_PLIC_IE0_0_REG_OFFSET as u32;
pub const PLIC_CCCR0: u32 = PLIC_ADDR + RV_PLIC_CC0_REG_OFFSET as u32;
register_structs! {
pub PlicRegisters {
/// Interrupt Priority Registers
(RV_PLIC_PRIO0_REG_OFFSET => priority: [ReadWrite<u32, priority::Register>; RV_PLIC_PARAM_NUM_SRC as usize]),
(RV_PLIC_PRIO0_REG_OFFSET + 4*(RV_PLIC_PARAM_NUM_SRC as usize) => _reserved0),
/// Interrupt Pending Register
(RV_PLIC_IP_0_REG_OFFSET => pending: [ReadOnly<u32>; RV_PLIC_IP_MULTIREG_COUNT as usize]),
(RV_PLIC_IP_0_REG_OFFSET + 4*(RV_PLIC_IP_MULTIREG_COUNT as usize) => _reserved1),
/// Interrupt Enable Register
(RV_PLIC_IE0_0_REG_OFFSET => enable: [ReadWrite<u32>; RV_PLIC_IE0_MULTIREG_COUNT as usize]),
(RV_PLIC_IE0_0_REG_OFFSET + 4*(RV_PLIC_IE0_MULTIREG_COUNT as usize) => _reserved2),
/// Priority Threshold Register
(RV_PLIC_THRESHOLD0_REG_OFFSET => threshold: ReadWrite<u32, priority::Register>),
/// Claim/Complete Register
(RV_PLIC_CC0_REG_OFFSET => claim: ReadWrite<u32>),
(RV_PLIC_CC0_REG_OFFSET + 4 => @END),
}
}
register_bitfields![u32,
priority [
Priority OFFSET(0) NUMBITS(3) []
]
];
const PLIC_BASE: StaticRef<PlicRegisters> =
unsafe { StaticRef::new(PLIC_ADDR as *const PlicRegisters) };
/// Clear all pending interrupts.
pub unsafe fn clear_all_pending() {
let _plic: &PlicRegisters = &*PLIC_BASE;
}
/// Enable all interrupts.
pub unsafe fn enable_all() {
let plic: &PlicRegisters = &*PLIC_BASE;
// TODO(aappleby): Enable all relevant interrupts.
// USB hardware on current OT master branch seems to have
// interrupt bugs: running Alarms causes persistent USB
// CONNECTED interrupts that can't be masked from USBDEV and
// cause the system to hang. So enable all interrupts except
// for the USB ones. Some open PRs on OT fix this, we'll re-enable
// USB interrurupts.
//
// https://github.com/lowRISC/opentitan/issues/3388
plic.enable[0].set(0xFFFF_FFFE);
plic.enable[1].set(0xFFFF_FFFF);
plic.enable[2].set(0xFFFF_0000); // USB are 64-79
// Set the max priority for each interrupt. This is not really used
// at this point.
let mut iter = plic.priority.iter();
iter.next(); // Interrupt source 0 can't be enabled.
for priority in iter {
priority.write(priority::Priority.val(3));
}
// Accept all interrupts.
plic.threshold.write(priority::Priority.val(1));
}
/// Disable all interrupts.
pub unsafe fn disable_all() {
let plic: &PlicRegisters = &*PLIC_BASE;
for enable in plic.enable.iter() {
enable.set(0);
}
}
/// Get the index (0-256) of the lowest number pending interrupt, or `None` if
/// none is pending. RISC-V PLIC has a "claim" register which makes it easy
/// to grab the highest priority pending interrupt.
pub unsafe fn next_pending() -> Option<u32> {
let plic: &PlicRegisters = &*PLIC_BASE;
let claim = plic.claim.get();
if claim == 0 {
None
} else {
Some(claim)
}
}
/// Signal that an interrupt is finished being handled. In Tock, this should be
/// called from the normal main loop (not the interrupt handler).
pub unsafe fn complete(index: u32) {
let plic: &PlicRegisters = &*PLIC_BASE;
plic.claim.set(index);
}
/// Return `true` if there are any pending interrupts in the PLIC, `false`
/// otherwise.
pub unsafe fn has_pending() -> bool {
let plic: &PlicRegisters = &*PLIC_BASE;
plic.pending.iter().fold(0, |i, pending| pending.get() | i) != 0
}