Skip to main content

How to Write Device Tree Overlays

Introduction

This article aims to guide you through the process of writing and deploying Device Tree Overlays suitable for your customized hardware and peripherals.

Prerequisites

How to Write an Overlay

Pin Definitions Header

Each SoC has its own pin definitions file, as you can see on the related Pinmuxing Guide. This file lists each pin with all of its possible muxing options. It contains definitions in a pattern comprising the ball name prefix and the muxing option suffix.

As an example, MX6QDL_PAD_SD2_DAT1__SD2_DATA1, comprises:

  • The ball name: MX6QDL_PAD_SD2_DAT1
  • The pin mux option: SD2_DATA1

Pin Control Node

Here we have the actual pin control node that will add the chosen pins as GPIOs. In the template file, we defined the node my_muxgrp and its label as pinctrl_my_pins. Keep in mind that these are arbitrary names, and you can choose any name according to your preference. But be careful: not all characters are allowed, as it's explained at Device Tree Specifications.

    &iomuxc {
...
pinctrl_my_pins: my_muxgrp {
fsl,pins = <
PIN_NAME_PIN_MUX PAD_CTRL // PINS
>;
};
};

Depending on the SoM, you might have to put the pin control node inside another node named after the SoM, like apalis-imx8qm. Check how pin control nodes are declared inside iomuxc in the unaltered Device Tree and use that as reference.

    &iomuxc {
...
name-of-som {
pinctrl_my_pins: my_muxgrp {
fsl,pins = <
PIN_NAME_PIN_MUX PAD_CTRL // PINS
>;
};
};
};

Adding the Pins

Finally, we come to the pin functions declaration themselves. The PIN_NAME_PIN_MUX field should be in the format <name prefix>_<muxing option suffix> or <name prefix>__<muxing option suffix> (depending on the SoC) as explained earlier, and that will directly reference the speficif header file. The PAD_CTRL is a hexadecimal value to set the Pad Control register of the SoC, which you can find related information on the Pinmuxing Guides

Referencing the Pin Control Node

A pin control node/group alone has limited functionality. However, other nodes can make use of its pins by referencing it, which effectively applies functions to the pins. When referencing a pin group, there are typically two available options:

  1. IOMUXC node: Setting the value of pinctrl-0 in iomuxc with a pin control node indicates that the pins inside it will be available in userspace, making it possible to use them as GPIOs. <&pinctrl-originally-in-device-tree> represents one or more pin control nodes that were referenced in the unaltered device tree, and it's usually good to keep them in the overlay, as they would be otherwise dereferenced.

    ...
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl-originally-in-device-tree>, <&pinctrl_my_pins>;
    };
  2. Peripherals: Similarly, setting pinctrl-0 in a peripheral node with a pin control group makes the pins in it available to the peripheral. While you can reference the same pin control group on multiple nodes, be sure to only have one of them enabled in the device tree to avoid pin conflicts.

    &genericPeripheral {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_my_pins>; // Pin group used in genericPeripheral
    status = "okay";

This section aims to guide you on how to use a device tree overlay to modify the device tree of a Verdin iMX8M Mini with a Verdin Development Board, multiplexing pins to allow a LED blinking. The custom-overlay.dts will be written from scratch, and each step will be carefully explained.

  1. Device tree overlays are built standalone, without any connection to the device tree they will modify. For this reason, they must include headers and specifiers just like a regular device tree file.

    custom-overlay.dts
    /* Custom Device Tree Overlay for LED blinking */

    /dts-v1/;
    /plugin/;

    #include <dt-bindings/gpio/gpio.h>
    #include <dt-bindings/leds/common.h>
    #include "imx8mm-pinfunc.h"

    As it can be seen, we have tree includes:

    • dt-bindings/gpio/gpio.h: Define macros for GPIO bindings.
    • dt-bindings/leds/common.h: Define macros for the common LEDs device tree bindings.
    • imx8mm-pinfunc.h: Define macros for pins and its possible muxings according to SoC's Technical Reference Manual through IOMUXC driver.
  2. Define the hardware compatibility using the compatible property. This will make the overlay modify the right device tree source file.

    custom-overlay.dts
    / {
    compatible = "toradex,verdin-imx8mm";
    };
  3. Define which nodes you want to enable or disable through the property status. In this case, as we are going to disable the PWM interfaces.

    custom-overlay.dts
    &pwm1 {
    status = "disabled";
    };

    &pwm2 {
    status = "disabled";
    };
  4. Overlay, modify or create the desired node with custom configuration. In this case, we will create and add the leds node to the root node (&{/}) with custom properties in pinctrl-0, later defined by pinmuxing operations.

    custom-overlay.dts
    &{/} {
    leds {
    compatible = "gpio-leds";

    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_my_led>;

    myled_sodimm_19 {
    label = "my_led_sodimm_19";
    gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
    linux,default-trigger = "heartbeat";
    default-state = "on";
    };

    myled_sodimm_55 {
    label = "my_led_sodimm_55";
    gpios = <&gpio5 16 GPIO_ACTIVE_HIGH>;
    linux,default-trigger = "heartbeat";
    default-state = "on";
    };
    };
    };

    It can be seen that this overlay contains two child nodes myled_sodimm_19 and myled_sodimm_55, with custom properties.

  5. To perform pinmuxing operations, we need to overlay the iomuxc node and customize the multiplexing operations through fsl,pins property.

    custom-overlay.dts
    &iomuxc {
    pinctrl_my_led: myledgrp {
    fsl,pins =
    <MX8MM_IOMUXC_GPIO1_IO01_GPIO1_IO1 0x104>, // SODIMM 19 // pull-down enabled and drive strength X2
    <MX8MM_IOMUXC_I2C2_SCL_GPIO5_IO16 0x104>; // SODIMM 55 // pull-down enabled and drive strength X2
    };
    };

After all the previous steps, the custom-overlay.dts should look like this:

View custom-overlay.dts
/dts-v1/;
/plugin/;

#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>
#include "imx8mm-pinfunc.h"

/ {
compatible = "toradex,verdin-imx8mm";
};

&pwm1 {
status = "disabled";
};

&pwm2 {
status = "disabled";
};

&{/} {
leds {
compatible = "gpio-leds";

pinctrl-names = "default";
pinctrl-0 = <&pinctrl_my_led>;

myled_sodimm_19 {
label = "my_led_sodimm_19";
gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
default-state = "on";
};

myled_sodimm_55 {
label = "my_led_sodimm_55";
gpios = <&gpio5 16 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
default-state = "on";
};
};
};

&iomuxc {
/*pinctrl-0 = <&pinctrl_gpio1>, <&pinctrl_gpio2>,
<&pinctrl_gpio3>, <&pinctrl_gpio4>,
<&pinctrl_gpio7>, <&pinctrl_gpio8>,
<&pinctrl_gpio_hog1>, <&pinctrl_gpio_hog2>, <&pinctrl_gpio_hog3>,
<&pinctrl_pmic_tpm_ena>, <&pinctrl_my_led>;*/
pinctrl_my_led: myledgrp {
fsl,pins =
<MX8MM_IOMUXC_GPIO1_IO01_GPIO1_IO1 0x104>, // SODIMM 19 // pull-down enabled and drive strength X2
<MX8MM_IOMUXC_I2C2_SCL_GPIO5_IO16 0x104>; // SODIMM 55 // pull-down enabled and drive strength X2
};
};


Send Feedback!