Inside platform_get_irq Device Tree Resolution- Free Linux Device Drivers Course

 

Inside platform_get_irq Device Tree Resolution

Free Linux Kernel Development Course — Device Tree Chapter, Part 14

If you have ever wondered how platform_get_irq() quietly returns the right interrupt number whether your board uses a hardcoded resource table or a device tree node, this lecture answers that exact question. Understanding platform_get_irq device tree resolution is one of those topics that separates driver authors who copy-paste boilerplate from engineers who actually understand what the kernel is doing on their behalf. In this free Linux kernel development course lecture, we open up the helper function itself and trace what happens between your probe() call and the interrupt number landing in your hands.

What You Will Learn

  • What actually executes inside platform_get_irq() on a modern kernel
  • Why CONFIG_OF_IRQ and dev->of_node decide which code path runs
  • How of_platform_device_create_pdata() turns a device tree node into a real platform_device at boot
  • Why your driver never has to call an of_* function directly just to read an IRQ or a memory resource
  • How to build and test an original kernel module that exposes this internal behaviour on real hardware

Prerequisites

  • Comfortable with basic platform driver structure (probe, remove, of_match_table)
  • Familiar with device tree reg and interrupts properties from earlier lectures in this series
  • A Linux kernel 6.x build environment (Raspberry Pi, BeagleBone, or QEMU virt board all work)

Why platform_get_irq Needs to Support Two Worlds

Every platform driver you write is expected to run on two very different kinds of systems: legacy boards that describe hardware in C source files using struct resource arrays, and modern boards that describe the same hardware declaratively in a device tree. The kernel does not want driver authors writing two versions of every driver, so functions like platform_get_irq(), platform_get_resource(), and their friends are written to silently support both. That silent support is exactly what we are going to unpack.

The CONFIG_OF_IRQ Fast Path

The very first thing platform_get_irq() checks is whether device tree interrupt support is compiled in and whether your device actually has an associated device tree node. In pseudo-terms, the check looks like this: if CONFIG_OF_IRQ is enabled and dev->dev.of_node is not NULL, the function hands control to of_irq_get(), which walks the interrupts property of your node, resolves the interrupt-parent chain, and maps the specifier down to a Linux IRQ number through the interrupt controller driver. If that resolution succeeds, or if it returns -EPROBE_DEFER because the interrupt controller has not probed yet, platform_get_irq() returns immediately without touching the legacy resource table at all.

Falling Back to Resource Tables

If there is no device tree node, or the of_node exists but of_irq_get cannot resolve an entry, the function falls through to the older mechanism: it calls platform_get_resource() with type IORESOURCE_IRQ and reads the interrupt number straight out of the resource array your board file populated. This is the exact mechanism boards used before device tree existed, and it still works unchanged today, which is why old-style resource-based drivers never had to be rewritten when a board moved to device tree.

platform_get_irq() Internal Decision Flow
probe() calls platform_get_irq() of_node present + CONFIG_OF_IRQ?
Yes: of_irq_get() No or unresolved: platform_get_resource(IORESOURCE_IRQ)
IRQ number returned to your driver

How of_platform_device_create_pdata Bridges DT and platform_device

There is a second piece of the puzzle that most tutorials skip entirely: your driver’s probe() function receives a normal struct platform_device *pdev even when the hardware was only described in a device tree, with no C code registering it. That bridging happens during early boot, when the kernel walks the flattened device tree and calls of_platform_device_create_pdata() on each compatible node. This function allocates a real platform_device, attaches the resources it can infer from the node (reg ranges, interrupts), and links the device tree node into dev->of_node so later calls like of_property_read_u32() know exactly which node to consult.

The practical result is that by the time your probe() runs, platform_get_resource() and platform_get_irq() already have everything they need, regardless of whether the underlying description came from a board file or a device tree blob.

Boot Stage What Happens
1. DT parsing Kernel walks the flattened device tree blob node by node
2. Device creation of_platform_device_create_pdata() builds a platform_device per matching node
3. Bus matching platform_match() pairs the device with a registered platform_driver
4. probe() call Your driver’s probe runs with dev->of_node already populated
5. Resource extraction platform_get_irq() / platform_get_resource() resolve transparently

Hands-On: An Original Driver That Reveals the Internal Path

The following original module, ep_irqsrc_demo, does not hide the internal behaviour — it reports it. It checks whether the device has an of_node, then calls platform_get_irq() and prints the resolved IRQ number along with which code path almost certainly serviced the request. This is a teaching driver, written for this course, and is not taken from any existing textbook example.

// ep_irqsrc_demo.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/interrupt.h>

static irqreturn_t ep_irqsrc_isr(int irq, void *data)
{
    return IRQ_HANDLED;
}

static int ep_irqsrc_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    int irq;
    bool has_of_node = (dev->of_node != NULL);

    irq = platform_get_irq(pdev, 0);
    if (irq < 0) { dev_err(dev, "ep_irqsrc_demo: no IRQ resource found (err=%d)\n", irq); return irq; } dev_info(dev, "ep_irqsrc_demo: resolved IRQ %d via %s path\n", irq, has_of_node ? "device tree (of_irq_get)" : "legacy resource table"); return devm_request_irq(dev, irq, ep_irqsrc_isr, IRQF_SHARED, "ep_irqsrc_demo", pdev); } static void ep_irqsrc_remove(struct platform_device *pdev) { dev_info(&pdev->dev, "ep_irqsrc_demo: removed\n");
}

static const struct of_device_id ep_irqsrc_of_match[] = {
    { .compatible = "ep,irqsrc-demo" },
    { }
};
MODULE_DEVICE_TABLE(of, ep_irqsrc_of_match);

static struct platform_driver ep_irqsrc_driver = {
    .probe  = ep_irqsrc_probe,
    .remove = ep_irqsrc_remove,
    .driver = {
        .name = "ep_irqsrc_demo",
        .of_match_table = ep_irqsrc_of_match,
    },
};
module_platform_driver(ep_irqsrc_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Reveals which internal path platform_get_irq resolved through");

Add a matching node to your board’s device tree overlay, using an interrupt line that is actually free on your board (check your SoC’s datasheet or existing dts files first):

// ep-irqsrc-overlay.dts (fragment)
&soc {
    ep_irqsrc: irqsrc-demo {
        compatible = "ep,irqsrc-demo";
        interrupt-parent = <&intc>;
        interrupts = <0 45 4>;
    };
};

Build, compile the overlay, and load it:

make -C /lib/modules/$(uname -r)/build M=$PWD modules
dtc -@ -I dts -O dtb -o ep-irqsrc-overlay.dtbo ep-irqsrc-overlay.dts
sudo dtoverlay ep-irqsrc-overlay.dtbo
sudo insmod ep_irqsrc_demo.ko
dmesg | tail -5

Expected output on a device tree boot:

ep_irqsrc_demo: resolved IRQ 21 via device tree (of_irq_get) path

On a legacy, non-DT platform where the same driver is instantiated from a board file with a populated struct resource array instead, the identical driver prints:

ep_irqsrc_demo: resolved IRQ 21 via legacy resource table path

Same driver, same API call, two completely different internal roads — and your code never had to know which one it was on.

Common Mistakes and Troubleshooting

Mistake Why It Breaks Fix
Calling of_irq_get() directly instead of platform_get_irq() Breaks on non-DT boards, loses -EPROBE_DEFER handling Always call platform_get_irq(), let it dispatch internally
Ignoring a -EPROBE_DEFER return Interrupt controller may not have probed yet Propagate the error code straight back from probe()
Assuming IRQ numbers are stable across reboots Linux IRQ numbers are dynamically assigned Never hardcode IRQ numbers in userspace tooling
Missing interrupt-parent in the DT node of_irq_get() cannot resolve the specifier Verify the interrupt-parent chain resolves to a registered controller

Best Practices

  • Always use platform_get_irq() rather than reaching into of_* APIs manually — it already handles both legacy and DT paths correctly
  • Check the return value for negative errno on kernel 6.x; the old convention of returning 0 on failure was removed years ago
  • Use platform_get_irq_byname() once a device exposes more than one interrupt line, so the DT author’s naming stays authoritative
  • Keep interrupt handling registration (devm_request_irq) inside probe so it is automatically cleaned up on driver unbind

Performance and Security Considerations

The DT resolution path adds a small, one-time cost at probe time only — it is not repeated on every interrupt, so there is no runtime performance penalty from choosing device tree over board files. From a security standpoint, treat device tree overlays as trusted input: a malformed or malicious interrupts property can cause a driver to bind to the wrong interrupt line, so overlays should only be loaded from a verified boot chain in production systems.

Summary and Key Takeaways

  • platform_get_irq() checks dev->of_node and CONFIG_OF_IRQ first, calling of_irq_get() when a device tree node is present
  • It falls back to platform_get_resource(IORESOURCE_IRQ) for legacy, non-DT boards
  • of_platform_device_create_pdata() is what creates the platform_device from a DT node at boot time, before your probe ever runs
  • Your driver code stays identical across DT and non-DT boards because these helper functions absorb the difference
free linux kernel development course
free linux device drivers course
free embedded linux course
platform_get_irq device tree

Frequently Asked Questions

Does platform_get_irq work the same way on ACPI-based systems?

ACPI systems use a parallel resolution path through the ACPI interrupt tables rather than of_irq_get(), but the same platform_get_irq() call abstracts that difference too, so driver code does not need to change.

What does a negative return value from platform_get_irq mean?

Any negative value is a standard Linux errno, most commonly -ENXIO when no interrupt resource could be found, or -EPROBE_DEFER when the interrupt controller has not registered yet and your driver should retry later.

Can I skip device tree and still use platform_get_irq?

Yes. On boards without device tree support, platform_get_irq() automatically falls back to reading the legacy struct resource array populated in the board file, with no code changes required in your driver.

Why does my driver never call of_platform_device_create_pdata directly?

That function runs automatically during early boot as the kernel walks the device tree; individual drivers only implement probe() and never need to create their own platform_device instances for DT-described hardware.

Is there a runtime performance cost to device tree interrupt resolution?

No. Resolution happens once during probe(), not on every interrupt, so there is no measurable runtime overhead from using device tree over a legacy board file.

What is the difference between platform_get_irq and platform_get_irq_byname?

platform_get_irq() takes a numeric index into the interrupts property, while platform_get_irq_byname() looks up an entry using the interrupt-names property, which is safer once a device exposes more than one interrupt line.

Continue the Free Linux Kernel Development Course

Next, we look at platform data versus device tree and close out the Device Tree chapter.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *