The Linux kernel employs the struct irq_chip abstraction to represent interrupt controller hardware. Since different processor architectures manage interrupts uniquely, this structure encapsulates callback functon pointers directed at the underlying hardware control logic. Key operations such as enabling, masking, and configuring affinity are routed through these functions.
Structure Definition
The core interface resides in struct irq_chip, containing handlers for lifecycle events and configuration updates:
struct irq_chip {
const char *name;
unsigned int (*irq_startup)(struct irq_data *data);
void (*irq_shutdown)(struct irq_data *data);
void (*irq_enable)(struct irq_data *data);
void (*irq_disable)(struct irq_data *data);
void (*irq_ack)(struct irq_data *data);
void (*irq_mask)(struct irq_data *data);
void (*irq_unmask)(struct irq_data *data);
void (*irq_eoi)(struct irq_data *data);
int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
int (*irq_retrigger)(struct irq_data *data);
int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
/* Other members omitted for brevity */
unsigned long flags;
};
Device Tree Configuration
To bind interrupts within the device tree, the corresponding controller node must expose interrupt properties. Below is an example configuraton for a GPIO controller utilizing a GIC SPI line:
my_gpio_controller: gpio@e6050000 {
compatible = "renesas,gpio-r8a7796",
"renesas,rcar-gen3-gpio";
reg = <0 0xe6050000 0 0x50>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
#gpio-cells = <2>;
gpio-controller;
gpio-ranges = <&pfc 0 0 16>;
#interrupt-cells = <2>;
interrupt-controller;
clocks = <&cpg CPG_MOD 912>;
power-domains = <&sysc R8A7796_PD_ALWAYS_ON>;
resets = <&cpg 912>;
};
Driver Implementation
In a kernel driver, populate the irq_chip structure with specific handler implementations. Ensure all mandatory callbacks for the hardware state are defined.
static struct irq_chip platform_gpio_irqchip = {
.name = "platform_gpio",
.irq_ack = gpio_irq_ack_handler,
.irq_set_type = gpio_irq_set_trigger,
.irq_enable = gpio_irq_enable_handler,
.irq_disable = gpio_irq_disable_handler,
.irq_set_affinity = gpio_set_irq_affinity,
};
Handling CPU Affintiy Requests
When the kernel requests to bind an interrupt to specific CPU cores, the .irq_set_affinity callback executes. You must retrieve the parent IRQ chip and delegate the request to manage multi-level interrupt hierarchies effectively.
static int gpio_set_irq_affinity(struct irq_data *data,
const struct cpumask *cpumask,
bool force)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
struct irq_chip *parent_chip = irq_get_chip(gc->irq.irq);
if (parent_chip && parent_chip->irq_set_affinity) {
return parent_chip->irq_set_affinity(data, cpumask, force);
}
return -EINVAL;
}
This ensures that affinity settings propagate correctly up the interrupt hierarchy to the root controller capable of executing the hardware routing change.