Device Tree Sub-Nodes And Matching
Free Linux Kernel Development Course — Device Tree Chapter, Lecture 9
Device tree sub-nodes let a single hardware entry describe several related child components without inventing a new top-level device for each one. In this lecture of our free Linux kernel development course, we walk through device tree sub-nodes using an original example driver that models a flash memory device split into two partitions, then we close the loop by revisiting how the kernel actually matches a device tree node to the correct driver in the first place.
Keywords Covered In This Lecture
for_each_child_of_node
of_device_id compatible matching
free linux kernel development course
What You Will Learn
- Why device tree sub-nodes exist and when to use them instead of separate top-level nodes
- How to walk device tree sub-nodes safely with for_each_child_of_node() on kernel 6.x
- How to build an original driver that parses two sub-node partitions from one parent node
- How the kernel’s platform match logic connects a device tree node to a driver’s probe() function
Prerequisites
- Lecture 8 of this chapter: Device Tree Custom Properties
- Comfort building and loading a platform driver module
- A kernel 6.x build environment or QEMU/Raspberry Pi target
Why Use Device Tree Sub-Nodes
A single physical chip sometimes exposes several logical components. A flash memory chip is one device but may hold several independent partitions. Rather than declaring a separate top-level node — and a separate compatible match — for each partition, the device tree format lets you nest child nodes inside the parent. These device tree sub-nodes inherit the parent’s addressing context and are walked by the parent driver during probe, not matched independently by the platform bus.
Parent Node With Two Device Tree Sub-Nodes
| flash@0 | compatible = “ep,partition-demo”; |
| reg = <0x0>; | |
| partition-a | label=”private”; offset=<0>; size=<1024>; |
| partition-b | label=”data”; offset=<1024>; size=<64512>; read-only; |
Walking Device Tree Sub-Nodes With for_each_child_of_node
The kernel provides for_each_child_of_node() as a safe iterator over a node’s children. It automatically takes and drops a reference count on each child, so you must not call of_node_put() yourself inside a normal iteration — the macro does it for you between steps. If you break out of the loop early, you are responsible for calling of_node_put() on the node you stopped at.
struct device_node *child;
for_each_child_of_node(np, child) {
const char *label;
u32 offset, size;
of_property_read_string(child, "label", &label);
of_property_read_u32(child, "offset", &offset);
of_property_read_u32(child, "size", &size);
dev_info(dev, "partition %s: offset=%u size=%u\n",
label, offset, size);
}
Complete Original Driver: ep_partition_demo
This original driver, written for kernel 6.x, models a flash-style device with two device tree sub-nodes and stores the parsed information in a small array for later use by the driver.
// ep_partition_demo.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
struct ep_partition {
const char *label;
u32 offset;
u32 size;
bool read_only;
};
static int ep_partition_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *child;
struct ep_partition parts[4];
int count = 0;
for_each_child_of_node(np, child) {
if (count >= 4) {
of_node_put(child);
break;
}
of_property_read_string(child, "label", &parts[count].label);
of_property_read_u32(child, "offset", &parts[count].offset);
of_property_read_u32(child, "size", &parts[count].size);
parts[count].read_only = of_property_read_bool(child, "read-only");
dev_info(dev, "partition[%d] %s offset=%u size=%u ro=%d\n",
count, parts[count].label, parts[count].offset,
parts[count].size, parts[count].read_only);
count++;
}
dev_info(dev, "ep_partition_demo: %d partitions parsed\n", count);
return 0;
}
static void ep_partition_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "ep_partition_demo removed\n");
}
static const struct of_device_id ep_partition_ids[] = {
{ .compatible = "ep,partition-demo" },
{ }
};
MODULE_DEVICE_TABLE(of, ep_partition_ids);
static struct platform_driver ep_partition_driver = {
.probe = ep_partition_probe,
.remove = ep_partition_remove,
.driver = {
.name = "ep_partition_demo",
.of_match_table = ep_partition_ids,
},
};
module_platform_driver(ep_partition_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Original demo driver for device tree sub-nodes");
Matching Device Tree Node With Sub-Nodes
flash@0 {
compatible = "ep,partition-demo";
reg = <0x0>;
partition-a {
label = "private";
offset = <0>;
size = <1024>;
read-only;
};
partition-b {
label = "data";
offset = <1024>;
size = <64512>;
};
};
Build, Load, And Expected Output
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_partition_demo.ko
dmesg | tail -5
ep_partition_demo flash@0: partition[0] private offset=0 size=1024 ro=1
ep_partition_demo flash@0: partition[1] data offset=1024 size=64512 ro=0
ep_partition_demo flash@0: ep_partition_demo: 2 partitions parsed
How Device Tree Sub-Nodes Get Matched To A Driver
It is worth restating clearly: device tree sub-nodes are not matched to a driver on their own. Only the parent node is matched by the platform bus, using the compatible string and the driver’s of_device_id table. Once probe() runs for the parent, the driver — and only the driver — decides what to do with its sub-nodes. This is why partition-a and partition-b in the example above carry no compatible property of their own; they are private data structures for the parent driver, walked with for_each_child_of_node().
Match Path: Device Tree Node To Driver
| Device tree node | compatible = “ep,partition-demo” |
| Platform bus | scans registered of_device_id tables |
| Driver match | of_device_id.compatible string equal |
| probe() runs | driver walks its own device tree sub-nodes |
If you need a quick refresher on the full compatible / ACPI / id_table / name matching order, that is covered in depth in the Platform Device Drivers chapter of this free linux device drivers course — the mechanism does not change here, sub-nodes simply never participate in it directly.
Common Mistakes With Device Tree Sub-Nodes
| Mistake | Symptom | Fix |
|---|---|---|
| Adding a compatible property to a sub-node expecting it to be matched separately | Sub-node is silently ignored by the platform bus | Only the parent node is matched; parse sub-nodes manually in probe() |
| Calling of_node_put() inside a normal for_each_child_of_node() loop | Reference count underflow, use-after-free warnings | Let the macro manage reference counts; only call of_node_put() if you break early |
| Assuming sub-node order in the DTS matches probe order | Wrong partition data reported | Match on a distinguishing property such as label, not array position |
Best Practices For Device Tree Sub-Nodes
- Use device tree sub-nodes only for components that are always owned and probed by the same parent driver
- Give every sub-node a label or name property so the driver never has to depend on ordering
- Bound your sub-node count in the driver to avoid overrunning a fixed-size array
Real-World Use Cases
Flash partition maps, multi-channel ADC or DAC chips, and GPIO expander pin banks are all commonly modeled with device tree sub-nodes, letting one physical chip’s driver manage several logical resources from a single probe() call.
Summary And Key Takeaways
- Device tree sub-nodes describe child components that belong to one parent hardware device
- for_each_child_of_node() safely walks device tree sub-nodes, managing reference counts automatically
- Only the parent node participates in compatible-string matching; sub-nodes are parsed manually
Conclusion
Device tree sub-nodes complete the picture for hardware that is logically one chip but functionally several components. Combined with the custom properties covered in the previous lecture, you now have the tools to describe almost any real device tree binding you will meet in production kernel work. This closes out the property-parsing portion of the Device Tree chapter in this free linux kernel development course.
Frequently Asked Questions
What is a device tree sub-node?
It is a child node nested inside a parent device tree node, typically used to describe a logical component that is owned and parsed entirely by the parent’s driver.
Are device tree sub-nodes matched to drivers independently?
No. Only the parent node is matched by the platform bus using its compatible property. Sub-nodes are read manually by the parent driver’s probe() function.
Do I need to call of_node_put() inside for_each_child_of_node()?
No, the macro manages reference counting automatically during normal iteration. You only need to call of_node_put() if you break out of the loop before it finishes.
Can device tree sub-nodes have their own reg property?
Yes, if the parent node defines #address-cells and #size-cells for its children, but for driver-private sub-nodes like the partition example, a plain offset/size property pair is often simpler.
How many device tree sub-nodes can one parent have?
There is no hard kernel limit, but your driver should always bound how many it processes to avoid overrunning a fixed-size buffer.
What is the difference between of_device_id matching and sub-node parsing?
of_device_id matching happens once, at the platform bus level, to select which driver’s probe() runs. Sub-node parsing happens afterward, entirely inside that driver.
Continue The Free Linux Device Drivers Course
Explore more free embedded systems course and free linux development course lectures at EmbeddedPathashala.
