blob: 59bf065ac7cc3a1fd39cd025919a05492f39d591 [file] [log] [blame]
//! Platform Level Interrupt Control peripheral driver.
use crate::plic_constants::*;
use kernel::common::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
use kernel::common::StaticRef;
pub const PLIC_EN0: u32 = 0x41010400;
pub const PLIC_CCCR0: u32 = 0x4101041C;
pub const PLIC_MSIP0: u32 = 0x41010420;
pub const PLIC_EN1: u32 = 0x41010500;
pub const PLIC_CCCR1: u32 = 0x4101051C;
pub const PLIC_MSIP1: u32 = 0x41010520;
register_structs! {
pub PlicRegisters {
/// Interrupt Pending Register
(RV_PLIC_IP_0_REG_OFFSET => pending: [ReadOnly<u32>; RV_PLIC_IP_MULTIREG_COUNT as usize]),
/// Interrupt Source Register
(RV_PLIC_LE_0_REG_OFFSET => source: [ReadWrite<u32>; RV_PLIC_LE_MULTIREG_COUNT as usize]),
/// Interrupt Priority Registers
(RV_PLIC_PRIO0_REG_OFFSET => priority: [ReadWrite<u32, priority::Register>; RV_PLIC_PARAM_NUM_SRC as usize]),
(0x30C => _reserved0: [ReadWrite<u32>; 61]),
/// Interrupt Enable Register
(RV_PLIC_IE0_0_REG_OFFSET => enable: [ReadWrite<u32>; RV_PLIC_IE0_MULTIREG_COUNT as usize]),
/// Priority Threshold Register
(RV_PLIC_THRESHOLD0_REG_OFFSET => threshold: ReadWrite<u32, priority::Register>),
/// Claim/Complete Register
(RV_PLIC_CC0_REG_OFFSET => claim: ReadWrite<u32>),
/// MSIP Register
(RV_PLIC_MSIP0_REG_OFFSET => msip: ReadWrite<u32>),
(RV_PLIC_MSIP0_REG_OFFSET + 4 => @END),
}
}
register_bitfields![u32,
priority [
Priority OFFSET(0) NUMBITS(3) []
]
];
const PLIC_BASE: StaticRef<PlicRegisters> =
unsafe { StaticRef::new(0x4101_0000 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;
// 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_FFFF);
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.
for priority in plic.priority.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
}