| //! 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_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 |
| } |