Skip to content

Day78 - GPIO IRQ Consumer Driver

Objective

Learn how a GPIO consumer driver obtains interrupt resources from a GPIO descriptor and registers an interrupt handler.

Topics covered:

  • GPIO interrupt resources
  • GPIO to IRQ mapping
  • gpiod_to_irq()
  • request_irq()
  • free_irq()
  • GPIO IRQ consumer driver flow
  • Probe and remove lifecycle

Lab1 - Raspberry Pi GPIO IRQ Investigation

Goal

Investigate the relationship between GPIO resources and Linux IRQ resources on Raspberry Pi.

Commands

cat /proc/interrupts
sudo cat /sys/kernel/debug/gpio

Observation

Example from Raspberry Pi 5:

161: ... 107d508500.gpio 20 Edge pwr_button

GPIO debug information:

gpio-553 (PWR_GPIO | pwr_button)

Finding

GPIO553
GPIO Controller
IRQ Mapping
Linux IRQ161

GPIO numbers and IRQ numbers are different resources.


Lab2 - GPIO Descriptor to IRQ Mapping

Goal

Simulate Linux gpiod_to_irq().

Architecture

gpio_desc
chip + offset
IRQ mapping table
Linux IRQ number

IRQ Mapping Structure

struct gpio_irq_map {
    struct gpio_chip *chip;
    uint32_t offset;
    int irq;
};

APIs

int gpio_irq_register_map(
        struct gpio_chip *chip,
        uint32_t offset,
        int irq);
int gpiod_to_irq(
        struct gpio_desc *desc);

Test

Register mappings:

GPIO5 → IRQ100
GPIO7 → IRQ101

Convert GPIO descriptor:

irq = gpiod_to_irq(button);

Result

button GPIO5: irq=100
reset GPIO10: error=-2

Conclusion

gpiod_to_irq() converts a GPIO descriptor into a Linux IRQ resource.


Lab3 - GPIO IRQ Consumer Driver Simulation

Goal

Simulate a Linux GPIO interrupt consumer driver.

Driver Structure

struct demo_button_device {
    struct gpio_desc *button_gpio;
    int irq;
    int irq_count;
};

IRQ Descriptor

struct irq_desc {
    int irq;
    irq_handler_t handler;
    void *dev_id;
    const char *name;
};

IRQ APIs

int request_irq(
        int irq,
        irq_handler_t handler,
        const char *name,
        void *dev_id);
int free_irq(int irq);
int irq_trigger(int irq);

ISR

static int button_isr(
        int irq,
        void *dev_id)
{
    struct demo_button_device *button = dev_id;

    button->irq_count++;

    printf(
        "[ISR] irq=%d count=%d\n",
        irq,
        button->irq_count);

    return 0;
}

Probe Flow

probe()
gpiod_get()
gpiod_to_irq()
request_irq()
device ready

Implementation:

button->button_gpio =
        gpiod_get(chip, 5, "button", false);

button->irq =
        gpiod_to_irq(button->button_gpio);

request_irq(
        button->irq,
        button_isr,
        "demo-button",
        button);

Remove Flow

remove()
free_irq()
gpiod_put()
cleanup

Implementation:

free_irq(button->irq);

gpiod_put(button->button_gpio);

Test Result

Output:

[PROBE] button irq=100
[ISR] irq=100 count=1
[ISR] irq=100 count=2
[REMOVE] free IRQ
[TEST] irq_trigger after remove ret=-2

Validation

Interrupt registration:

PASS

ISR execution:

PASS

IRQ release:

PASS

IRQ trigger after removal:

PASS

Key Takeaways

GPIO interrupt consumer drivers follow the sequence:

Device Tree
devm_gpiod_get()
gpio_desc
gpiod_to_irq()
Linux IRQ
request_irq()
ISR

The GPIO descriptor identifies a GPIO resource, while the IRQ subsystem manages interrupt delivery.

A GPIO line and a Linux IRQ number are different resources connected through GPIO-to-IRQ mapping.