ARM GIC Interrupt Toggle Guide

GICD_ISENABLER and GICD_ICENABLER

The ARM Generic Interrupt Controller architecture specification describes two sets of registers meant for enabling and disabling the forwarding of interrupts at the GIC Distributor.

  • Interrupt Set-Enable Registers: GICD_ISENABLER
  • Interrupt Clear-Enable Registers: GICD_ICENABLER

For INTID m, when DIV and MOD are the integer division and modulo operati= ons:

  • The corresponding ENABLER number, n, is given by:

n = m DIV 32

  • The offset of the required ISENABLER (Enable) is:

(0x100 + (4*n))

  • The offset of the required ICENABLER (Disable) is:

(0x180 + (4*n))

  • The bit number of the required group modifier bit in this register is:

m = MOD 32

NOTE: Writing a 1 to a GICD_ICENABLER bit only disables the forwarding of the corresponding interrupt from the Distributor to any CPU interface. It does not prevent the interrupt from changing state, for example becoming pending or active and pending if it is already active

APU GIC Base Address

The Zynq Ultrascale+ Devices Register Reference (UG1087) contains information on the APU GIC Interrupt Controller and its registers.

Register Name Offset
GICD_ISENABLER0 0x0000010100
GICD_ISENABLER1 0x0000010104
GICD_ISENABLER2 0x0000010108
GICD_ISENABLER3 0x000001010C
GICD_ISENABLER4 0x0000010110
GICD_ISENABLER5 0x0000010114
GICD_ICENABLER0 0x0000010180
GICD_ICENABLER1 0x0000010184
GICD_ICENABLER2 0x0000010188
GICD_ICENABLER3 0x000001018C
GICD_ICENABLER4 0x0000010190
GICD_ICENABLER5 0x0000010194

NOTE: Base address of the APU GIC Interrupt for Zynq Ultrascale+ Devices is 0xF9000000 but the offsets start at 0x10000 giving us 0xF9010000 as our base address.

Determining the Interrupt ID (INTID)

The Zynq UltraScale+ TRM (UG1085) Table 13-1 lists all system interrupts and their corresponding GIC number. PL_PS_Group 0 and 1 corresponds to interrupts from the PL which enter the PS at the GIC. You can stipulate the GIC number of your interrupt signal from this.

Name GIC # Description
PL_PS_Group0 121:128 PL to PS interrupt signals 0 to 7
PL_PS_Group1 136:143 PL to PS interrupt signals 8 to 15

Example: I routed a UART interrupt to pin 0 of the GIC, therefore my GIC#/INTID is 121

You can also determine the INTID on target with:

$ cat /proc/interrupts
...
  22:          0          0     GIC  35 Level     arm-pmu
  23:          0          0     GIC  37 Level     cdns-i2c
  24:         42          0     GIC  38 Level     cdns-i2c
 138:       1254          0     GIC 138 Level     ptp_sync
 139:          0          0     GIC 139 Level     xilinx-dpdma
 140:          0          0     GIC 140 Level     xilinx-display

INTID is the value after GIC (35, 37, etc)

C Example

NOTE: When modifying GIC registers in a multi-core system, ensure proper synchronization and use appropriate memory barriers. This prevents race conditions and ensures that changes are visible across all cores. This example is NOT a final product.

#define GIC_ENABLER_BASE_ADDR                     0xF9010000U
#define IC_ENABLER_OFFSET(n)                (0x180 + (4 * n))
#define IS_ENABLER_OFFSET(n)                (0x100 + (4 * n))

#define BITS_PER_REGISTER                                  32
#define REG_INDEX(int_num)      (int_num / BITS_PER_REGISTER)
#define BIT_POSITION(int_num)   (int_num % BITS_PER_REGISTER)

const uint32_t INTID = 138; // arbitrary #

// Calculate the register index and bit position for the interrupt
uint32_t reg_index = REG_INDEX(INTID);
uint32_t bit_position = BIT_POSITION(INTID);

// Calculate the memory offset for the clear-enable register
uint32_t clear_enable_offset = IC_ENABLER_OFFSET(reg_index);

// Get a pointer to the clear-enable register
volatile uint32_t* enabler_offset = (volatile uint32_t*)((uint8_t*)mapped_base + clear_enable_offset);

// Set the bit to disable the interrupt
*enabler_offset |= (1 << bit_position);

References

  • ARM Generic Interrupt Controller Architecture Specification (12.9.7, 12.9.26)
  • Zynq UltraScale+ Device TRM (UG1085) (Table 13-1)
  • Zynq UltraScale+ Devices Register Reference (UG1087) (GIC400 Module)