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 kernel 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.
For example, the following comments are provided with a callout_interrupt_*.s file for an ARM board:
/* * 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 */
You can't return from the middle of a interrupt_id_*() or interrupt_eoi_*() kernel callout. You must fall through to the end of the callout code.
For more information about interrupt flags, see INTR_* flags in the System Page chapter.
An interrupt_id_*() kernel callout has the following responsibilities:
These responsibilities mean that an interrupt ID kernel callout needs to:
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 kernel callout needs to 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)
An interrupt_eoi_*() kernel callout has the following responsibilities:
Below is an example of a sends the EOI and unmasks the interrupt from an ARM V2 GIC. See your hardware documentation to learn what your kernel callout needs to 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)