Interrupt(3): interrupt callouts (ARM GIC)

Registers:

  1. Interrupt Acknowledge Register (ICCIAR)
      • ICCIAR[12:10]: the processor(CPU) that requested the interrupt;
      • ICCIAR[9:0]: the interrupt ID
    • The processor reads this register to obtain the interrupt ID of the signaled interrupt.This read acts as an acknowledge for the interrupt.
    • A read of the ICCIAR returns the interrupt ID of the highest priority pending interrupt for the CPU interface,
      • The read returns a spurious interrupt ID of 1023 if any of the following apply:
        • Signalling of interrupts to the CPU interface is disabled
        • There is no pending interrupt on this CPU interface with sufficient priority for the interface to signal it to the processor.
  2. End of Interrupt Register (ICCEOIR)
    • A processor writes to this register to inform the CPU interface that it has completed its interrupt service routine for the specified interrupt.
    • The interrupt service routine on the connected processor must write to the ICCEOIR for every read of a valid Interrupt ID from the ICCIAR,
    • Writing to this register causes the GIC to change the status of the identified interrupt:
      • to inactive, if it was active
      • to pending, if it was active and pending.
  3. Interrupt Clear-Enable Registers (ICDICERn), n = 0 ~31
    • Writing 1 to a Clear-enable bit disables forwarding of the corresponding interrupt to the CPU interfaces.
  4. Interrupt Set-Enable Registers (ICDISERn)
    • Writing 1 to a Set-enable bit enables forwarding of the corresponding interrupt to the CPU interfaces.

id() callout:

  1. read ICCIAR reg to get the interrupt ID –> this is step 5 described in arm-interrupt-controller.
    • if  ICCIAR[9:0] is 0 (it might be SGI), write   ICCEOIR and exit id().
    • if ICCIAR[12:0} is set to 0x3FF, it is a spurious interrupt ID, exit id()
  2. disable interrupt source by writing to the corresponding bit in ICENABLERn.
  3. writes the interrupt ID to the ICCEOIR, to signal the completion — > step 6

eoi() callout:

  1. if Interrupt ID is 0, do nothing.
  2. if mask count is not zero, do nothing.
  3. if mask count is zero, enable interrupt source by writing to the corresponding bit in ISENABLERn

mask() callout: write to ICDICERn.

unmask() callout writes to ISDICERn

/*
 * -----------------------------------------------------------------------
 * Identify interrupt source.
 *
 * Returns interrupt number in r4
 * -----------------------------------------------------------------------
 */
CALLOUT_START(interrupt_id_gic, rw_intr, patch_id)
	mov		ip,     #0x000000ff		// RW data offset (patched)	
	orr		ip, ip, #0x0000ff00
	add		ip, r5, ip				// INTR_GENFLAG_LOAD_SYSPAGE specified

	ldr		r0, [ip, #OFF_GIC_CPU]	// cpu interface registers
	ldr		r1, [ip, #OFF_GIC_DIST]	// distributor registers

	/*
	 * Get interrupt ID and check for special cases:
	 * ID0    - used for IPI: immediately send eoi as we need src cpu to eoi
	 * ID1023 - spurious interrupt: set vector to -1
	 */
	ldr		r4, [r0, #ARM_GICC_IAR]
	bics	ip, r4, #ARM_GICC_IAR_SRCID   // defined as 0x1C00. r4 contains Interrupt ID
	streq	r4, [r0, #ARM_GICC_EOIR]   // if r4[9:0] is 0, write EOI.
	mov		r4, ip   // mov from ip to r4, Interrupt IDs?
	beq		0f
	mov		ip,     #0x0ff
	orr		ip, ip, #0x300
	teq		r4, ip
	mvneq	r4, #0     // if (r4 == 0x3FF), move -1 to r4
	beq		0f

	/*
	 * Mask the interrupt source
	 */
	and		r2, r4, #0x1f     // lower 5 bits of r4 --> r2
	mov		r3, #1
	mov		r3, r3, lsl r2	// 1 << r2 --> r3   ==> 1 << (id % 32)
	mov		r2, r4, lsr #5   // r4 << 5 --> r2  ==> get the index n
	mov		r2, r2, lsl #2   // r2 << 2 --> r2 ==> reg offset to GICD_ICENABLEERn
	add		r2, r2, #ARM_GICD_ICENABLERn		// ENABLE_CLR[id / 32]
	str		r3, [r1, r2]

	/*
	 * Send EOI to controller
	 */
	str		r4, [r0, #ARM_GICC_EOIR]
0:
CALLOUT_END(interrupt_id_gic)
 * On entry:
 *	r4 contains the interrupt number
 *	r7 contains the interrupt mask count
 * -----------------------------------------------------------------------
 */
CALLOUT_START(interrupt_eoi_gic, rw_intr, patch_rw)
	mov		ip,     #0x000000ff		// RW data offset (patched)	
	orr		ip, ip, #0x0000ff00
	add		ip, r5, ip				// INTR_GENFLAG_LOAD_SYSPAGE specified

	/*
	 * Special case: IPI interrupt (ID0) is never disabled
	 */
	teq		r4, #0    // if r4 == 0, the condition is false (0)
	beq		0f

	/*
	 * Only unmask interrupt if mask count is zero
	 */
	teq		r7, #0
	bne		0f

	ldr		r0, [ip, #OFF_GIC_DIST]				// distributor registers
	and		r2, r4, #0x1f
	mov		r3, #1
	mov		r3, r3, lsl r2						// 1 << (id % 32)
	mov		r2, r4, lsr #5
	mov		r2, r2, lsl #2
	add		r2, r2, #ARM_GICD_ISENABLERn		// ENABLE_SET[id / 32]
	str		r3, [r0, r2]
0:
CALLOUT_END(interrupt_eoi_gic)
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s