interrupt_id_*() and interrupt_eoi_*()
To improve performance, the interrupt_id_*() and interrupt_eoi_*() kernel callouts are intergrated directly into the kernel code.
The interrupt ID and end of interrupt (EOI) kernel callouts aren't called in the same way as the other callouts. For details about interrupt_id_*() and interrupt_eoi_*(), see the callout_interrupt_*.s files in the startup library for the relevant CPU architecture. These files have descriptions that specify which registers are used to pass values to and from these callouts.
/*
* MC9328MX1/MC9328MX21/MCIMX31 specific interrupt callouts.
*
* interrupt_id_* and interrupt_eoi_* are copied and intermixed with other
* kernel code during initialisation.
*
* They do not follow normal calling conventions, and must fall through
* to the end, rather than attempting to perform a return instruction.
*
* The INTR_GENFLAG_* bits in the intrinfo_entry defines which of the
* following values can be loaded on entry to these code fragments:
*
* r5 - holds the syspageptr (INTR_GENFLAG_SYSPAGE set)
* r6 - holds the intrinfo_entry pointer (INTR_GENFLAG_INTRINFO set)
* r7 - holds the interrupt mask count (INTR_GENFLAG_INTRMASK set)
*
* The interrupt_id_* routine returns the controller-relative level in r4
*/
INTR_* flagsin the System Page chapter.
interrupt_id_*() kernel callouts
- Put the asserted interrupt level into a CPU register.
- Mask the asserted interrupt level. Masking is necessary to handle edge-triggered interrupts and prevent the current interrupt from interrupting again before we’re finished responding to it.
- Read information from some kind of interrupt Status register.
- Perform bit manipulation to determine the interrupt level.
- Put the interrupt level value into a general-purpose CPU register, so the information can be used by the kernel.
- If a glitch or spurious assertion occurs, put a -1 into a GPR to indicate no interrupt to the kernel.
- Mask the interrupt by manipulating bits in an enable-and-mask register.
Below is an example of a kernel callout that gets the interrupt ID and masks the interrupt from an ARM V2 GIC. Check your hardware documentation to learn what your callout must do to identify and mask callouts.
/*
* -----------------------------------------------------------------------
* Identify interrupt source
*
* x20 - syspage pointer
* -----------------------------------------------------------------------
*/
CALLOUT_START(interrupt_id_gic_v2, rw_size, rw_patch)
mov w7, 0xabcd // offset to rw data (patched)
add x7, x7, x20 // address of rw data
ldr x6, [x7, #OFF_GICC]
/*
* Get interrupt ID and handle special cases:
* ID0 - used for IPIs
* ID1022 - spurious interrupt
* ID1023 - spurious interrupt
*/
ldr w0, [x6, #ARM_GICC_IAR]
and w19, w0, #ARM_GICC_IAR_IDMASK
cbz w19, 0f
cmp w19, #1022
bhi 1f
/*
* Mask interrupt
*/
ldr x5, [x7, #OFF_GICD]
add x5, x5, #ARM_GICD_ICENABLERn
and w0, w19, #0x1f
mov w1, #1
lsl w1, w1, w0 // bit to set = 1 << (id % 32)
lsr w0, w19, #5 // index = id / 32
str w1, [x5, x0, lsl #2] // ICENABLERn[index] = bit
b 2f
/*
* IPI interrupt (ID0) - acknowledge using full SRCID and exit
*/
0: str w0, [x6, #ARM_GICC_EOIR]
b 2f
/*
* Spurious interrupt - set id to -1
*/
1: mov x19, #-1
/*
* Done - interrupt id is in x19
*/
2:
CALLOUT_END(interrupt_id_gic_v2)
interrupt_eoi_*() kernel callouts
- Tell the interrupt controller that the interrupt has been serviced.
- Unmask the interrupt level. Unmasking is usually done by manipulating bits in the enable-and-mask register where the masking was done.
- If necessary, manipulate other bits in the register to prompt the interrupt controller to reevaluate the input it received.
Below is an example of a kernel callout that sends the EOI and unmasks the interrupt from an ARM V2 GIC. See your hardware documentation to learn what your callout must do to perform these tasks.
/*
* -----------------------------------------------------------------------
* Acknowledge specified interrupt
*
* x19 - contains interrupt id
* x20 - contains syspage pointer (INTR_GENFLAG_LOAD_SYSPAGE was used)
* x22 - contains intr mask count (INTR_GENFLAG_LOAD_INTRMASK was used)
* -----------------------------------------------------------------------
*/
CALLOUT_START(interrupt_eoi_gic_v2, rw_size, rw_patch)
mov w7, 0xabcd // offset to rw data (patched)
/*
* Skip ID0 because we EOI in the id callout and never mask it
*/
cbz w19, 0f
add x7, x7, x20 // address of rw data
ldr x5, [x7, #OFF_GICD]
ldr x6, [x7, #OFF_GICC]
/*
* Send EOI
*/
str w19, [x6, #ARM_GICC_EOIR]
/*
* Unmask interrupt if mask count is zero.
*/
cbnz w22, 0f
add x5, x5, #ARM_GICD_ISENABLERn
and w0, w19, #0x1f
mov w1, #1
lsl w1, w1, w0 // bit to set = 1 << (id % 32)
lsr w0, w19, #5 // index = id / 32
str w1, [x5, x0, lsl #2] // ISENABLERn[index] = bit
0:
CALLOUT_END(interrupt_eoi_gic_v2)