1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Cortex-M NVIC
//!
//! Most NVIC configuration is in the NVIC registers:
//! <https://developer.arm.com/docs/100165/0201/nested-vectored-interrupt-controller/nvic-programmers-model/table-of-nvic-registers>
//!
//! Also part of the NVIC conceptually is the ICTR, which in older versions of
//! the ARM ARM was listed in the "Summary of system control and ID registers
//! not in the SCB" and newer ARM ARMs just file it in its own little private
//! sub-section with the NVIC documentation. Seems a configuration register
//! without a home, so we include it in the NVIC files as it's conceptually here.
//! <https://developer.arm.com/docs/ddi0337/latest/nested-vectored-interrupt-controller/nvic-programmers-model/interrupt-controller-type-register-ictr>
use kernel::utilities::registers::interfaces::{Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, register_structs, ReadOnly, ReadWrite};
use kernel::utilities::StaticRef;
/// Generates the (u128, u128) tuple used for the NVIC's mask functions
/// `next_pending_with_mask` and `next_pending_with_mask`.
///
/// if let Some(interrupt) =
/// cortexm0p::nvic::next_pending_with_mask(interrupt_mask!(interrupts::SIO_IRQ_PROC1))
/// {
/// // ...
/// }
#[macro_export]
macro_rules! interrupt_mask {
($($interrupt: expr),+) => {{
let mut high_interrupt: u128 = 0;
let mut low_interrupt: u128 = 0;
$(
if ($interrupt < 128) {
low_interrupt |= (1 << $interrupt) as u128
}
else
{
high_interrupt |= (1 << ($interrupt-128)) as u128
}
);+
(high_interrupt, low_interrupt)
}};
}
register_structs! {
/// NVIC Registers.
///
/// Note this generic interface exposes all possible NVICs. Most cores will
/// not implement all NVIC_XXXX registers. If you need to find the number
/// of NVICs dynamically, consult `ICTR.INTLINESNUM`.
NvicRegisters {
(0x000 => _reserved0),
/// Interrupt Controller Type Register
(0x004 => ictr: ReadOnly<u32, InterruptControllerType::Register>),
(0x008 => _reserved1),
/// Interrupt Set-Enable Registers
(0x100 => iser: [ReadWrite<u32, NvicSetClear::Register>; 32]),
/// Interrupt Clear-Enable Registers
(0x180 => icer: [ReadWrite<u32, NvicSetClear::Register>; 32]),
/// Interrupt Set-Pending Registers
(0x200 => ispr: [ReadWrite<u32, NvicSetClear::Register>; 32]),
/// Interrupt Clear-Pending Registers
(0x280 => icpr: [ReadWrite<u32, NvicSetClear::Register>; 32]),
/// Interrupt Active Bit Registers
(0x300 => iabr: [ReadWrite<u32, NvicSetClear::Register>; 32]),
(0x380 => _reserved2),
/// Interrupt Priority Registers
(0x400 => ipr: [ReadWrite<u32, NvicInterruptPriority::Register>; 252]),
(0x7f0 => @END),
}
}
register_bitfields![u32,
InterruptControllerType [
/// Total number of interrupt lines in groups of 32
INTLINESNUM OFFSET(0) NUMBITS(4)
],
NvicSetClear [
/// For register NVIC_XXXXn, access interrupt (m+(32*n)).
/// - m takes the values from 31 to 0, except for NVIC_XXXX15, where:
/// - m takes the values from 15 to 0
/// - register bits[31:16] are reserved, RAZ/WI
BITS OFFSET(0) NUMBITS(32)
],
NvicInterruptPriority [
/// For register NVIC_IPRn, priority of interrupt number 4n+3.
PRI_N3 OFFSET(24) NUMBITS(8),
/// For register NVIC_IPRn, priority of interrupt number 4n+2.
PRI_N2 OFFSET(16) NUMBITS(8),
/// For register NVIC_IPRn, priority of interrupt number 4n+1.
PRI_N1 OFFSET(8) NUMBITS(8),
/// For register NVIC_IPRn, priority of interrupt number 4n.
PRI_N0 OFFSET(0) NUMBITS(8)
]
];
/// The NVIC peripheral in MMIO space.
const NVIC: StaticRef<NvicRegisters> =
unsafe { StaticRef::new(0xe000e000 as *const NvicRegisters) };
/// Number of valid NVIC_XXXX registers. Note this is a ceiling on the number
/// of available interrupts (as this is the number of banks of 32), but the
/// actual number may be less. See NVIC and ICTR documentation for more detail.
fn number_of_nvic_registers() -> usize {
(NVIC.ictr.read(InterruptControllerType::INTLINESNUM) + 1) as usize
}
/// Clear all pending interrupts
pub unsafe fn clear_all_pending() {
for icpr in NVIC.icpr.iter().take(number_of_nvic_registers()) {
icpr.set(!0)
}
}
/// Enable all interrupts
pub unsafe fn enable_all() {
for icer in NVIC.iser.iter().take(number_of_nvic_registers()) {
icer.set(!0)
}
}
/// Disable all interrupts
pub unsafe fn disable_all() {
for icer in NVIC.icer.iter().take(number_of_nvic_registers()) {
icer.set(!0)
}
}
/// Get the index (0-240) the lowest number pending interrupt, or `None` if none
/// are pending.
pub unsafe fn next_pending() -> Option<u32> {
for (block, ispr) in NVIC
.ispr
.iter()
.take(number_of_nvic_registers())
.enumerate()
{
let ispr = ispr.get();
// If there are any high bits there is a pending interrupt
if ispr != 0 {
// trailing_zeros == index of first high bit
let bit = ispr.trailing_zeros();
return Some(block as u32 * 32 + bit);
}
}
None
}
/// Get the index (0-240) the lowest number pending interrupt while ignoring the interrupts
/// that correspond to the bits set in mask, or `None` if none
/// are pending.
///
/// Mask is defined as two u128 fields,
/// mask.0 has the bits corresponding to interrupts from 128 to 240
/// mask.1 has the bits corresponding to interrupts from 0 to 127
pub unsafe fn next_pending_with_mask(mask: (u128, u128)) -> Option<u32> {
for (block, ispr) in NVIC
.ispr
.iter()
.take(number_of_nvic_registers())
.enumerate()
{
let interrupt_mask = if block < 4 { mask.1 } else { mask.0 };
let ispr_masked = ispr.get() & !((interrupt_mask >> (32 * block % 4)) as u32);
// If there are any high bits there is a pending interrupt
if ispr_masked != 0 {
// trailing_zeros == index of first high bit
let bit = ispr_masked.trailing_zeros();
return Some(block as u32 * 32 + bit);
}
}
None
}
pub unsafe fn has_pending() -> bool {
NVIC.ispr
.iter()
.take(number_of_nvic_registers())
.fold(0, |i, ispr| ispr.get() | i)
!= 0
}
/// Returns whether there are any pending interrupt bits set while ignoring
/// the indices that correspond to the bits set in mask
///
/// Mask is defined as two u128 fields,
/// mask.0 has the bits corresponding to interrupts from 128 to 240
/// mask.1 has the bits corresponding to interrupts from 0 to 127
pub unsafe fn has_pending_with_mask(mask: (u128, u128)) -> bool {
NVIC.ispr
.iter()
.take(number_of_nvic_registers())
.enumerate()
.fold(0, |i, (block, ispr)| {
let interrupt_mask = if block < 4 { mask.1 } else { mask.0 };
(ispr.get() & !((interrupt_mask >> (32 * block % 4)) as u32)) | i
})
!= 0
}
/// An opaque wrapper for a single NVIC interrupt.
///
/// Hand these out to low-level driver to let them control their own interrupts
/// but not others.
pub struct Nvic(u32);
impl Nvic {
/// Creates a new `Nvic`
///
/// Marked unsafe because only chip/platform configuration code should be
/// able to create these.
pub const unsafe fn new(idx: u32) -> Nvic {
Nvic(idx)
}
/// Enable the interrupt
pub fn enable(&self) {
let idx = self.0 as usize;
NVIC.iser[idx / 32].set(1 << (self.0 & 31));
}
/// Disable the interrupt
pub fn disable(&self) {
let idx = self.0 as usize;
NVIC.icer[idx / 32].set(1 << (self.0 & 31));
}
/// Clear pending state
pub fn clear_pending(&self) {
let idx = self.0 as usize;
NVIC.icpr[idx / 32].set(1 << (self.0 & 31));
}
}