Device Tree Phandles And Compiler- Free Linux Device Drivers Course

Device Tree Phandles And Compiler

Free Linux Kernel Development Course — Chapter 6, Part 3

Kernel 6.x Ready
Original Driver Code
Hands-On Lab

PREV_LEC  |  NEXT_LEC

Nodes in a Linux device tree rarely stand alone — a GPIO consumer needs to point back at a GPIO
controller, a display needs to point at its power regulator. That cross-referencing is done with a
device tree phandle, built on top of labels and aliases. This final lecture of
Chapter 6 in our free linux kernel development course covers aliases, labels, and
phandles, then walks through the device tree compiler (dtc) itself — how a
.dts source file becomes the .dtb blob the kernel actually boots from.
As always in this free linux device drivers course, the driver code is original and
tested on a current 6.x kernel.

Topics Covered In This Free Embedded Linux Course Lecture

device tree phandle
device tree alias
of_parse_phandle
device tree compiler dtc
dts dtsi dtb

What You Will Learn

  • The difference between a label, an alias, and a phandle
  • How to write a driver that follows a phandle reference into another node
  • The difference between .dts, .dtsi, and compiled .dtb files
  • How to compile and decompile device trees with dtc
  • How to inspect the live device tree from user space

Prerequisites

  • Completed Parts 1 and 2 of this chapter (device tree concept and node naming)
  • The device-tree-compiler package installed for the dtc command
  • A board or QEMU target that boots from device tree

Labels: Tagging A Node For Reference

A label is a short tag placed before a node so it can be uniquely identified from
elsewhere in the tree. The device tree compiler converts each label into a unique 32-bit
phandle value behind the scenes.

gpio1: gpio@0209c000 {
    compatible = "fsl,imx6q-gpio", "fsl,imx35-gpio";
};

node_label: nodename@reg {
    gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
};

Here gpio1 and node_label are both labels. The &gpio1
syntax works exactly like the address-of operator in C — it resolves to a phandle pointing at the
gpio1 node, which any property in any other node can then store and later follow.

Phandles: The Resolved Reference

A phandle is the actual 32-bit value the compiler generates for a label. You never
write a phandle number by hand — you write &label in the source, and dtc
resolves it to the correct phandle when it compiles the tree. In the kernel, drivers read a phandle
property and get back a pointer to the target node, ready to read its properties too.

Aliases: A Shortcut Lookup Table

Walking the entire tree to find one node by hand is wasteful. The special aliases node
acts as an index, mapping short names to full node references:

aliases {
    ethernet0 = &fec;
    gpio0 = &gpio1;
    gpio1 = &gpio2;
    mmc0 = &usdhc1;
};

Aliases are never referenced directly inside other device tree nodes — they exist purely so kernel
code and user-space tools can look a node up quickly by a stable short name, using APIs such as
of_alias_get_id(), instead of walking the whole tree.

Label, Phandle, And Alias At A Glance

Label
Source-code tag on a node, e.g. gpio1:
Phandle
Compiled 32-bit value the label resolves to
Alias
Short lookup name defined in the aliases node

Hands-On: Following A Phandle In A Driver

Let’s write an original driver that consumes a phandle — a common real-world pattern where one
device references a second device it depends on.

// ep-phandle-overlay.dts (fragment)
ep_power: power-ctrl@60 {
    compatible = "ep,demo-power";
    reg = <0x60>;
    status-flag = <1>;
};

ep_client: client@53 {
    compatible = "ep,demo-client";
    reg = <0x53>;
    ep,power-supply = &ep_power;
};
// ep_phandle_demo.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>

static int ep_phandle_demo_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct device_node *power_node;
    u32 status_flag = 0;

    power_node = of_parse_phandle(dev->of_node, "ep,power-supply", 0);
    if (!power_node) {
        dev_err(dev, "ep,power-supply phandle not found\n");
        return -ENODEV;
    }

    of_property_read_u32(power_node, "status-flag", &status_flag);

    dev_info(dev, "ep_phandle_demo: linked to %pOF, status-flag=%u\n",
             power_node, status_flag);

    of_node_put(power_node);
    return 0;
}

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

static struct platform_driver ep_phandle_demo_driver = {
    .probe = ep_phandle_demo_probe,
    .driver = {
        .name           = "ep_phandle_demo",
        .of_match_table = ep_phandle_demo_of_match,
    },
};
module_platform_driver(ep_phandle_demo_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Original device tree phandle demo");

of_parse_phandle() follows the reference and hands back the target
device_node, which you can then read properties from just like any node you probed
directly. Always pair it with of_node_put() once you’re done, since it takes a
reference count on the node.

$ dtc -@ -I dts -O dtb -o ep-phandle.dtbo ep-phandle-overlay.dts
$ sudo dtoverlay ep-phandle.dtbo
$ sudo insmod ep_phandle_demo.ko
$ dmesg | tail -2

Expected output:

[  73.552209] ep_phandle_demo client@53: ep_phandle_demo: linked to /power-ctrl@60, status-flag=1

The Device Tree Compiler And File Types

Device tree exists in three forms, and knowing which is which avoids a lot of confusion:

Form Extension Purpose
Source .dts Board-level description you or a vendor writes
Include source .dtsi SoC-level shared definitions, included into one or more .dts files — think of it like a C header
Compiled blob .dtb / .dtbo Binary the bootloader hands to the kernel; .dtbo is a compiled overlay

A .dtsi is included the same way a C header is included in a source file — never the
reverse. It holds definitions shared across every board that uses the same SoC.

Compiling And Decompiling With dtc

To compile every device tree for an entire architecture from the kernel source tree:

$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make dtbs

To compile just one board’s device tree:

$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make imx6dl-sabrelite.dtb

Given only a compiled .dtb, you can reverse the process and recover readable source:

$ dtc -I dtb -O dts imx6dl-sabrelite.dtb -o my_devicetree.dts

Inspecting The Live Device Tree At Runtime

On a modern kernel you don’t need any special config option to inspect the tree the system
actually booted with — it’s exposed automatically once CONFIG_OF is enabled:

$ ls /proc/device-tree
$ ls /sys/firmware/devicetree/base

Both paths point at the same live tree; /proc/device-tree is simply a symlink into
/sys/firmware/devicetree/base on current kernels. Note this is a modernization from
older references — a separate CONFIG_PROC_DEVICETREE option used to gate this on
legacy PowerPC kernels, but it has been removed from mainline for years and is not needed on any
current 6.x kernel.

Common Mistakes And Troubleshooting

Symptom Likely Cause Fix
of_parse_phandle() returns NULL Property name typo, or referenced label doesn’t exist in this tree Confirm the label is defined in the base tree or an included .dtsi
dtc error: “undefined reference” Used &label before that label was defined anywhere in scope Make sure the .dtsi defining the label is actually included
Kernel oops after using target node Missing of_node_put(), leading to a stale reference elsewhere Always release the node reference once you’re done reading from it
Alias not resolving in user space Looking for the alias name directly instead of via of_alias_get_id() Aliases are a kernel-side lookup mechanism, not a literal DT path

Best Practices

  • Prefer .dtsi for anything shared across boards using the same SoC — don’t duplicate it per board
  • Always pair of_parse_phandle() with of_node_put()
  • Use aliases for stable numbering (mmc0, mmc1) that user-space tools and bootloaders can rely on
  • Keep phandle-referencing properties named after what they represent, not the target node’s type

Real-World Use Case

Regulator consumers, clock consumers, and interrupt parent references in real kernel drivers all
use exactly this phandle pattern — a device pointing at another device or resource it depends on,
resolved at probe time rather than hard-coded.

Summary And Key Takeaways

  • A label tags a node in source; the compiler turns it into a phandle, a resolved 32-bit reference
  • Aliases are a lookup table for stable short names, not used directly inside other nodes
  • of_parse_phandle() lets a driver follow a reference into another node’s properties
  • .dts/.dtsi are source forms; .dtb/.dtbo are what the kernel actually boots from
  • The live tree is always inspectable at /sys/firmware/devicetree/base, no extra config needed on modern kernels

Conclusion

That completes the device tree foundation for this free linux kernel development course:
concept and data types, naming convention, and now aliases, phandles, and the compiler itself. From
here, every driver chapter that follows will lean on these same building blocks to bind real
hardware to real drivers.

Frequently Asked Questions

What is the difference between a label and a phandle?

A label is the human-readable tag you write in .dts source; a phandle is the 32-bit numeric value the compiler generates from that label for the kernel to use.

Can I write a phandle value directly instead of using &label?

You could, but it’s fragile and non-portable — always let the compiler resolve &label instead of hardcoding numbers.

Are device tree aliases used inside driver source code?

Not directly in the tree itself; kernel code looks them up through APIs like of_alias_get_id() rather than referencing them as a property value.

What is the difference between .dts and .dtsi?

.dts describes a specific board; .dtsi holds shared SoC-level definitions meant to be included into one or more .dts files, similar to a C header.

How do I decompile a .dtb back into readable source?

Run dtc -I dtb -O dts on the .dtb file and redirect the output to a .dts file.

Do I still need CONFIG_PROC_DEVICETREE to view the device tree?

No, that option is legacy and has been removed from mainline. Modern kernels expose the live tree automatically at /sys/firmware/devicetree/base, with /proc/device-tree as a symlink to it.

Why does of_parse_phandle() need an index argument?

Some phandle properties hold a list of references rather than a single one, so the index selects which entry in that list to resolve.

Continue This Free Embedded Systems Course

Chapter 6 complete — device tree concept, naming, aliases, phandles, and the compiler.

PREV_LEC  |  NEXT_LEC

 

Leave a Reply

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