Zephyr devicetree bindings
Sometimes, your application requires cutomizing the definitions of the board/SoC devicetree nodes. Therefore, you might find yourself needing to add some additional properties to the node definitions.
In the context of Zephyr, a devicetree binding is a YAML file, which is used to tell the build system which properties a devicetree node is allowed to have. Apart from this, a devicetree binding also serves as documentation, telling the developers which properties they’re allowed to use and providing brief documentation on said properties.
Most importantly, a devicetree binding is specific to a certain programming
model. Consequently, if you want to identify the devicetree binding for a
certain devicetree node, you’ll have to look at the value of the compatible
property.
Let’s assume we’re interested in the tpm3
node. Its definition is shown below:
/* node definition taken from nxp_mimx93_a55.dtsi */
tpm3: tpm@424e0000 {
compatible = "nxp,tpm-timer";
reg = <0x424e0000 DT_SIZE_K(64)>;
interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>;
interrupt-names = "irq_0";
interrupt-parent = <&gic>;
clocks = <&ccm IMX_CCM_TPM3_CLK 0 0>;
prescaler = <1>;
status = "disabled";
};
Looking at the value of the compatible
property, we can see that the
configured programming model is nxp,tpm-timer
.
In Zephyr, the root directory of the devicetree bindings can be found under:
~/work/repos/nxpcup_root/zephyr/dts/bindings/
with all the devicetree binding files being organized in subdirectories, one for each subsystem.
Going back to our example, assuming we don’t known the subsystem for our binding, we have two options:
Use
git grep
:
# run this inside ~/work/repos/nxpcup_root/zephyr
git grep nxp,tpm-timer -- dts/bindings
# run this inside ~\Desktop\nxpcup_root\zephyr
git grep nxp,tpm-timer -- dts\bindings
In which case, the output should be similar to:
dts/bindings/counter/nxp,tpm-timer.yaml:compatible: "nxp,tpm-timer"
What we’re interested here is finding the YAML file that contains a
compatible: <compatible_value>
line, where compatible_value
is
the value of our compatible (e.g. nxp,tpm-timer
).
To confirm that the file returned by the git grep
command is the
right one, we can look at its name. Usually, the name of the binding
matches the name of the programming model (e.g. nxp,tpm-timer
).
Use
find
:
The name of the devicetree binding will usually match the name of the
programming model. We can use this to search for the binding directly
using find
:
# run this inside ~/work/repos/nxpcup_root/zephyr
find dts/bindings -name nxp,tpm-timer.yaml
# run this inside ~\Desktop\nxpcup_root\zephyr
Get-ChildItem -Path .\dts\bindings\ -Recurse -Name "nxp,tpm-timer.yaml"
If everything went well, you should see the path to the binding file:
dts/bindings/counter/nxp,tpm-timer.yaml
Inspecting the content of a binding file
Now that we’ve indentified the devicetree binding for our programming model, it’s time to look at its content:
# taken from nxp,tpm-timer.yaml - some keys were intentionally omitted
description: NXP Timer/PWM Module (TPM) used as timer
compatible: "nxp,tpm-timer"
include: base.yaml
properties:
reg:
required: true
interrupts:
required: true
clocks:
required: true
prescaler:
type: int
required: true
enum:
- 1
- 2
- 4
- 8
- 16
- 32
- 64
- 128
description: Input clock prescaler
The description
key is used to provide additional information on the
underlying hardware. In this particular case, we’re working with a timer/PWM
module from NXP.
The compatible
key is used by the build system to identify which devicetree
nodes this binding applies to. In our example, this binding would apply to
all devicetree node with the compatible
value containing the nxp,tpm-timer
string.
The include
key is used to import property definitions from other
binding files. Here, we’re importing the content of base.yaml
,
which contains the definitions for standard properties such as
clock
, interrupts
, etc..
The properties
key is used to define properties or modify imported
ones. This is the key we’ll be most interested in as it contains the
list of properties we’re allowed to specify in our devicetree nodes.
If we’re working with imported properties (i.e. we have an include
key in our binding), then we also need to consider the properties
defined in the included binding file. For example, the compatible
property is defined in base.yaml
and imported in nxp,tpm-timer.yaml
,
which is why we’re allowed to use the compatible
property in our
tpm3
node.
In this particular case, we have 3 standard propeties (defined in base.yaml
):
reg
, interrupts
, clocks
, which are modified by our nxp,tpm-timer.yaml
binding file. By default, these properties are optional (i.e. you’re not forced to
add these properties to your devicetree node), but, by adding the required: true
line, we’re making them mandatory. Therefore, each devicetree node using the
nxp,tpm-timer
programming model must have these properties. Otherwise,
building your application will fail.
Apart from the standard properties, we also have the definition of a non-standard
property: prescaler
. As you can probably deduce from its definition, this
property takes an int
from the {1, 2, 4, 8, 16, 32, 64, 128}
set as
its value and is mandatory.
Further reading
For more information, check out the following documentation pages from Zephyr: