Skip to content

Commit a34720d

Browse files
committed
feat(irq): Implement BCM2712 Interrupt Controller and map UART IRQ
1 parent 610381e commit a34720d

File tree

4 files changed

+209
-5
lines changed

4 files changed

+209
-5
lines changed

19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
//
33
// Copyright (c) 2020-2023 Andre Richter <andre.o.richter@gmail.com>
4+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
45

56
//! GICv2 Driver - ARM Generic Interrupt Controller v2.
67
//!
@@ -104,13 +105,13 @@ pub type IRQNumber = BoundedUsize<{ GICv2::MAX_IRQ_NUMBER }>;
104105
/// Representation of the GIC.
105106
pub struct GICv2 {
106107
/// The Distributor.
107-
gicd: gicd::GICD,
108+
pub(crate) gicd: gicd::GICD,
108109

109110
/// The CPU Interface.
110-
gicc: gicc::GICC,
111+
pub(crate) gicc: gicc::GICC,
111112

112113
/// Stores registered IRQ handlers. Writable only during kernel init. RO afterwards.
113-
handler_table: InitStateLock<HandlerTable>,
114+
pub(crate) handler_table: InitStateLock<HandlerTable>,
114115
}
115116

116117
//--------------------------------------------------------------------------------------------------
@@ -137,6 +138,12 @@ impl GICv2 {
137138
handler_table: InitStateLock::new(Vec::new()),
138139
}
139140
}
141+
142+
/// Set the trigger type for an interrupt (Edge or Level).
143+
#[cfg(feature = "bsp_rpi5")]
144+
pub fn set_trigger(&self, irq_number: &IRQNumber, edge: bool) {
145+
self.gicd.set_trigger(irq_number, edge);
146+
}
140147
}
141148

142149
//------------------------------------------------------------------------------

19_kernel_heap/kernel/src/bsp/device_driver/arm/gicv2/gicd.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
//
33
// Copyright (c) 2020-2023 Andre Richter <andre.o.richter@gmail.com>
4+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
45

56
//! GICD Driver - GIC Distributor.
67
//!
@@ -54,7 +55,8 @@ register_structs! {
5455
(0x104 => ISENABLER: [ReadWrite<u32>; 31]),
5556
(0x180 => _reserved2),
5657
(0x820 => ITARGETSR: [ReadWrite<u32, ITARGETSR::Register>; 248]),
57-
(0xC00 => @END),
58+
(0xC00 => ICFGR: [ReadWrite<u32>; 64]),
59+
(0xD00 => @END),
5860
}
5961
}
6062

@@ -65,7 +67,9 @@ register_structs! {
6567
(0x100 => ISENABLER: ReadWrite<u32>),
6668
(0x104 => _reserved2),
6769
(0x800 => ITARGETSR: [ReadOnly<u32, ITARGETSR::Register>; 8]),
68-
(0x820 => @END),
70+
(0x820 => _reserved3),
71+
(0xC00 => ICFGR: [ReadWrite<u32>; 2]), // Banked for PPIs
72+
(0xC08 => @END),
6973
}
7074
}
7175

@@ -198,4 +202,33 @@ impl GICD {
198202
}
199203
}
200204
}
205+
206+
/// Set the trigger type for an interrupt (Edge or Level).
207+
#[cfg(feature = "bsp_rpi5")]
208+
pub fn set_trigger(&self, irq_num: &super::IRQNumber, edge: bool) {
209+
let irq_num = irq_num.get();
210+
// Each register holds 16 IRQs (2 bits per IRQ).
211+
let reg_index = irq_num >> 4;
212+
let bit_shift = (irq_num % 16) * 2;
213+
let config_val = if edge { 0b10 } else { 0b00 }; // 10=Edge, 00=Level
214+
215+
match irq_num {
216+
0..=31 => {
217+
let reg = &self.banked_registers.ICFGR[reg_index];
218+
let mut val = reg.get();
219+
val &= !(0b11 << bit_shift);
220+
val |= config_val << bit_shift;
221+
reg.set(val);
222+
}
223+
_ => {
224+
self.shared_registers.lock(|regs| {
225+
let reg = &regs.ICFGR[reg_index];
226+
let mut val = reg.get();
227+
val &= !(0b11 << bit_shift);
228+
val |= config_val << bit_shift;
229+
reg.set(val);
230+
});
231+
}
232+
}
233+
}
201234
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
//
3+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
4+
5+
//! BCM2712 Interrupt Controller (MIP + GICv2).
6+
7+
use crate::{
8+
bsp::device_driver::{arm::gicv2::GICv2, common::MMIODerefWrapper},
9+
driver, exception,
10+
memory::{Address, Virtual},
11+
synchronization::interface::ReadWriteEx,
12+
};
13+
use tock_registers::{
14+
interfaces::Writeable,
15+
register_structs,
16+
registers::{ReadOnly, ReadWrite},
17+
};
18+
19+
register_structs! {
20+
#[allow(non_snake_case)]
21+
pub MipRegs {
22+
(0x00 => pub MIP_STATUS: ReadOnly<u32>),
23+
(0x04 => _reserved0),
24+
(0x20 => pub INT_CFGL_HOST: ReadWrite<u32>),
25+
(0x24 => _reserved1),
26+
(0x30 => pub INT_CFGH_HOST: ReadWrite<u32>),
27+
(0x34 => _reserved2),
28+
(0x40 => pub INT_MASKL_HOST: ReadWrite<u32>),
29+
(0x44 => _reserved3),
30+
(0x50 => pub INT_MASKH_HOST: ReadWrite<u32>),
31+
(0x54 => _reserved4),
32+
(0x60 => pub INT_MASKL_VPU: ReadWrite<u32>),
33+
(0x64 => _reserved5),
34+
(0x70 => pub INT_MASKH_VPU: ReadWrite<u32>),
35+
(0x74 => @END),
36+
}
37+
}
38+
39+
const RP1_APB_OFFSET: usize = 0x8000;
40+
const UART0_VECTOR: usize = 25;
41+
42+
pub struct BCM2712InterruptController {
43+
gic: GICv2,
44+
mip: MMIODerefWrapper<MipRegs>,
45+
rp1_base: Address<Virtual>,
46+
}
47+
48+
impl BCM2712InterruptController {
49+
pub const COMPATIBLE: &'static str = "BCM2712 IntC";
50+
51+
pub const unsafe fn new(
52+
gicd_base: Address<Virtual>,
53+
gicc_base: Address<Virtual>,
54+
mip_base: Address<Virtual>,
55+
rp1_base: Address<Virtual>,
56+
) -> Self {
57+
Self {
58+
gic: GICv2::new(gicd_base, gicc_base),
59+
mip: MMIODerefWrapper::new(mip_base),
60+
rp1_base,
61+
}
62+
}
63+
64+
unsafe fn rearm_rp1_uart(&self) {
65+
let apb_base = (self.rp1_base + RP1_APB_OFFSET).as_usize() as *mut u32;
66+
// Re-arm register is at APB_BASE + 0x8 + (Vector * 4)
67+
let ctrl_reg = apb_base.add(2).add(UART0_VECTOR); // 2 = 0x8/4
68+
69+
// Write 0xD to re-arm/acknowledge
70+
core::ptr::write_volatile(ctrl_reg, 0xD);
71+
}
72+
}
73+
74+
impl driver::interface::DeviceDriver for BCM2712InterruptController {
75+
type IRQNumberType = exception::asynchronous::IRQNumber;
76+
77+
fn compatible(&self) -> &'static str {
78+
Self::COMPATIBLE
79+
}
80+
81+
unsafe fn init(&self) -> Result<(), &'static str> {
82+
// 1. Mask VPU interrupts (Crucial to prevent VPU from stealing IRQs)
83+
self.mip.INT_MASKL_VPU.set(0xFFFFFFFF);
84+
self.mip.INT_MASKH_VPU.set(0xFFFFFFFF);
85+
86+
// 2. Configure Host for Edge Trigger (Active High) to match RP1 signaling
87+
self.mip.INT_CFGL_HOST.set(0xFFFFFFFF);
88+
self.mip.INT_CFGH_HOST.set(0xFFFFFFFF);
89+
90+
// 3. Unmask Host interrupts
91+
self.mip.INT_MASKL_HOST.set(0);
92+
self.mip.INT_MASKH_HOST.set(0);
93+
94+
// 4. Init GIC
95+
self.gic.init()
96+
}
97+
}
98+
99+
impl exception::asynchronous::interface::IRQManager for BCM2712InterruptController {
100+
type IRQNumberType = exception::asynchronous::IRQNumber;
101+
102+
fn register_handler(
103+
&self,
104+
descriptor: exception::asynchronous::IRQHandlerDescriptor<Self::IRQNumberType>,
105+
) -> Result<(), &'static str> {
106+
let irq_num = descriptor.number().get();
107+
108+
// MIP Input 0 maps to GIC SPI 128 (ID 160).
109+
// It requires Edge Triggering.
110+
if irq_num == 160 {
111+
self.gic.set_trigger(&descriptor.number(), true);
112+
}
113+
114+
self.gic.register_handler(descriptor)
115+
}
116+
117+
fn enable(&self, irq: &Self::IRQNumberType) {
118+
self.gic.enable(irq)
119+
}
120+
121+
fn handle_pending_irqs<'irq_context>(
122+
&'irq_context self,
123+
ic: &exception::asynchronous::IRQContext<'irq_context>,
124+
) {
125+
// Custom handling to support RP1 Re-arm
126+
127+
// 1. Read IAR
128+
let irq_number = self.gic.gicc.pending_irq_number(ic);
129+
130+
// 2. Dispatch
131+
if irq_number <= 1019 {
132+
self.gic
133+
.handler_table
134+
.read(|table| match table[irq_number] {
135+
None => panic!("No handler registered for IRQ {}", irq_number),
136+
Some(descriptor) => {
137+
descriptor.handler().handle().expect("Error handling IRQ");
138+
}
139+
});
140+
141+
// 3. Re-arm RP1 if UART (ID 160)
142+
if irq_number == 160 {
143+
unsafe {
144+
self.rearm_rp1_uart();
145+
}
146+
}
147+
}
148+
149+
// 4. EOI
150+
self.gic.gicc.mark_comleted(irq_number as u32, ic);
151+
}
152+
153+
fn print_handler(&self) {
154+
self.gic.print_handler()
155+
}
156+
}

19_kernel_heap/kernel/src/bsp/raspberrypi/exception/asynchronous.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
//
33
// Copyright (c) 2020-2023 Andre Richter <andre.o.richter@gmail.com>
4+
// Copyright (c) 2026 Devansh Lodha <devanshlodha12@gmail.com>
45

56
//! BSP asynchronous exception handling.
67
@@ -26,3 +27,10 @@ pub(in crate::bsp) mod irq_map {
2627

2728
pub const PL011_UART: IRQNumber = IRQNumber::new(153);
2829
}
30+
31+
#[cfg(feature = "bsp_rpi5")]
32+
pub(in crate::bsp) mod irq_map {
33+
use super::bsp::device_driver::IRQNumber;
34+
35+
pub const PL011_UART: IRQNumber = IRQNumber::new(160);
36+
}

0 commit comments

Comments
 (0)