Device Tree Overlay

Device Tree Overlay

Device Tree Intro

In the embedded system various busses can be used to connect peripherals to the CPU (directly or indirectly): system/platform, I2C, SPI, UART, USB, PCI, etc.

Some of these busses support automatic detection and enumeration of devices, which allows system to “recognize” the device that is connected and use it without a manual intervention.

However, many busses (like platform, I2C, SPI) do not have those capabilities, so there has to be a way to provide that information to the operating system, so those peripherals can be used.

In the x86 world, the BIOS contains the ACPI (Advanced COnfiguration and Power Interface) which provides that information to the OS. However, the ACPI is not used in the ARM world (there are some examples for the Aarch64 but not yet widespread).

Looking at the Linux kernel, in the beginning the hardware specifics for ARM systems were hardcoded in the kernel itself. This meant that for every board variant new C files had to be added.

This led to both kernel increasing in size and LOC, but also meant that a different kernel binary had to be used for each different variant of the board, which is a huge overhead.

That is where Device Tree (mandatory for ARM platforms since 2012) comes in handy.

The Device Tree description is used to provide the information about the hardware layout in a hierarchical form, from processor configuration, over all system/platform busses and peripherals, to individual I2C and SPI components, and more. The Device Tree Source (dts) file is compiled into a Device Tree Blob (dtb) using the Device Tree Compiler (dtc). The Device Tree Blob is then loaded at boot time by the bootloader and provided to the Linux kernel when starting the system.

The peripherals in the Device Tree use compatible strings to provide information to match the described peripheral with a driver that can handle peripherals with the corresponding compatible string. Also the base addresses and memory ranges are specified, clocks, reset, interrupt and DMA configuration, GPIO configuration, pin mapping and multiplexing, etc.

With the Device Tree descriptions, the same kernel can be used across multiple different variants of a board, even between different boards. For more details on the Device Tree, the Open Firmware and Device Tree kernel document can be used.

Device Tree Overlay

Sometimes it is easier to work with differences between different board descriptions, rather than having the copy-pasted device trees with small differences. This is especially interesting when working with development kits where different I2C/SPI peripherals can be used at different times, or when different pin configurations are used.

In these cases it would be more practical to be able to use a base Device Tree description, and then just modify it on-the-fly depending on the usecase before Linux starts using it.

This is where Device Tree Overlays come into play. The Device Tree Overlay is used to specify differences that need to be applied to a base Device Tree description.

Writing Device Tree Overlay

The Device Tree Overlay syntax is the same as the regular Device Tree file, except that it must have the

/plugin/;

in the start of the description.

In the example of the I2C driver for the Custom peripheral, the Device Tree patch that was used looked like

diff --git a/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts
index 787e018f6..4f4de36ec 100644
--- a/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts
+++ b/arch/arm/boot/dts/allwinner/sun4i-a10-cubieboard.dts
@@ -139,6 +139,11 @@ axp209: pmic@34 {
   reg = <0x34>;
   interrupts = <0>;
  };
+
+ i2csens: sens@36 {
+  compatible = "mistra,i2csens";
+  reg = <0x36>;
+ };
 };

 &i2c1 {

The I2C sens device was put under the i2c0 controller in the Cubieboard Device Tree description. The corresponding Device Tree Overlay would look like

// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
/*
 * Copyright 2025 Strahinja Jankovic <strahinja@mistrasolutions.com>
 */

// CubieboardNG Custom QEMU I2C driver

/dts-v1/;
/plugin/;

/ {
  compatible = "cubietech,a10-cubieboard", "allwinner,sun4i-a10";
};

&i2c0 {
  #address-cells = <1>;
  #size-cells = <0>;
  status = "okay";

  i2csens: sens@36 {
    compatible = "mistra,i2csens";
    reg = <0x36>;
  };
};

The advantages of using Device Tree Overlay instead of patching (or modifying) the Device Tree are that it is modular and the decision to use it is moved from compile time to runtime (boot time to be specific).

More details on the Device Tree Overlay can be found in the Kernel documentation notes

Enabling support for the Device Tree Overlays

In order for U-Boot to be able to handle Device Tree Overlays, the following configuration needs to be enabled

CONFIG_OF_LIBFDT_OVERLAY=y`
CONFIG_CMD_FDT=y

The first option enables the Overlay support, and the second one adds the ftd command that can be used to apply the Overlay at runtime.

Additionally, the base Device Tree and the Overlay have to be compiled with the -@ option, to enable the symbol table.

Adding to Yocto

In case Yocto is used, the following changes in the Linux kernel recipe have to be made (assuming cubieboard-ng-i2csens-overlay.dtso is placed in the correct directory)

  • Enable the -@ option by adding it to KERNEL_DTC_FLAGS
KERNEL_DTC_FLAGS += "-@"
  • Add the Device Tree Overlay source file to the SRC_URI
SRC_URI:append = " file://cubieboard-ng-i2csens-overlay.dtso;subdir=linux-${PV}/arch/arm/boot/dts/allwinner"
  • Add the Overlay Blob to the KERNEL_DEVICETREE variable
KERNEL_DEVICETREE:append = " allwinner/cubieboard-ng-i2csens-overlay.dtbo"

Applying the Device Tree Overlay

The Device Tree Overlay can be applied from U-Boot, before the Linux kernel is loaded.

The U-Boot fdt command is used to set the base Device Tree Blob loaded in the memory, then resize it so overlay can be applied and then to apply the Overlay blob (documentation).

The steps are shown in the following excerpt

# Load device tree and overlay
setenv i2c_overlay cubieboard-ng-i2csens-overlay.dtbo
load mmc 0:${rootpart} ${fdt_addr_r} boot/${fdtfile}
load mmc 0:${rootpart} ${fdtoverlay_addr_r} boot/${i2c_overlay}
fdt addr ${fdt_addr_r}
fdt resize
fdt apply ${fdtoverlay_addr_r}

# Proceed to load and start the kernel
...

After this, the Device Tree description is updated and Linux can be started.

Tip

The full list of changes needed to support the Device Tree Overlay for the I2C device driver are in the scarthgap branch of the meta-mistra repository.

Summary

The Device Tree Overlay can help support different hardware extensions without having to change the base device tree description. It is very practical when working with different configurations since the changes are modular and can be applied at boot time.

Subscribe
If you would like to get information as soon as new content is published, please subscribe to the "MistraSolutions newsletter".