Platform Resources In Device Tree
Free Linux Kernel Development Course — Device Tree Chapter, Lecture 13
free linux kernel development course
free embedded systems course
free linux device drivers course
free embedded linux course
platform_get_resource platform_get_irq
What You Will Learn
This lecture continues our free Linux kernel development course and shows how platform resources are pulled out of the device tree automatically, without any extra device-tree-specific code in the driver. Platform resources in device tree systems — memory regions, interrupt lines, DMA channels — are exposed to the driver through the same platform_get_resource() and platform_get_irq() APIs used on non-DT systems, because the platform bus core walks the device tree internally on your behalf. You will build an original driver that reads a memory-mapped register block and an interrupt line straight from a DT node, using the modern kernel 6.x resource helpers.
- How platform_get_resource() transparently reads the device tree
- devm_platform_ioremap_resource() and devm_platform_get_and_ioremap_resource()
- platform_get_irq() for interrupt resources
- Original ep_resource_demo driver
- Testing and expected dmesg output
Prerequisites
Before this lecture, in this free embedded Linux course you should already be comfortable with the reg property from our device tree addressing lectures, the named resources lecture covering reg-names and interrupt-names, and basic request_threaded_irq() usage. A kernel 6.x build setup with headers for your target board or a QEMU environment is assumed.
How platform_get_resource() Works With Device Tree
A platform device populated from a device tree node does not need its resource array filled in by hand. When the device tree core creates a struct platform_device for a DT node, it walks that node’s reg and interrupts properties and builds the resource array automatically before probe ever runs. This means a driver written years before device tree existed, and one written today for a DT-only board, can call the exact same platform_get_resource() function and get back a valid struct resource either way. The driver code does not know or care whether the resource came from a device tree node or a hand-built board file resource array — that is the whole point of the platform resource abstraction.
Resource Resolution Flow
| Device tree node (reg, interrupts properties) |
| ↓ |
| OF core builds struct platform_device with resource array filled in |
| ↓ |
| Driver calls platform_get_resource() / platform_get_irq() as usual |
| ↓ |
| devm_ioremap_resource() maps the memory region for driver use |
Memory-Mapped Resources From DT Without Extra Code
The classic two-step pattern looks up the memory resource, then maps it:
struct resource *mem;
void __iomem *base;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(base))
return PTR_ERR(base);
This works identically whether pdev came from a device tree node or a board file, because platform_get_resource() reads from the same resource array either way. No of_-prefixed function is required at all for this basic case.
The Modern Helper: devm_platform_ioremap_resource()
On kernel 6.x, most new drivers skip the two-step pattern entirely and use a single helper that combines both calls:
void __iomem *base;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
This is functionally identical to the two-step version but shorter, and it is the form recommended in current kernel driver-model documentation and used throughout recent mainline patches that modernize older drivers.
devm_platform_get_and_ioremap_resource() When You Need The Resource Too
Sometimes a driver still needs the struct resource * itself — for example to log the physical address, or to compute the region size for a secondary mapping. For that case, kernel 6.x provides a variant that both maps the resource and hands back the resource pointer:
struct resource *res;
void __iomem *base;
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
dev_info(&pdev->dev, "mapped %pa size 0x%llx at %p\n",
&res->start, (unsigned long long)resource_size(res), base);
IRQ Resources With platform_get_irq()
Interrupt lines follow the same transparent pattern. platform_get_irq() reads the DT node’s interrupts property (already decoded by the interrupt controller’s parser) exactly as it would read a hand-built IORESOURCE_IRQ entry on a non-DT board:
int irq;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
On kernel 6.x, platform_get_irq() always returns a negative errno on failure rather than zero, so checking irq < 0 is the correct and only reliable failure check; older code that only checked if (!irq) is considered a bug today.
Original Driver Example: ep_resource_demo
The example driver in this lecture models a small memory-mapped status register block with one interrupt line, similar to a simple sensor or watchdog peripheral, sourced entirely from a device tree node.
Device Tree Node
status_ctrl0: status-controller@30000000 {
compatible = "ep,status-ctrl";
reg = <0x30000000 0x100>;
interrupts = <0 45 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
};
Driver Source
#define EP_STATUS_REG 0x00
#define EP_CONTROL_REG 0x04
struct ep_resource_dev {
struct device *dev;
void __iomem *base;
int irq;
};
static irqreturn_t ep_resource_irq_handler(int irq, void *data)
{
struct ep_resource_dev *edev = data;
u32 status = readl(edev->base + EP_STATUS_REG);
dev_info(edev->dev, "ep_resource_demo: irq fired, status=0x%08x\n", status);
writel(status, edev->base + EP_STATUS_REG); /* write-1-to-clear */
return IRQ_HANDLED;
}
static int ep_resource_probe(struct platform_device *pdev)
{
struct ep_resource_dev *edev;
struct resource *res;
int ret;
edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL);
if (!edev)
return -ENOMEM;
edev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(edev->base))
return PTR_ERR(edev->base);
edev->irq = platform_get_irq(pdev, 0);
if (edev->irq < 0)
return edev->irq;
ret = devm_request_threaded_irq(&pdev->dev, edev->irq, NULL,
ep_resource_irq_handler,
IRQF_ONESHOT, "ep_resource_demo", edev);
if (ret)
return ret;
edev->dev = &pdev->dev;
platform_set_drvdata(pdev, edev);
dev_info(&pdev->dev, "ep_resource_demo: mem 0x%pa size 0x%llx irq %d\n",
&res->start, (unsigned long long)resource_size(res), edev->irq);
return 0;
}
static const struct of_device_id ep_resource_of_ids[] = {
{ .compatible = "ep,status-ctrl" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_resource_of_ids);
static struct platform_driver ep_resource_driver = {
.driver = {
.name = "ep_resource_demo",
.of_match_table = ep_resource_of_ids,
},
.probe = ep_resource_probe,
};
module_platform_driver(ep_resource_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Platform resources from device tree demo driver");
Testing And Expected Output
Add the node shown above to your board’s device tree source, rebuild the DTB, and boot with it. Then build and load the module:
$ make -C /lib/modules/$(uname -r)/build M=$PWD modules
$ sudo insmod ep_resource_demo.ko
$ dmesg | tail -n 3
Expected probe log:
ep_resource_demo: mem 0x30000000 size 0x100 irq 77
The IRQ number printed is the Linux virtual IRQ number assigned by the interrupt controller driver, not the raw hardware line 45 written in the DT node — that translation happens automatically as part of resource resolution.
Resource Helper Comparison Table
| Helper | Returns Resource Pointer | When To Use |
|---|---|---|
| platform_get_resource() + devm_ioremap_resource() | Yes (manual) | Legacy two-step style, still common in older drivers |
| devm_platform_ioremap_resource() | No | Simplest case, resource pointer not needed afterward |
| devm_platform_get_and_ioremap_resource() | Yes | Need resource size/address for logging or further computation |
Common Mistakes And Troubleshooting
- Checking platform_get_irq() with `if (!irq)`: on kernel 6.x this function returns a negative errno on failure, never zero, so this old style check silently misses errors.
- Skipping IS_ERR() on devm_platform_ioremap_resource(): the return value is an error-encoded pointer, not NULL, on failure.
- Forgetting IRQF_ONESHOT with a NULL primary handler: threaded IRQ registration with only a thread function requires this flag or registration fails.
- Mismatched reg size in the DT node: if the size in the reg property is smaller than the registers you access, you will get a kernel warning or a crash on some platforms with strict MMIO checking.
Best Practices
- Prefer
devm_platform_ioremap_resource()or its and-get variant over the two-step legacy pattern in new code. - Always check
irq < 0, never!irq, when validatingplatform_get_irq(). - Use the devm-managed variants so resources are automatically released if probe fails partway through, avoiding manual cleanup paths.
Performance Considerations
Resource resolution from device tree happens once at boot, when the OF core populates the platform device, so there is no runtime overhead in the driver’s hot path. The mapped MMIO pointer returned by the ioremap helpers is used directly with readl()/writel() exactly as on a non-DT system.
Security Considerations
Always validate the resource size before mapping if your driver accesses register offsets beyond a fixed small block, and never trust interrupt or memory values from a device tree overlay loaded at runtime without confirming the overlay source is trusted, since a malicious overlay could describe resources that overlap unrelated hardware.
Real-World Use Cases
Nearly every memory-mapped peripheral driver in the mainline kernel tree uses this exact pattern — UART controllers, I2C/SPI host controllers, GPIO banks, and watchdog timers all read their register base address and interrupt line through platform_get_resource() and platform_get_irq(), whether the underlying platform boots from device tree or ACPI.
Summary And Key Takeaways
- Platform resources are extracted from the device tree automatically before probe runs; the driver API is the same as on non-DT systems.
devm_platform_ioremap_resource()anddevm_platform_get_and_ioremap_resource()are the modern kernel 6.x helpers for memory-mapped resources.platform_get_irq()always returns a negative errno on failure on current kernels.- devm-managed helpers avoid manual cleanup on probe failure paths.
Conclusion
Platform resources in device tree systems demonstrate one of the cleanest parts of the Linux driver model: the resource abstraction means driver code barely changes between a device tree platform and a legacy board file platform. Once you are comfortable pulling memory and interrupt resources out of a DT node this way, you have everything needed to write real memory-mapped peripheral drivers, which is where this free Linux kernel development course heads next.
Frequently Asked Questions
Does platform_get_resource() need CONFIG_OF to read device tree data?
The OF core needs CONFIG_OF to build the resource array from a DT node at device creation time, but the driver call to platform_get_resource() itself is not device-tree specific.
What is the difference between devm_platform_ioremap_resource() and devm_ioremap_resource()?
devm_platform_ioremap_resource() takes a platform_device and resource index and internally calls platform_get_resource() plus devm_ioremap_resource() for you, saving a line of code.
Why did my platform_get_irq() failure check never trigger?
You are likely checking `if (!irq)`. On kernel 6.x this function returns a negative errno, never zero, on failure, so the check must be `if (irq < 0)`.
Do I need interrupt-parent in every device tree node?
Only if the node’s interrupt-parent differs from an inherited one higher in the tree; otherwise it is inherited automatically from a parent node.
Can platform_get_resource() be used for DMA channels too?
DMA channels described in device tree are typically read through the dmaengine API using dma-names, not IORESOURCE_DMA, which is largely a legacy resource type today.
What happens if the reg property size is wrong in the DT node?
Accessing offsets beyond the declared size can trigger a kernel warning under strict MMIO region checking, or undefined behavior on hardware without memory protection for that region.
Continue The Free Linux Device Drivers Course
Explore more free lectures on device tree, platform drivers, and Linux kernel synchronization.
