zynq7000_hal/gic.rs
1//! # Global Interrupt Controller (GIC) module
2//!
3//! The primary interface to configure and allow handling the interrupts are the
4//! [GicConfigurator] and the [GicInterruptHelper] structures.
5//!
6//! # Examples
7//!
8//! - [GTC ticks](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/examples/simple/src/bin/gtc-ticks.rs)
9use arbitrary_int::prelude::*;
10
11use cortex_ar::interrupt;
12use zynq7000::gic::{
13 DistributorControlRegister, GicCpuInterface, GicDistributor, InterfaceControl,
14 InterruptSignalRegister, MmioGicCpuInterface, MmioGicDistributor, PriorityRegister,
15};
16
17const SPURIOUS_INTERRUPT_ID: u32 = 1023;
18
19pub const HIGHEST_PRIORITY: u8 = 0;
20pub const LOWEST_PRIORITY: u8 = 31;
21
22/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
23/// Configures #32 to #47.
24pub const ICFR_2_FIXED_VALUE: u32 = 0b01010101010111010101010001011111;
25/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
26/// This configures `PL[2:0]` to high-level sensitivity.
27/// Configures #48 to #63.
28pub const ICFR_3_FIXED_VALUE: u32 = 0b01010101010101011101010101010101;
29/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
30/// This configures `PL[7:3]` to high-level sensitivity.
31/// Configures #64 to #79.
32pub const ICFR_4_FIXED_VALUE: u32 = 0b01110101010101010101010101010101;
33/// These fixed values must be programmed according to the Zynq7000 TRM p.236.
34/// This configures `PL[15:8]` to high-level sensitivity.
35/// Configures #80 to #95.
36pub const ICFR_5_FIXED_VALUE: u32 = 0b00000011010101010101010101010101;
37
38/// Helper value to target all interrupts which can be targetted to CPU 0
39pub const TARGETS_ALL_CPU_0_IPTR_VAL: u32 = 0x01010101;
40/// Helper value to target all interrupts which can be targetted to CPU 1
41pub const TARGETS_ALL_CPU_1_IPTR_VAL: u32 = 0x02020202;
42
43pub const ACTIVATE_ALL_SGIS_MASK_ISER: u32 = 0x0000_FFFF;
44pub const ACTIVATE_ALL_PPIS_MASK_ISER: u32 = 0xF800_0000;
45
46pub enum SpiSensitivity {
47 Level = 0b01,
48 Edge = 0b11,
49}
50
51pub enum TargetCpu {
52 None = 0b00,
53 Cpu0 = 0b01,
54 Cpu1 = 0b10,
55 Both = 0b11,
56}
57
58/// Private Peripheral Interrupt (PPI) which are private to the CPU.
59#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
60#[repr(u8)]
61pub enum PpiInterrupt {
62 GlobalTimer = 27,
63 // Interrupt signal from the PL. CPU0: `IRQF2P[18]` and CPU1: `IRQF2P[19]`
64 NFiq = 28,
65 CpuPrivateTimer = 29,
66 /// AWDT0 and AWDT1 for each CPU.
67 Awdt = 30,
68 // Interrupt signal from the PL. CPU0: `IRQF2P[16]` and CPU1: `IRQF2P[17]`
69 NIrq = 31,
70}
71
72/// Shared Peripheral Interrupt IDs.
73#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
74#[repr(u8)]
75pub enum SpiInterrupt {
76 Cpu0 = 32,
77 Cpu1 = 33,
78 L2Cache = 34,
79 Ocm = 35,
80 _Reserved0 = 36,
81 Pmu0 = 37,
82 Pmu1 = 38,
83 Xadc = 39,
84 DevC = 40,
85 Swdt = 41,
86 Ttc00 = 42,
87 Ttc01 = 43,
88 Ttc02 = 44,
89 DmacAbort = 45,
90 Dmac0 = 46,
91 Dmac1 = 47,
92 Dmac2 = 48,
93 Dmac3 = 49,
94 Smc = 50,
95 Qspi = 51,
96 Gpio = 52,
97 Usb0 = 53,
98 Eth0 = 54,
99 Eth0Wakeup = 55,
100 Sdio0 = 56,
101 I2c0 = 57,
102 Spi0 = 58,
103 Uart0 = 59,
104 Can0 = 60,
105 Pl0 = 61,
106 Pl1 = 62,
107 Pl2 = 63,
108 Pl3 = 64,
109 Pl4 = 65,
110 Pl5 = 66,
111 Pl6 = 67,
112 Pl7 = 68,
113 Ttc10 = 69,
114 Ttc11 = 70,
115 Ttc12 = 71,
116 Dmac4 = 72,
117 Dmac5 = 73,
118 Dmac6 = 74,
119 Dmac7 = 75,
120 Usb1 = 76,
121 Eth1 = 77,
122 Eth1Wakeup = 78,
123 Sdio1 = 79,
124 I2c1 = 80,
125 Spi1 = 81,
126 Uart1 = 82,
127 Can1 = 83,
128 Pl8 = 84,
129 Pl9 = 85,
130 Pl10 = 86,
131 Pl11 = 87,
132 Pl12 = 88,
133 Pl13 = 89,
134 Pl14 = 90,
135 Pl15 = 91,
136 ScuParity = 92,
137}
138
139/// Interrupt ID wrapper.
140#[derive(Debug, Clone, Copy)]
141pub enum Interrupt {
142 Sgi(usize),
143 Ppi(PpiInterrupt),
144 Spi(SpiInterrupt),
145 /// Detects an invalid interrupt ID.
146 Invalid(usize),
147 /// Spurious interrupt (ID# 1023).
148 Spurious,
149}
150
151#[derive(Debug)]
152pub struct InterruptInfo {
153 raw_reg: InterruptSignalRegister,
154 interrupt: Interrupt,
155 cpu_id: u8,
156}
157
158impl InterruptInfo {
159 pub fn raw_reg(&self) -> InterruptSignalRegister {
160 self.raw_reg
161 }
162
163 pub fn cpu_id(&self) -> u8 {
164 self.cpu_id
165 }
166
167 pub fn interrupt(&self) -> Interrupt {
168 self.interrupt
169 }
170}
171
172#[derive(Debug, thiserror::Error)]
173#[error("Invalid priority value {0}, range is [0, 31]")]
174pub struct InvalidPriorityValue(pub u8);
175
176#[derive(Debug, thiserror::Error)]
177#[error("Invalid PL interrupt ID {0}")]
178pub struct InvalidPlInterruptId(pub usize);
179
180/// Invalid Shared Peripheral Interrupt (SPI) ID.
181#[derive(Debug, thiserror::Error)]
182#[error("Invalid SPI interrupt ID {0}")]
183pub struct InvalidSpiInterruptId(pub usize);
184
185/// Invalid Software Generated Interrupt (SGI) ID.
186#[derive(Debug, thiserror::Error)]
187#[error("Invalid SGI interrupt ID {0}")]
188pub struct InvalidSgiInterruptId(pub usize);
189
190/// Higher-level GIC controller for the Zynq70000 SoC.
191///
192/// The flow of using this controller is as follows:
193///
194/// 1. Create the controller using [Self::new_with_init]. You can use the [zynq7000::Peripherals]
195/// structure or the [zynq7000::gic::GicCpuInterface::new_mmio] and [zynq7000::gic::GicDistributor::new_mmio]
196/// functions to retrieve the MMIO instances. The constructor configures all PL interrupts
197/// sensivities to high-level sensitivity and configures all sensitivities which are expected
198/// to have a certain value. It also sets the priority mask to 0xff by calling
199/// [Self::set_priority_mask] to prevent masking of the interrupts.
200/// 2. Perform the configuration of the interrupt targets and the interrupt sensitivities.
201/// The CPU targets are encoded with [TargetCpu] while the sensitivities are encoded by
202/// the [SpiSensitivity] enum. You can use the following (helper) API to configure the
203/// interrupts:
204///
205/// - [Self::set_spi_interrupt_cpu_target]
206/// - [Self::set_all_spi_interrupt_targets_cpu0]
207/// - [Self::set_pl_interrupt_sensitivity]
208///
209/// 3. Enable all required interrupts. The following API can be used for this:
210///
211/// - [Self::enable_sgi_interrupt]
212/// - [Self::enable_ppi_interrupt]
213/// - [Self::enable_spi_interrupt]
214/// - [Self::enable_all_spi_interrupts]
215/// - [Self::enable_all_ppi_interrupts]
216/// - [Self::enable_all_sgi_interrupts]
217/// - [Self::enable_all_interrupts]
218///
219/// You might also chose to enable these interrupts at run-time after the GIC was started.
220/// 4. Start the GIC by calling [Self::update_ctrl_regs] with the required settings or
221/// with [Self::enable] which assumes a certain configuration.
222/// 5. Enable interrupts for the Cortex-AR core by calling [Self::enable_interrupts].
223///
224/// For the handling of the interrupts, you can use the [GicInterruptHelper] which assumes a
225/// properly configured GIC.
226pub struct GicConfigurator {
227 pub gicc: MmioGicCpuInterface<'static>,
228 pub gicd: MmioGicDistributor<'static>,
229}
230
231impl GicConfigurator {
232 /// Create a new GIC controller instance and calls [Self::initialize] to perform
233 /// strongly recommended initialization routines for the GIC.
234 #[inline]
235 pub fn new_with_init(
236 gicc: MmioGicCpuInterface<'static>,
237 gicd: MmioGicDistributor<'static>,
238 ) -> Self {
239 let mut gic = GicConfigurator { gicc, gicd };
240 gic.initialize();
241 gic
242 }
243
244 /// Create a new GIC controller instance without performing any initialization routines.
245 ///
246 /// # Safety
247 ///
248 /// This creates the GIC without performing any of the initialization routines necessary
249 /// for proper operation. It also circumvents ownership checks. It is mainly intended to be
250 /// used inside the interrupt handler.
251 #[inline]
252 pub unsafe fn steal() -> Self {
253 GicConfigurator {
254 gicc: unsafe { GicCpuInterface::new_mmio_fixed() },
255 gicd: unsafe { GicDistributor::new_mmio_fixed() },
256 }
257 }
258
259 /// Sets up the GIC by configuring the required sensitivites for the shared peripheral
260 /// interrupts.
261 ///
262 /// With a few exeception, the GIC expects software to set up the sensitivities
263 /// to fixed values. The only exceptions are the interupts coming from the programmable
264 /// logic. These are configured to high level sensitivity by this function.
265 /// If you need a different sensitivity, you need to update the bits using the
266 /// [Self::set_pl_interrupt_sensitivity] function.
267 #[inline]
268 pub fn initialize(&mut self) {
269 self.gicd.write_icfr_2_spi(ICFR_2_FIXED_VALUE);
270 self.gicd.write_icfr_3_spi(ICFR_3_FIXED_VALUE);
271 self.gicd.write_icfr_4_spi(ICFR_4_FIXED_VALUE);
272 self.gicd.write_icfr_5_spi(ICFR_5_FIXED_VALUE);
273 self.set_priority_mask(0xff);
274 }
275
276 /// Set the priority mask for the CPU.
277 ///
278 /// Only interrupts with a higher priority than the mask will be accepted.
279 /// A lower numerical number means a higher priority. This means that the reset value 0x0
280 /// will mask all interrupts to the CPU while 0xff will unmask all interrupts.
281 ///
282 /// Please note that the highest priority mask will NOT be necessarily the number of priority
283 /// levels: The IPRn register always sets the priority level number to the upper bits of the
284 /// 8-bit bitfield. See p.83 of the ARM GICv1 architecture specification.
285 pub fn set_priority_mask(&mut self, mask: u8) {
286 self.gicc
287 .write_pmr(PriorityRegister::new_with_raw_value(mask as u32));
288 }
289
290 /// Set the sensitivity of a the Programmable Logic SPI interrupts.
291 ///
292 /// These are the only interrupt IDs which are configurable for SPI. They are set
293 /// to high-level sensitivity by default by the [Self::initialize] function. You can
294 /// use this method to override certain sensitivies.
295 #[inline]
296 pub fn set_pl_interrupt_sensitivity(
297 &mut self,
298 pl_int_id: usize,
299 sensitivity: SpiSensitivity,
300 ) -> Result<(), InvalidPlInterruptId> {
301 if pl_int_id >= 16 {
302 return Err(InvalidPlInterruptId(pl_int_id));
303 }
304 match pl_int_id {
305 0..=2 => {
306 let pos = 26 + (pl_int_id * 2);
307 let mask = 0b11 << pos;
308 self.gicd
309 .modify_icfr_3_spi(|v| (v & !mask) | ((sensitivity as u32) << pos));
310 }
311 3..=7 => {
312 let pos = pl_int_id * 2;
313 let mask = 0b11 << pos;
314 self.gicd
315 .modify_icfr_4_spi(|v| (v & !mask) | ((sensitivity as u32) << pos));
316 }
317 8..=15 => {
318 let pos = 8 + (pl_int_id * 2);
319 let mask = 0b11 << pos;
320 self.gicd
321 .modify_icfr_5_spi(|v| (v & !mask) | ((sensitivity as u32) << pos));
322 }
323 _ => unreachable!(),
324 }
325 Ok(())
326 }
327
328 /// Set the CPU target for a SPI interrupt.
329 ///
330 /// See [Self::set_all_spi_interrupt_targets_cpu0] for a utility method to handle all
331 /// interrupts with one core.
332 #[inline]
333 pub fn set_spi_interrupt_cpu_target(&mut self, spi_int: SpiInterrupt, target: TargetCpu) {
334 let spi_int_raw = spi_int as u32;
335 let spi_offset_to_0 = spi_int_raw as usize - 32;
336 // Unwrap okay, calculated index is always valid.
337 self.gicd
338 .write_iptr_spi(
339 spi_offset_to_0 / 4,
340 (target as u32) << ((spi_offset_to_0 % 4) * 8),
341 )
342 .unwrap();
343 }
344
345 /// Utility function to set all SGI interrupt targets to CPU0.
346 ///
347 /// This is useful if only CPU0 is active in a system, or if CPU0 handles most interrupts in
348 /// the system.
349 #[inline]
350 pub fn set_all_spi_interrupt_targets_cpu0(&mut self) {
351 for i in 0..0x10 {
352 self.gicd
353 .write_iptr_spi(i, TARGETS_ALL_CPU_0_IPTR_VAL)
354 .unwrap();
355 }
356 }
357
358 #[inline]
359 pub fn enable_sgi_interrupt(&mut self, int_id: usize) -> Result<(), InvalidSpiInterruptId> {
360 if int_id >= 16 {
361 return Err(InvalidSpiInterruptId(int_id));
362 }
363 unsafe { self.gicd.write_iser_unchecked(0, 1 << int_id) };
364 Ok(())
365 }
366
367 #[inline]
368 pub fn enable_all_sgi_interrupts(&mut self) {
369 // Unwrap okay, index is valid.
370 self.gicd
371 .modify_iser(0, |mut v| {
372 v |= ACTIVATE_ALL_SGIS_MASK_ISER;
373 v
374 })
375 .unwrap();
376 }
377
378 #[inline]
379 pub fn enable_ppi_interrupt(&mut self, ppi_int: PpiInterrupt) {
380 // Unwrap okay, index is valid.
381 self.gicd
382 .modify_iser(0, |mut v| {
383 v |= 1 << (ppi_int as u32);
384 v
385 })
386 .unwrap();
387 }
388
389 #[inline]
390 pub fn enable_all_ppi_interrupts(&mut self) {
391 unsafe {
392 self.gicd.modify_iser_unchecked(0, |mut v| {
393 v |= ACTIVATE_ALL_PPIS_MASK_ISER;
394 v
395 })
396 };
397 }
398
399 #[inline]
400 pub fn enable_spi_interrupt(&mut self, spi_int: SpiInterrupt) {
401 let spi_int_raw = spi_int as u32;
402 match spi_int_raw {
403 32..=63 => {
404 let bit_pos = spi_int_raw - 32;
405 // Unwrap okay, valid index.
406 self.gicd.write_iser(1, 1 << bit_pos).unwrap();
407 }
408 64..=92 => {
409 let bit_pos = spi_int_raw - 64;
410 // Unwrap okay, valid index.
411 self.gicd.write_iser(2, 1 << bit_pos).unwrap();
412 }
413 _ => unreachable!(),
414 }
415 }
416
417 #[inline]
418 pub fn enable_all_spi_interrupts(&mut self) {
419 self.gicd.write_iser(1, 0xFFFF_FFFF).unwrap();
420 self.gicd.write_iser(2, 0xFFFF_FFFF).unwrap();
421 }
422
423 /// Enables all interrupts by calling [Self::enable_all_sgi_interrupts],
424 /// [Self::enable_all_ppi_interrupts] and [Self::enable_all_spi_interrupts].
425 pub fn enable_all_interrupts(&mut self) {
426 self.enable_all_sgi_interrupts();
427 self.enable_all_ppi_interrupts();
428 self.enable_all_spi_interrupts();
429 }
430
431 /// Enable the GIC assuming a possibly non-secure configuration.
432 ///
433 /// This function will NOT configure and enable the various interrupt sources. You need to
434 /// set the interrupt sensitivities and targets before calling this function.
435 /// This function configured the control registers with the following settings:
436 ///
437 /// - CPU interface: Secure and non-secure interrupts are enabled. SBPR, FIQen and AckCtrl
438 /// fields set to default value 0.
439 /// - Distributor interface: Both non-secure and secure interrupt distribution enabled.
440 ///
441 /// It calls [Self::update_ctrl_regs] to update the control registers.
442 /// If you need custom settings, you can call [Self::update_ctrl_regs] with your required
443 /// settings.
444 ///
445 /// This will not enable the interrupt exception for the Cortex-AR core. You might also have
446 /// to call [Self::enable_interrupts] for interrupts to work.
447 pub fn enable(&mut self) {
448 self.update_ctrl_regs(
449 InterfaceControl::builder()
450 .with_sbpr(false)
451 .with_fiq_en(false)
452 .with_ack_ctrl(false)
453 .with_enable_non_secure(true)
454 .with_enable_secure(true)
455 .build(),
456 DistributorControlRegister::builder()
457 .with_enable_non_secure(true)
458 .with_enable_secure(true)
459 .build(),
460 );
461 }
462
463 /// Enable the regular interrupt exceprion for the Cortex-A core by calling the
464 /// [interrupt::enable] function. You also need to [Self::enable] the GIC for interrupts to
465 /// work.
466 ///
467 /// # Safety
468 ///
469 /// Do not call this in a critical section.
470 pub unsafe fn enable_interrupts(&self) {
471 unsafe {
472 interrupt::enable();
473 }
474 }
475
476 /// Enable the interrupts for the Cortex-A core by calling the [interrupt::enable] module.
477 pub fn disable_interrupts(&self) {
478 interrupt::disable();
479 }
480
481 /// Update the control registers which control the safety configuration and which also enable
482 /// the GIC.
483 pub fn update_ctrl_regs(&mut self, icr: InterfaceControl, dcr: DistributorControlRegister) {
484 self.gicc.write_icr(icr);
485 self.gicd.write_dcr(dcr);
486 }
487}
488
489/// Helper structure which should only be used inside the interrupt handler once the GIC has
490/// been configured with the [GicConfigurator].
491pub struct GicInterruptHelper(MmioGicCpuInterface<'static>);
492
493impl GicInterruptHelper {
494 /// Create the interrupt helper with the fixed GICC MMIO instance.
495 pub const fn new() -> Self {
496 GicInterruptHelper(unsafe { GicCpuInterface::new_mmio_fixed() })
497 }
498
499 /// Acknowledges an interrupt by reading the IAR register and returning the interrupt context
500 /// information structure.
501 ///
502 /// This should be called at the start of an interrupt handler.
503 pub fn acknowledge_interrupt(&mut self) -> InterruptInfo {
504 let iar = self.0.read_iar();
505 let int_id = iar.ack_int_id().as_u32();
506 let interrupt = match int_id {
507 0..=15 => Interrupt::Sgi(int_id as usize),
508 27..=31 => Interrupt::Ppi(PpiInterrupt::try_from(int_id as u8).unwrap()),
509 32..=92 => Interrupt::Spi(SpiInterrupt::try_from(int_id as u8).unwrap()),
510 SPURIOUS_INTERRUPT_ID => Interrupt::Spurious,
511 _ => Interrupt::Invalid(int_id as usize),
512 };
513 InterruptInfo {
514 interrupt,
515 cpu_id: iar.cpu_id().as_u8(),
516 raw_reg: iar,
517 }
518 }
519
520 /// Acknowledges the end of an interrupt by writing the EOIR register of the GICC.
521 ///
522 /// This should be called at the end of an interrupt handler.
523 pub fn end_of_interrupt(&mut self, irq_info: InterruptInfo) {
524 self.0.write_eoir(irq_info.raw_reg())
525 }
526}
527
528impl Default for GicInterruptHelper {
529 fn default() -> Self {
530 Self::new()
531 }
532}