Device Trees, overlays, and parameters

Raspberry Pi's latest kernels and firmware, including Raspbian and NOOBS releases, now use a Device Tree (DT) to manage some resource allocation and module loading by default. This was implemented to alleviate the problem of multiple drivers contending for system resources, and to allow HAT modules to be auto-configured.

The current implementation is not a pure Device Tree system – there is still board support code that creates some platform devices – but the external interfaces (I2C, I2S, SPI), and the audio devices that use them, must now be instantiated using a Device Tree Blob (DTB) passed to the kernel by the loader (start.elf).

The main impact of using Device Tree is to change from everything on, relying on module blacklisting to manage contention, to everything off unless requested by the DTB. In order to continue to use external interfaces and the peripherals that attach to them, you will need to add some new settings to your config.txt. Full details are given in Part 3, but these are a few examples:

# Uncomment some or all of these lines to enable the optional hardware interfaces

# Uncomment one of these lines to enable an audio interface

# Uncomment this to enable the lirc-rpi module

# Uncomment this to override the defaults for the lirc-rpi module

Device Trees



1.1: Basic DTS syntax


The following is a simple tree in the .dts format:

/include/ "common.dtsi";

/ {
    node1 {
        a-string-property = "A string";
        cousin: child-node1 {
            second-child-property = <1>;
            a-string-property = "Hello, world";
        child-node2 {
    node2 {
        child-node1 {
            my-cousin = <&cousin>;

/node2 {

This tree contains:

  • a required header: /dts-v1/.
  • a single root node: /
  • a couple of child nodes: node1 and node2
  • several properties scattered through the tree



string-property = "a string";

Cells are 32-bit unsigned integers delimited by angle brackets:

cell-property = <0xbeef 123 0xabcd1234>;



Data of differing representations can be concatenated using a comma:


Commas are also used to create lists of strings:


1.2: An aside about /include/



/node2 {
    child-node1 {
        my-cousin = <&cousin>;


1.3: Labels and references


  1. Path strings

    Paths should be self-explanatory, by analogy with a filesystem - /soc/[email protected] is the full path to the I2S device in BCM2835 and BCM2836. Note that although it is easy to construct a path to a property (for example, /soc/[email protected]/status), the standard APIs don't do that; you first find a node, then choose properties of that node.

  2. phandles


  3. Labels


  4. Aliases


1.4: Device Tree semantics




Part 2: Device Tree overlays


But when a system like Raspberry Pi supports optional plug-in accessories, such as HATs, the problem grows. Ultimately, each possible configuration requires a Device Tree to describe it, but once you factor in different base hardware (models A, B, A+, and B+) and gadgets only requiring the use of a few GPIO pins that can coexist, the number of combinations starts to multiply rapidly.


2.1: Fragments


// Enable the i2s interface

/ {
    compatible = "brcm,bcm2708";

    [email protected] {
        target = <&i2s>;
        __overlay__ {
            status = "okay";

The compatible string identifies this as being for BCM2708, which is the base architecture of the BCM2835 part. For the BCM2836 part you could use a compatible string of "brcm,bcm2709", but unless you are targeting features of the ARM CPUs, the two architectures should be equivalent, so sticking to "brcm,bcm2708" is reasonable. Then comes the first (and in this case only) fragment. Fragments are numbered sequentially from zero. Failure to adhere to this may cause some or all of your fragments to be missed.



/ {
    compatible = "brcm,bcm2708";

&i2s {
    status = "okay";



you will get an error:

Label or path i2s not found


Trying again, this time using the original example and adding the [email protected] option to allow unresolved references:

dtc [email protected] -I dts -O dtb -o 1st.dtbo 1st-overlay.dts

If dtc returns an error about the third line, it doesn't have the extensions required for overlay work. Run sudo apt-get install device-tree-compiler and try again - this time, compilation should complete successfully. Note that a suitable compiler is also available in the kernel tree as scripts/dtc/dtc, built when the dtbs make target is used:

make ARCH=arm dtbs


$ fdtdump 1st.dtbo

// magic:           0xd00dfeed
// totalsize:       0x106 (262)
// off_dt_struct:   0x38
// off_dt_strings:  0xe8
// off_mem_rsvmap:  0x28
// version:         17
// last_comp_version:    16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x1e
// size_dt_struct:  0xb0

/ {
    compatible = "brcm,bcm2708";
    [email protected] {
        target = <0xdeadbeef>;
        __overlay__ {
            status = "okay";
    __fixups__ {
        i2s = "/[email protected]:target:0";

After the verbose description of the file structure there is our fragment. But look carefully - where we wrote &i2s it now says 0xdeadbeef, a clue that something strange has happened. After the fragment there is a new node, __fixups__. This contains a list of properties mapping the names of unresolved symbols to lists of paths to cells within the fragments that need patching with the phandle of the target node, once that target has been located. In this case, the path is to the 0xdeadbeef value of target, but fragments can contain other unresolved references which would require additional fixes.

If you write more complicated fragments, the compiler may generate two more nodes: __local_fixups__ and __symbols__. The former is required if any node in the fragments has a phandle, because the program performing the merge will have to ensure that phandle numbers are sequential and unique. However, the latter is the key to how unresolved symbols are dealt with.

Back in section 1.3 it says that "the original labels do not appear in the compiled output", but this isn't true when using the [email protected] switch. Instead, every label results in a property in the __symbols__ node, mapping a label to a path, exactly like the aliases node. In fact, the mechanism is so similar that when resolving symbols, the Raspberry Pi loader will search the "aliases" node in the absence of a __symbols__ node. This is useful because by providing sufficient aliases, we can allow an older dtc to be used to build the base DTB files.

UPDATE: The Dynamic Device Tree support in the kernel requires a different format of "local fixups" in the overlay. To avoid problems with old and new styles of overlay coexisting, and to match other users of overlays, the old "name-overlay.dtb" naming scheme has been replaced with "name.dtbo" from 4.4 onwards. Overlays should be referred to by name alone, and the firmware or utility that loads them will append the appropriate suffix. For example:

dtoverlay=awesome-overlay      # This is wrong
dtoverlay=awesome              # This is correct

2.2: Device Tree parameters



2.2.1: String parameters

String parameters are declared like this:

name = <&label>,"property";



2.2.2: Integer parameters

Integer parameters are declared like this:



2.2.3: Boolean parameters


boolean_property; // Set 'boolean_property' to true


name = <&label>,"property?";

where label and property are replaced by suitable values. Boolean parameters can cause properties to be created or deleted.

2.2.4 Overlay/fragment parameters

The DT parameter mechanism as described has a number of limitations, including the inability to change the name of a node and to write arbitrary values to arbitrary properties when a parameter is used. One way to overcome some of these limitations is to conditionally include or exclude certain fragments.


+<n>    // Enable fragment <n>
-<n>    // Disable fragment <n>


                          // otherwise disable 3, enable 4.

The i2c-mux overlay uses this technique.

2.2.5 Examples


/ {
    [email protected] {
        target-path = "/";
        __overlay__ {

            test: test_node {
                string = "hello";
                status = "disabled";
                bytes = /bits/ 8 <0x67 0x89>;
                u16s = /bits/ 16 <0xabcd 0xef01>;
                u32s = /bits/ 32 <0xfedcba98 0x76543210>;
                u64s = /bits/ 64 < 0xaaaaa5a55a5a5555 0x0000111122223333>;
                bool1; // Defaults to true
                       // bool2 defaults to false

    [email protected] {
        target-path = "/";
        __overlay__ {

    [email protected] {
        target-path = "/";
        __dormant__ {

    __overrides__ {
        string =      <&test>,"string";
        enable =      <&test>,"status";
        bool1 =       <&test>,"bool1?";
        bool2 =       <&test>,"bool2?";
        toggle1 =     <0>,"=1";
        toggle2 =     <0>,"=2";
        not1 =        <0>,"!1";
        not2 =        <0>,"!2";

2.2.6: Parameters with multiple targets


    __overrides__ {

(example taken from the w1-gpio overlay)


2.2.7: Further overlay examples

There is a growing collection of overlay source files hosted in the Raspberry Pi/Linux GitHub repository here.

Part 3: Using Device Trees on Raspberry Pi

3.1: Overlays and config.txt

On a Raspberry Pi it is the job of the loader (one of the start.elf images) to combine overlays with an appropriate base device tree, and then to pass a fully resolved Device Tree to the kernel. The base Device Trees are located alongside start.elf in the FAT partition (/boot from Linux), named bcm2708-rpi-b.dtb, bcm2708-rpi-b-plus.dtb, bcm2708-rpi-cm.dtb, and bcm2709-rpi-2-b.dtb. Note that Models A and A+ will use the "b" and "b-plus" variants, respectively. This selection is automatic, and allows the same SD card image to be used in a variety of devices.

Note that DT and ATAGs are mutually exclusive. As a result, passing a DT blob to a kernel that doesn't understand it causes a boot failure. To guard against this, the loader checks kernel images for DT-compatibility, which is marked by a trailer added by the mkknlimg utility; this can be found in the scripts directory of a recent kernel source tree. Any kernel without a trailer is assumed to be non-DT-capable.

A kernel built from the rpi-4.4.y tree (and later) will not function without a DTB, so from the 4.4 releases onwards, any kernel without a trailer is assumed to be DT-capable. You can override this by adding a trailer without the DTOK flag or by putting device_tree= in config.txt, but don't be surprised if it doesn't work. N.B. A corollary to this is that if the kernel has a trailer indicating DT capability then device_tree= will be ignored.

The loader now supports builds using bcm2835_defconfig, which selects the upstreamed BCM2835 support. This configuration will cause bcm2835-rpi-b.dtb and bcm2835-rpi-b-plus.dtb to be built. If these files are copied with the kernel, and if the kernel has been tagged by a recent mkknlimg, then the loader will attempt to load one of those DTBs by default.

In order to manage Device Tree and overlays, the loader supports a number of new config.txt directives:


This will cause the loader to look for overlays/acme-board.dtbo in the firmware partition, which Raspbian mounts on /boot. It will then search for parameters foo and level, and assign the indicated values to them.

The loader will also search for an attached HAT with a programmed EEPROM, and load the supporting overlay from there; this happens without any user intervention.


  2. Some time later, there may also be another kernel message saying "No ATAGs?" - this is expected.


The flipside is that because platform devices don't get created unless requested by the DTB, it should no longer be necessary to blacklist modules that used to be loaded as a result of platform devices defined in the board support code. In fact, current Raspbian images ship without a blacklist file.

3.2: DT parameters

As described above, DT parameters are a convenient way to make small changes to a device's configuration. The current base DTBs support parameters for enabling and controlling the onboard audio, I2C, I2S and SPI interfaces without using dedicated overlays. In use, parameters look like this:


Note that multiple assignments can be placed on the same line, but ensure you don't exceed the 80-character limit.

A future default config.txt may contain a section like this:




or appended to the overlay line like this:





3.3: Board-specific labels and parameters


To make it possible to use one set of overlays and parameters with all Pis, the firmware creates some board-specific DT parameters. These are:


These are aliases for i2c0, i2c1, i2c0_baudrate, and i2c1_baudrate. It is recommended that you only use i2c_vc and i2c_vc_baudrate if you really need to - for example, if you are programming a HAT EEPROM. Enabling i2c_vc can stop the Pi Camera being detected.


[email protected] {
    target = <&i2c_arm>;
    __overlay__ {
        status = "okay";


3.4: HATs and Device Tree

A Raspberry Pi HAT is an add-on board for a "Plus"-shaped (A+, B+ or Pi 2 B) Raspberry Pi with an embedded EEPROM. The EEPROM includes any DT overlay required to enable the board, and this overlay can also expose parameters.


3.5: Dynamic Device Tree



There are some new commands for managing overlays:

3.5.1 The dtoverlay command


[email protected] ~ $ dtoverlay -h
                           Add an overlay (with parameters)
  dtoverlay -l             List active overlays/params
  dtoverlay -a             List all overlays (marking the active)
  dtoverlay -h             Show this usage message
  dtoverlay -h <overlay>   Display help on an overlay
Options applicable to most variants:
    -d <dir>    Specify an alternate location for the overlays
    -n          Dry run - show what would be executed
    -v          Verbose operation


Two points to note:


  2. Only overlays and parameters applied at run-time can be unloaded - an overlay or parameter applied by the firmware becomes "baked in" such that it won't be listed by dtoverlay and can't be removed.

3.5.2 The dtparam command




3.5.3 Guidelines for writing runtime-capable overlays





3.5.4 Caveats







  • Adding clocks under the /clocks node at run-time doesn't cause a new clock provider to be registered, so devm_clk_get will fail for a clock created in an overlay.

3.6: Supported overlays and parameters


Part 4: Troubleshooting and pro tips

4.1: Debugging

The loader will skip over missing overlays and bad parameters, but if there are serious errors, such as a missing or corrupt base DTB or a failed overlay merge, then the loader will fall back to a non-DT boot. If this happens, or if your settings don't behave as you expect, it is worth checking for warnings or errors from the loader:

sudo vcdbg log msg


If the kernel fails to come up in DT mode, this is probably because the kernel image does not have a valid trailer. Use knlinfo to check for one, and the mkknlimg utility to add one. Both utilities are included in the scripts directory of current Raspberry Pi kernel source trees.


dtc -I fs /proc/device-tree


If kernel modules don't load as expected, check that they aren't blacklisted in /etc/modprobe.d/raspi-blacklist.conf; blacklisting shouldn't be necessary when using Device Tree. If that shows nothing untoward, you can also check that the module is exporting the correct aliases by searching /lib/modules/<version>/modules.alias for the compatible value. Otherwise, your driver is probably missing either:

.of_match_table = xxx_of_match,


MODULE_DEVICE_TABLE(of, xxx_of_match);

Failing that, depmod has failed or the updated modules haven't been installed on the target filesystem.

4.2: Testing overlays using dtmerge and dtdiff





b) copy it from the source DTBs in /boot. This won't include overlays and parameters, but it also won't include any other modifications by the firmware. To allow testing of all overlays, the dtmerge utility will create some of the board-specific aliases ("i2c_arm", etc.), but this means that the result of a merge will include more differences from the original DTB than you might expect. The solution to this is to use dtmerge to make the copy:


(the - indicates an absent overlay name).

You can now try applying an overlay or parameter:

dtmerge base.dtb merged.dtb - sd_overclock=62
dtdiff base.dtb merged.dtb

which will return:

@@ -594,7 +594,7 @@

                [email protected] {
-                       brcm,overclock-50 = <0x0>;
+                       brcm,overclock-50 = <0x3e>;
                        brcm,pio-limit = <0x1>;
                        bus-width = <0x4>;
                        clocks = <0x8>;

You can also compare different overlays or parameters.

dtdiff merged1.dtb merged2.dtb

to get:

@@ -453,7 +453,7 @@

                        spi1_cs_pins {
                                brcm,function = <0x1>;
-                               brcm,pins = <0x12>;
+                               brcm,pins = <0x12 0x11>;
                                phandle = <0x3e>;

@@ -725,7 +725,7 @@
                        #size-cells = <0x0>;
                        clocks = <0x13 0x1>;
                        compatible = "brcm,bcm2835-aux-spi";
-                       cs-gpios = <0xc 0x12 0x1>;
                        interrupts = <0x1 0x1d>;
                        linux,phandle = <0x30>;
                        phandle = <0x30>;
@@ -743,6 +743,16 @@
                                spi-max-frequency = <0x7a120>;
                                status = "okay";
+                       [email protected] {
+                               #address-cells = <0x1>;
+                               #size-cells = <0x0>;
+                               compatible = "spidev";
+                               phandle = <0x41>;
+                               reg = <0x1>;
+                               spi-max-frequency = <0x7a120>;
+                               status = "okay";
+                       };

                [email protected] {

4.3: Forcing a specific Device Tree

If you have very specific needs that aren't supported by the default DTBs (in particular, people experimenting with the pure-DT approach used by the ARCH_BCM2835 project), or if you just want to experiment with writing your own DTs, you can tell the loader to load an alternate DTB file like this:


4.4: Disabling Device Tree usage

Since the switch to the 4.4 kernel and the use of more upstream drivers, Device Tree usage is required in Pi kernels. The method of disabling DT usage is to add:


to config.txt. However, if the kernel has a mkknlimg trailer indicating DT capability then this directive will be ignored.

4.5: Shortcuts and syntax variants

The loader understands a few shortcuts:


can be shortened to:



The loader used to accept the use of whitespace and colons as separators, but support for these has been discontinued for simplicity and so they can be used within parameter values without quotes.

4.6: Other DT commands available in config.txt



dtdebug If non-zero, turn on some extra logging for the firmware's device tree processing.

enable_uart Enable the primary/console UART (ttyS0 on a Pi 3, ttyAMA0 otherwise - unless swapped with an overlay such as pi3-miniuart-bt). If the primary UART is ttyAMA0 then enable_uart defaults to 1 (enabled), otherwise it defaults to 0 (disabled). This is because it is necessary to stop the core frequency from changing which would make ttyS0 unusable, so enable_uart=1 implies core_freq=250 (unless force_turbo=1). In some cases this is a performance hit, so it is off by default. More details on UARTs can be found here

