Fundamentals 47 min read

How ARM+Linux Interrupts Power Efficient Computing: Deep Dive into Mechanisms

This article explains the role of the interrupt system in ARM‑based Linux platforms, covering hardware modes, vector tables, exception handling, GIC architecture, and kernel integration, and provides detailed code examples and optimization techniques for developers working on embedded and high‑performance systems.

Deepin Linux
Deepin Linux
Deepin Linux
How ARM+Linux Interrupts Power Efficient Computing: Deep Dive into Mechanisms

In today’s digital era, efficient computation drives technological progress, and the ARM architecture combined with the Linux interrupt system acts as a key to unlocking high‑performance, low‑power computing across devices from smartphones to industrial controllers.

The interrupt system works like an intelligent manager: when external devices (e.g., keyboard, mouse) or internal events (e.g., timer overflow) request service, the CPU temporarily pauses the current task, handles the urgent request, and then resumes the original work, greatly improving response speed and overall efficiency. In the ARM+Linux combination, the interrupt system is heavily optimized to exploit the strengths of both.

1. Interrupt System: The "Emergency Responder" of the Computing World

Just as a fire alarm instantly alerts firefighters, an interrupt instantly notifies the CPU of urgent events. The CPU stops its current execution, processes the interrupt, and then returns to the original flow. This mechanism dramatically speeds up system response.

2. ARM Interrupt Hardware Foundations

2.1 ARM Operating Modes and Exception Categories

User Mode : Normal application execution with limited privileges, similar to a citizen operating within a confined space.

System Mode : A privileged mode that can freely access system resources, akin to a city manager.

IRQ Mode : Handles general interrupt requests (e.g., keyboard, mouse).

FIQ Mode : Handles fast, time‑critical interrupts with additional banked registers for quicker context switching.

Supervisor Mode : The default mode after power‑on, used for software interrupts (SWI) and system initialization.

Abort Mode : Triggered by illegal memory accesses, similar to a security checkpoint.

Undefined Mode : Entered when the CPU cannot decode an instruction, used for software emulation of coprocessors.

Except for User Mode, the other six modes are privileged and can be entered either by software or by specific exception events.

When an exception occurs (e.g., FIQ), the CPU saves the return address in LR, copies CPSR to SPSR, switches to the appropriate mode, and jumps to the exception vector address. Exiting the exception restores LR to PC, copies SPSR back to CPSR, and clears the interrupt disable flag.

For an external IRQ, the CPU checks the I bit in CPSR; if it is 0, the CPU switches to IRQ mode, jumps to vector address 0x18, determines the interrupt source, executes the corresponding service routine, clears the interrupt flag, and returns to the pre‑interrupt context.

2.2 ARM Interrupt Pins and Enable Control

ARM cores typically have two interrupt pins: irq for normal interrupts and fiq for fast interrupts. The CPSR I and F bits enable or disable handling of these pins. When both bits are 0, the CPU will respond to signals on both pins.

3. ARM+Linux Interrupt System Basics

3.1 ARM Architecture Interrupt Mechanism

ARM defines two main interrupt types: IRQ (general external interrupt) and FIQ (fast interrupt). IRQ is used for most peripherals, while FIQ is reserved for high‑priority, time‑critical events such as high‑speed data transfer.

3.2 Linux Management of ARM Interrupts

Linux builds the interrupt vector table during early boot (e.g.,

early_trap_init()

), copies the exception vectors and stubs to the configured base address (usually 0xffff0000), and registers device interrupt handlers via

request_irq()

. The kernel’s

irq_desc

array links interrupt numbers to

irqaction

structures that contain the handler function, flags, and device information.

4. Linux on ARM Interrupt Construction

4.1 Exception Vector Table Creation and Setup

<code>void __init early_trap_init(void)
{
    unsigned long vectors = CONFIG_VECTORS_BASE;
    extern char __stubs_start[], __stubs_end[];
    extern char __vectors_start[], __vectors_end[];
    extern char __kuser_helper_start[], __kuser_helper_end[];
    int kuser_sz = __kuser_helper_end - __kuser_helper_start;

    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
    memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, sizeof(sigreturn_codes));
    flush_icache_range(vectors, vectors + PAGE_SIZE);
    modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
#define CONFIG_VECTORS_BASE 0xffff0000</code>

The vector table contains entries such as

b vector_irq + stubs_offset

for normal interrupts and

b vector_fiq + stubs_offset

for fast interrupts.

4.2 Interrupt Handling Function Flow

When hardware raises an interrupt, the controller checks mask and priority registers. If allowed, it asserts the IRQ pin. The CPU switches to IRQ mode, jumps to address 0x18, which typically contains

LDR PC, IRQ_ADDR

to branch to the specific service routine. The routine saves registers, determines the source, calls the registered handler via

irq_handler

, clears the pending flag, and restores the saved context.

<code>asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc = irq_desc + irq;
    if (irq >= NR_IRQS)
        desc = &amp;bad_irq_desc;
    irq_enter();
    desc_handle_irq(irq, desc);
    irq_finish(irq);
    irq_exit();
    set_irq_regs(old_regs);
}
static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc-&gt;handle_irq(irq, desc);
}</code>

The

asmlinkage

macro expands to

extern "C"

on ARM, indicating that arguments are passed according to the ATPCS calling convention (register‑based), not via the stack.

5. ARM GIC Interrupt Controller Analysis

5.1 GIC Concepts and Versions

The Generic Interrupt Controller (GIC) acts as the central dispatcher for all interrupt sources. Versions V1–V4 exist; V2 is common in ARMv7‑A SoCs, while V3/V4 target ARM64 servers.

5.2 GIC Interrupt Input Types

PPI (Private Peripheral Interrupt): Up to 16 per‑CPU interrupts, e.g., per‑core timers.

SPI (Shared Peripheral Interrupt): Up to 988 shared interrupts for devices such as GPIO, UART, etc.

5.3 GIC Working Mechanism and Register Functions

The GIC consists of a Distributor and a CPU Interface.

Distributor : Enables/disables global and per‑interrupt signals, sets priorities, routes interrupts to specific CPUs, configures trigger mode (edge/level), and groups interrupts for security.

CPU Interface : Controls delivery of pending interrupts to each core, acknowledges interrupts, signals end‑of‑interrupt, masks low‑priority interrupts, and defines pre‑emptive policies.

6. ARM+Linux Interrupt System Applications and Optimizations

6.1 Interrupt Handler Optimization

Separate critical (time‑sensitive) work from non‑critical work, use fast algorithms (e.g., quicksort instead of bubble sort), and keep the handler short to avoid long interrupt latency.

6.2 Reasonable Hardware Resource Configuration

Set appropriate GIC interrupt priorities, route high‑priority interrupts to powerful cores, and balance load across CPUs to maximize throughput.

6.3 Code Implementation

Example of registering and unregistering an interrupt in a kernel module:

<code>#include &lt;linux/module.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/interrupt.h&gt;
#include &lt;linux/irq.h&gt;

static irqreturn_t my_interrupt_handler(int irq, void *dev_id)
{
    printk(KERN_INFO "My Interrupt Handler: IRQ %d\n", irq);
    return IRQ_HANDLED;
}

static int __init my_module_init(void)
{
    int irq_num = 50; // example interrupt number
    int ret;
    ret = request_irq(irq_num, my_interrupt_handler, IRQF_TRIGGER_RISING, "my_device", NULL);
    if (ret) {
        printk(KERN_ERR "Failed to request IRQ %d: %d\n", irq_num, ret);
        return ret;
    }
    printk(KERN_INFO "Successfully registered IRQ %d\n", irq_num);
    return 0;
}

static void __exit my_module_exit(void)
{
    int irq_num = 50;
    free_irq(irq_num, NULL);
    printk(KERN_INFO "Successfully unregistered IRQ %d\n", irq_num);
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("ARM+Linux Interrupt Example");
</code>

The handler checks the interrupt source (e.g., button or timer), performs the necessary action, and returns

IRQ_HANDLED

or

IRQ_NONE

for unknown sources.

kernelLinuxARMEmbedded SystemsGICinterrupts
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.