Device Tree Named Resources Guide
Free Linux Kernel Development Course — Device Tree Chapter, Lecture 6
Device tree named resources solve a very real problem that every Linux device driver author eventually runs into: what happens when your driver expects a register block or an interrupt line in a specific position inside the reg or interrupts property, but the person who wrote the board’s device tree source listed them in a different order? In this lecture of our free linux kernel development course we break down device tree named resources — the reg-names, clock-names, interrupt-names and dma-names properties — and then move straight into accessing those registers safely from a modern kernel 6.x driver.
Free Embedded Linux Course Keywords
free linux development course
free linux device drivers course
free linux kernel development course
device tree named resources
What You Will Learn
- Why resource ordering in a device tree node is not guaranteed and why that matters to a driver author
- How
reg-names,clock-names,interrupt-namesanddma-namesremove that ordering risk - The exact kernel APIs used to fetch a resource by its name instead of its index
- How a driver takes ownership of a memory-mapped register block using the modern
devm_platform_ioremap_resource_byname()API - How to build, load and test an original character driver,
ep_res_demo, that reads two named register blocks and two named interrupt lines from a device tree overlay
Prerequisites
This lecture continues our device tree chapter directly, so you should already be comfortable with the following topics from earlier lectures in this free linux device drivers course:
- Basic device tree syntax and node naming conventions
- The
regproperty and how#address-cells/#size-cellscontrol its layout - How
platform_get_resource()reads a rawregentry by index - A working ARM64 or Raspberry Pi based Linux kernel 6.x build environment with device tree overlay support (
dtcinstalled)
Why Device Tree Named Resources Exist
A device tree is usually written by a board integrator. The driver, on the other hand, is usually written by someone else entirely — sometimes years earlier, sometimes upstream in the kernel tree itself. When a driver expects, say, two interrupt lines with the first one meant for a “data ready” event and the second for an “alarm” event, nothing in the device tree format forces the board author to list them in that exact order. If they get swapped, the driver silently wires the wrong handler to the wrong physical line, and you get a bug that is painful to trace because everything “compiles” and “loads” without error.
Device tree named resources close this gap. Instead of relying on array position, every resource entry gets a matching name in a sibling *-names property. The driver then looks resources up by that name, so the physical order inside reg or interrupts stops mattering entirely.
| Lookup Style | Driver Call | Breaks If Order Changes? |
|---|---|---|
| Index based | platform_get_resource(pdev, IORESOURCE_MEM, 0) |
Yes |
| Name based | platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl") |
No |
The Four Named Resource Properties
The kernel understands four sibling *-names properties, one for each resource type a device node commonly carries:
| Property | Names Entries In | Driver API |
|---|---|---|
reg-names |
reg |
platform_get_resource_byname(), devm_platform_ioremap_resource_byname() |
clock-names |
clocks |
devm_clk_get() |
interrupt-names |
interrupts |
platform_get_irq_byname() |
dma-names |
dmas |
dma_request_chan() |
Each name in a *-names list lines up positionally with the entry at the same index in its matching property. The name itself carries the meaning; the index becomes an implementation detail the driver never has to reason about.
An Original Device Tree Overlay Example
Instead of reusing the classic textbook fake device, let us design our own: a small sensor-hub style peripheral called ep,sensor-hub with two register blocks (a control block and a data block) and two interrupt lines (a data-ready line and an alarm line). Here is the overlay fragment for a Raspberry Pi class board:
/dts-v1/;
/plugin/;
& {
fragment@0 {
target-path = "/";
__overlay__ {
ep_sensor_hub: sensor-hub@7e100000 {
compatible = "ep,sensor-hub";
reg = <0x7e100000 0x100>, <0x7e100100 0x100>;
reg-names = "ctrl", "data";
interrupt-parent = <&gic>;
interrupts = <0 84 4>, <0 85 4>;
interrupt-names = "data-ready", "alarm";
status = "okay";
};
};
};
};
Notice that ctrl is listed before data in reg, and data-ready is listed before alarm in interrupts. If a future board revision swaps these around, our driver below will not care at all — that is the entire point of device tree named resources.
Accessing Registers The Modern Way
Older driver code you may find online, including in some print books, takes registers ownership in two separate steps: first call platform_get_resource() to find the memory region, then call devm_ioremap_resource() to map it. That pattern still works on kernel 6.x, but current upstream code increasingly prefers the combined helper devm_platform_ioremap_resource(), and its named variant devm_platform_ioremap_resource_byname(), which do both steps in a single call and take the platform device directly instead of a resource pointer.
| Style | Calls Needed | Recommended On Kernel 6.x |
|---|---|---|
| Two-step legacy | platform_get_resource() + devm_ioremap_resource() |
Still valid, more verbose |
| One-step modern | devm_platform_ioremap_resource_byname() |
Preferred for new drivers |
Hands-On Driver: ep_res_demo
Let us write an original character driver that binds to our ep,sensor-hub node, maps both named register blocks with the modern one-step API, and looks up both named interrupt lines with platform_get_irq_byname(). Every identifier below is our own; nothing is copied from any book or reference driver.
// ep_res_demo.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/interrupt.h>
struct ep_res_demo_priv {
void __iomem *ctrl_base;
void __iomem *data_base;
int data_ready_irq;
int alarm_irq;
};
static irqreturn_t ep_res_demo_isr(int irq, void *dev_id)
{
struct ep_res_demo_priv *priv = dev_id;
if (irq == priv->data_ready_irq)
pr_info("ep_res_demo: data-ready interrupt fired\n");
else if (irq == priv->alarm_irq)
pr_info("ep_res_demo: alarm interrupt fired\n");
return IRQ_HANDLED;
}
static int ep_res_demo_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ep_res_demo_priv *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Named register blocks - order in the DT no longer matters */
priv->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "ctrl");
if (IS_ERR(priv->ctrl_base))
return dev_err_probe(dev, PTR_ERR(priv->ctrl_base),
"failed to map ctrl block\n");
priv->data_base = devm_platform_ioremap_resource_byname(pdev, "data");
if (IS_ERR(priv->data_base))
return dev_err_probe(dev, PTR_ERR(priv->data_base),
"failed to map data block\n");
/* Named interrupt lines - order in the DT no longer matters */
priv->data_ready_irq = platform_get_irq_byname(pdev, "data-ready");
if (priv->data_ready_irq < 0)
return priv->data_ready_irq;
priv->alarm_irq = platform_get_irq_byname(pdev, "alarm");
if (priv->alarm_irq < 0)
return priv->alarm_irq;
ret = devm_request_irq(dev, priv->data_ready_irq, ep_res_demo_isr,
0, "ep_res_demo-data-ready", priv);
if (ret)
return ret;
ret = devm_request_irq(dev, priv->alarm_irq, ep_res_demo_isr,
0, "ep_res_demo-alarm", priv);
if (ret)
return ret;
platform_set_drvdata(pdev, priv);
dev_info(dev, "ep_res_demo: ctrl at %p, data at %p, irqs %d/%d\n",
priv->ctrl_base, priv->data_base,
priv->data_ready_irq, priv->alarm_irq);
return 0;
}
static void ep_res_demo_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "ep_res_demo: removed\n");
}
static const struct of_device_id ep_res_demo_of_match[] = {
{ .compatible = "ep,sensor-hub" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_res_demo_of_match);
static struct platform_driver ep_res_demo_driver = {
.probe = ep_res_demo_probe,
.remove = ep_res_demo_remove,
.driver = {
.name = "ep_res_demo",
.of_match_table = ep_res_demo_of_match,
},
};
module_platform_driver(ep_res_demo_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Named resource demo driver for free linux kernel course");
Two details worth calling out:
ep_res_demo_probe()uses the void-returningremove()callback, which has been the expected signature on kernel 6.11 and later. If you are on an older 6.x kernel, anint-returningremove()still works.dev_err_probe()is used instead of a manualdev_err()+ return pattern — it handles the common-EPROBE_DEFERcase cleanly and is the current best practice for probe error paths.
Building And Running The Driver
Compile ep_res_demo.c as an out-of-tree module against your running kernel headers:
cat > Makefile << 'EOF'
obj-m += ep_res_demo.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
EOF
make
Compile the overlay and load both the overlay and the module:
dtc -@ -I dts -O dtb -o ep-sensor-hub.dtbo ep-sensor-hub-overlay.dts
sudo cp ep-sensor-hub.dtbo /boot/overlays/
sudo dtoverlay ep-sensor-hub
sudo insmod ep_res_demo.ko
dmesg | tail -n 5
Expected output in dmesg once the overlay binds and the module loads:
ep_res_demo ep_sensor_hub: ep_res_demo: ctrl at 00000000a1b2c3d4, data at 00000000e5f60718, irqs 189/190
The exact pointer values and IRQ numbers will differ on your board, but seeing both named register blocks map cleanly and both named IRQs resolve to valid numbers confirms that named resource lookup is working end to end.
Common Mistakes And Troubleshooting
| Mistake | Symptom | Fix |
|---|---|---|
Name in *-names does not exactly match the name the driver requests |
platform_get_resource_byname() returns NULL, ioremap helper returns -EINVAL |
Compare strings character by character; names are case sensitive |
Number of entries in *-names does not match its sibling property |
Overlay fails to apply, or lookup silently returns the wrong entry | Keep the counts identical; verify with dtc before deploying |
| Mixing index based and name based lookups for the same resource | Works on one board, breaks on another with reordered DT | Pick named lookup consistently once the property exists |
Best Practices
- Prefer named resources over index based lookup for any device node with more than one entry of the same resource type
- Always check the return value of every
*_byname()call — a missing or misspelled name is a silent bug otherwise - Use
dev_err_probe()in probe failure paths so deferred probing is handled correctly - Keep resource names short, lower-case, and hyphen separated to match kernel convention
Performance And Security Considerations
Named resource lookup does a string comparison during probe, which runs once per device and has no measurable runtime cost on a running system. On the security side, treat the device tree itself as trusted input coming from firmware or the bootloader — a driver should still validate resource sizes before assuming a register offset is safe to write, since a malformed or malicious overlay could otherwise describe a memory region smaller than the driver expects.
Summary And Key Takeaways
- Device tree named resources remove ordering assumptions from
reg,clocks,interrupts, anddmasproperties - The four
*-namesproperties arereg-names,clock-names,interrupt-names, anddma-names - Modern kernel 6.x drivers should prefer
devm_platform_ioremap_resource_byname()over the older two-step ioremap pattern - Our original
ep_res_demodriver demonstrates named register and named interrupt lookup end to end on a device tree overlay
Conclusion
Device tree named resources are a small property addition with an outsized payoff: they decouple a driver’s correctness from the exact order a board integrator happened to list resources in. Once you start writing named reg, interrupt, clock, and dma entries by default, an entire category of “works on my board, breaks on theirs” bugs simply stops occurring. In the next lecture of this free linux kernel development course we move from register access into interrupt handling in full depth — the interrupt-controller property, #interrupt-cells, and how a consumer node like ours actually gets routed to the right IRQ line by the kernel’s interrupt subsystem.
Frequently Asked Questions
What is the purpose of device tree named resources?
They let a driver fetch a register block, interrupt line, clock, or DMA channel by a fixed name instead of a positional index, so the physical order those entries appear in inside the device tree no longer affects driver correctness.
Do I have to provide reg-names for every device node?
No. Named resources are only necessary when a node exposes more than one entry of the same resource type. A node with a single register block or a single interrupt line has no ordering ambiguity to solve.
Is devm_ioremap_resource() deprecated on kernel 6.x?
No, it still works. However current upstream drivers increasingly use the combined helpers devm_platform_ioremap_resource() and devm_platform_ioremap_resource_byname(), which fold the resource lookup and the ioremap call into one step.
What happens if I misspell a name in interrupt-names?
platform_get_irq_byname() will fail to find a match and return a negative error code. Your probe function should always check this return value and bail out cleanly rather than passing a negative number into request_irq().
Can reg-names and interrupt-names be used on the same node?
Yes. A single device node commonly carries both reg-names and interrupt-names at once, exactly as shown in the ep,sensor-hub example in this lecture.
Does the order of entries in reg-names have to match reg?
Yes. Each name in a *-names property is matched to the entry at the same index in its sibling property. The names remove the need for the driver to know that order, but the device tree author must still keep the two lists aligned with each other.
Which kernel header declares platform_get_resource_byname?
It is declared in linux/platform_device.h, the same header that declares platform_get_resource() and the platform_driver structure used throughout this free linux device drivers course.
Continue The Free Linux Kernel Development Course
Next up: Device Tree Interrupt Handling — interrupt-controller, #interrupt-cells, and writing an original IRQ-driven driver.
