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}