rv32i/
clic.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Core Local Interrupt Control peripheral driver.
6
7use kernel::utilities::registers::interfaces::{Readable, Writeable};
8use kernel::utilities::registers::{register_bitfields, ReadWrite};
9use kernel::utilities::StaticRef;
10
11/// CLIC Hart Specific Region
12#[repr(C)]
13struct ClicRegisters {
14    /// CLIC Interrupt Pending Registers
15    clicintip: IntPendRegisters,
16    /// CLIC Interrupt Enable Registers
17    clicintie: IntEnableRegisters,
18    /// CLIC Interrupt Configuration Registers
19    clicintcfg: IntConfigRegisters,
20    /// CLIC Configuration Registers
21    cliccfg: ConfigRegisters,
22}
23
24/// Interrupt Pending Registers
25#[repr(C)]
26struct IntPendRegisters {
27    _reserved0: [u8; 3],
28    /// Machine Software Interrupt
29    msip: ReadWrite<u8, intpend::Register>,
30    _reserved1: [u8; 3],
31    /// Machine Timer Interrupt
32    mtip: ReadWrite<u8, intpend::Register>,
33    _reserved2: [u8; 3],
34    /// Machine External Interrupt
35    meip: ReadWrite<u8, intpend::Register>,
36    /// CLIC Software Interrupt
37    csip: ReadWrite<u8, intpend::Register>,
38    _reserved3: [u8; 3],
39    /// Local Interrupt 0-127
40    localintpend: [ReadWrite<u8, intpend::Register>; 128],
41    _reserved4: [u8; 880],
42}
43
44/// Interrupt Enable Registers
45#[repr(C)]
46struct IntEnableRegisters {
47    _reserved0: [u8; 3],
48    /// Machine Software Interrupt
49    msip: ReadWrite<u8, inten::Register>,
50    _reserved1: [u8; 3],
51    /// Machine Timer Interrupt
52    mtip: ReadWrite<u8, inten::Register>,
53    _reserved2: [u8; 3],
54    /// Machine External Interrupt
55    meip: ReadWrite<u8, inten::Register>,
56    /// CLIC Software Interrupt
57    csip: ReadWrite<u8, inten::Register>,
58    _reserved3: [u8; 3],
59    /// Local Interrupt 0-127
60    localint: [ReadWrite<u8, inten::Register>; 128],
61    _reserved4: [u8; 880],
62}
63
64/// Interrupt Configuration Registers
65#[repr(C)]
66struct IntConfigRegisters {
67    _reserved0: [u8; 3],
68    /// Machine Software Interrupt
69    msip: ReadWrite<u8, intcon::Register>,
70    _reserved1: [u8; 3],
71    /// Machine Timer Interrupt
72    mtip: ReadWrite<u8, intcon::Register>,
73    _reserved2: [u8; 3],
74    /// Machine External Interrupt
75    meip: ReadWrite<u8, intcon::Register>,
76    /// CLIC Software Interrupt
77    csip: ReadWrite<u8, intcon::Register>,
78    _reserved3: [u8; 3],
79    /// Local Interrupt 0-127
80    localint: [ReadWrite<u8, intcon::Register>; 128],
81    _reserved4: [u8; 880],
82}
83
84/// Configuration Register
85#[repr(C)]
86struct ConfigRegisters {
87    cliccfg: ReadWrite<u8, conreg::Register>,
88}
89
90register_bitfields![u8,
91    intpend [
92        IntPend OFFSET(0) NUMBITS(1) []
93    ]
94];
95
96register_bitfields![u8,
97    inten [
98        IntEn OFFSET(0) NUMBITS(1) []
99    ]
100];
101
102// The data sheet isn't completely clear on this field, but it looks like there
103// are four bits for priority and level, and the lowest for bits of the register
104// are reserved.
105register_bitfields![u8,
106    intcon [
107        IntCon OFFSET(4) NUMBITS(4) []
108    ]
109];
110
111register_bitfields![u8,
112    conreg [
113        nvbits OFFSET(0) NUMBITS(1) [],
114        nlbits OFFSET(1) NUMBITS(4) [],
115        nmbits OFFSET(5) NUMBITS(2) []
116    ]
117];
118
119const CLIC_BASE: StaticRef<ClicRegisters> =
120    unsafe { StaticRef::new(0x0280_0000 as *const ClicRegisters) };
121
122pub struct Clic {
123    registers: StaticRef<ClicRegisters>,
124
125    /// A 32 bit long bit-vector of interrupts that are actually used on this
126    /// platform. This is needed because disabled interrupts can still set their
127    /// pending bits, but since they are disabled they do not actually cause the
128    /// trap handler to execute. Also, there are interrupts that can fire
129    /// without any way in software to disable them (for example with physical
130    /// switches wired directly to in-chip interrupt lines). This means that we
131    /// cannot rely on just using the pending interrupt bits to determine which
132    /// interrupts have fired. However, if we also keep track of which
133    /// interrupts this chip actually wants to use, then we can ignore the
134    /// pending bits that we cannot control and have no use for.
135    in_use_interrupts: u64,
136}
137
138impl Clic {
139    pub const fn new(in_use_interrupts: u64) -> Clic {
140        Clic {
141            registers: CLIC_BASE,
142            in_use_interrupts,
143        }
144    }
145
146    /// Clear all pending interrupts.
147    pub fn clear_all_pending(&self) {
148        self.registers.clicintip.msip.write(intpend::IntPend::CLEAR);
149        self.registers.clicintip.mtip.write(intpend::IntPend::CLEAR);
150        self.registers.clicintip.meip.write(intpend::IntPend::CLEAR);
151        self.registers.clicintip.csip.write(intpend::IntPend::CLEAR);
152
153        for pending in self.registers.clicintip.localintpend.iter() {
154            pending.write(intpend::IntPend::CLEAR);
155        }
156    }
157
158    /// Enable ONLY the interrupts we actually want to use.
159    ///
160    /// The CLIC allows disabled interrupts to still set the pending bit. Therefore
161    /// we have to be very careful about which interrupts we check.
162    pub fn enable_all(&self) {
163        if self.in_use_interrupts & (1 << 3) > 0 {
164            self.registers.clicintie.msip.write(inten::IntEn::SET);
165        } else if self.in_use_interrupts & (1 << 7) > 0 {
166            self.registers.clicintie.mtip.write(inten::IntEn::SET);
167        } else if self.in_use_interrupts & (1 << 11) > 0 {
168            self.registers.clicintie.meip.write(inten::IntEn::SET);
169        } else if self.in_use_interrupts & (1 << 12) > 0 {
170            self.registers.clicintie.csip.write(inten::IntEn::SET);
171        }
172
173        for (i, enable) in self.registers.clicintie.localint.iter().enumerate() {
174            if self.in_use_interrupts & (1 << (i + 16)) > 0 {
175                enable.write(inten::IntEn::SET);
176            }
177        }
178    }
179
180    // Disable any interrupt that has its pending bit set. Since the pending bit
181    // is how we check which interrupts need to be serviced, this just prevents
182    // the interrupt from re-firing until the kernel is able to service it.
183    pub fn disable_pending(&self) {
184        // Do all of the non-local interrupts.
185        if self.registers.clicintip.msip.is_set(intpend::IntPend) {
186            self.registers.clicintie.msip.write(inten::IntEn::CLEAR);
187        } else if self.registers.clicintip.mtip.is_set(intpend::IntPend) {
188            self.registers.clicintie.mtip.write(inten::IntEn::CLEAR);
189        } else if self.registers.clicintip.meip.is_set(intpend::IntPend) {
190            self.registers.clicintie.meip.write(inten::IntEn::CLEAR);
191        } else if self.registers.clicintip.csip.is_set(intpend::IntPend) {
192            self.registers.clicintie.csip.write(inten::IntEn::CLEAR);
193        }
194
195        // Iterate through all interrupts. If the interrupt is enabled and it
196        // is pending then disable the interrupt.
197        for (i, pending) in self.registers.clicintip.localintpend.iter().enumerate() {
198            if pending.is_set(intpend::IntPend)
199                && self.registers.clicintie.localint[i].is_set(inten::IntEn)
200            {
201                self.registers.clicintie.localint[i].write(inten::IntEn::CLEAR);
202            }
203        }
204    }
205
206    /// Disable all interrupts.
207    pub fn disable_all(&self) {
208        self.registers.clicintie.msip.write(inten::IntEn::CLEAR);
209        self.registers.clicintie.mtip.write(inten::IntEn::CLEAR);
210        self.registers.clicintie.meip.write(inten::IntEn::CLEAR);
211        self.registers.clicintie.csip.write(inten::IntEn::CLEAR);
212
213        for enable in self.registers.clicintie.localint.iter() {
214            enable.write(inten::IntEn::CLEAR);
215        }
216    }
217
218    /// Get the index (0-256) of the lowest number pending interrupt, or `None` if
219    /// none is pending.
220    pub fn next_pending(&self) -> Option<u32> {
221        if self.in_use_interrupts & (1 << 3) > 0
222            && self.registers.clicintip.msip.is_set(intpend::IntPend)
223        {
224            return Some(3);
225        } else if self.in_use_interrupts & (1 << 7) > 0
226            && self.registers.clicintip.mtip.is_set(intpend::IntPend)
227        {
228            return Some(7);
229        } else if self.in_use_interrupts & (1 << 11) > 0
230            && self.registers.clicintip.meip.is_set(intpend::IntPend)
231        {
232            return Some(11);
233        } else if self.in_use_interrupts & (1 << 12) > 0
234            && self.registers.clicintip.csip.is_set(intpend::IntPend)
235        {
236            return Some(12);
237        }
238
239        for (i, pending) in self.registers.clicintip.localintpend.iter().enumerate() {
240            if self.in_use_interrupts & (1 << (i + 16)) > 0 && pending.is_set(intpend::IntPend) {
241                return Some((i + 16) as u32);
242            }
243        }
244        None
245    }
246
247    /// Signal that an interrupt is finished being handled. In Tock, this should
248    /// be called from the normal main loop (not the interrupt handler). This
249    /// marks the interrupt as no longer pending and re-enables it.
250    pub fn complete(&self, index: u32) {
251        match index {
252            3 => {
253                self.registers.clicintip.msip.write(intpend::IntPend::CLEAR);
254                self.registers.clicintie.msip.write(inten::IntEn::SET);
255            }
256            7 => {
257                self.registers.clicintip.mtip.write(intpend::IntPend::CLEAR);
258                self.registers.clicintie.mtip.write(inten::IntEn::SET);
259            }
260            11 => {
261                self.registers.clicintip.meip.write(intpend::IntPend::CLEAR);
262                self.registers.clicintie.meip.write(inten::IntEn::SET);
263            }
264            12 => {
265                self.registers.clicintip.csip.write(intpend::IntPend::CLEAR);
266                self.registers.clicintie.csip.write(inten::IntEn::SET);
267            }
268            16..=144 => {
269                self.registers.clicintip.localintpend[(index as usize) - 16]
270                    .write(intpend::IntPend::CLEAR);
271                self.registers.clicintie.localint[(index as usize) - 16].write(inten::IntEn::SET);
272            }
273            _ => {}
274        }
275    }
276
277    /// Return `true` if there are any pending interrupts in the CLIC, `false`
278    /// otherwise.
279    pub fn has_pending(&self) -> bool {
280        self.next_pending().is_some()
281    }
282}
283
284/// Helper function to disable a specific interrupt.
285///
286/// This is outside of the `Clic` struct because it has to be called from the
287/// trap handler which does not have a reference to the CLIC object.
288pub unsafe fn disable_interrupt(index: u32) {
289    let regs: &ClicRegisters = &CLIC_BASE;
290
291    match index {
292        3 => regs.clicintie.msip.write(inten::IntEn::CLEAR),
293        7 => regs.clicintie.mtip.write(inten::IntEn::CLEAR),
294        11 => regs.clicintie.meip.write(inten::IntEn::CLEAR),
295        12 => regs.clicintie.csip.write(inten::IntEn::CLEAR),
296        16..=144 => regs.clicintie.localint[(index as usize) - 16].write(inten::IntEn::CLEAR),
297        _ => {}
298    }
299}