mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-04 12:41:57 +00:00
'title:' was deprecated in commit 2934ee2cda
("dts: bindings: Remove
'title:' and put all info. into 'description:'"). Overlooked leftover.
Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
914 lines
34 KiB
ReStructuredText
914 lines
34 KiB
ReStructuredText
.. _device-tree:
|
|
|
|
Devicetree
|
|
##########
|
|
|
|
Zephyr uses the *devicetree* data structure to describe the hardware available
|
|
on a board, as well as its initial configuration in an application. Note that
|
|
"devicetree" -- without spaces -- is preferred to "device tree". The
|
|
`Devicetree specification`_ fully defines this data structure and its source
|
|
and binary representations.
|
|
|
|
.. _device-tree-intro:
|
|
|
|
Introduction
|
|
************
|
|
|
|
.. _Devicetree specification: https://www.devicetree.org/
|
|
|
|
This figure shows how devicetree fits into the Zephyr build system:
|
|
|
|
.. figure:: zephyr_dt_build_flow.png
|
|
:figclass: align-center
|
|
|
|
Devicetree build flow
|
|
|
|
Zephyr's build system generates C preprocessor macros from devicetree
|
|
definitions. These macros can be referenced in :ref:`device drivers
|
|
<device_drivers>` and other C code. All macro identifiers that are directly
|
|
generated by the devicetree scripts start with ``DT_*``.
|
|
|
|
Some devicetree-related identifiers also start with ``CONFIG_*``, which is the
|
|
identifier prefix used by :ref:`Kconfig <kconfig>`. This happens when
|
|
devicetree-related information is referenced from Kconfig symbol definitions,
|
|
via the :ref:`Kconfig preprocessor <kconfig-functions>`.
|
|
|
|
This differs significantly from how devicetree is used on Linux. The
|
|
Linux kernel would instead read the entire devicetree data structure in its
|
|
binary form, parsing it at runtime in order to load and initialize device
|
|
drivers. Zephyr does not work this way because the size of the devicetree
|
|
binary and associated handling code would be too large to fit comfortably on
|
|
the relatively constrained devices Zephyr supports.
|
|
|
|
As the name indicates, a devicetree is a tree. The human-readable text format
|
|
for this tree is called DTS (for devicetree source), and is defined in the
|
|
Devicetree Specification. Here is an example DTS file:
|
|
|
|
.. code-block:: none
|
|
|
|
/dts-v1/;
|
|
|
|
/ {
|
|
a-node {
|
|
subnode_label: a-sub-node {
|
|
foo = <3>;
|
|
};
|
|
};
|
|
};
|
|
|
|
This example has three nodes:
|
|
|
|
#. A root node
|
|
#. A node named ``a-node``, which is a child of the root node
|
|
#. A node named ``a-sub-node``, which is a child of ``a-node``
|
|
|
|
Nodes can be given *labels*, which are unique shorthands that can be used to
|
|
refer to the labeled node elsewhere in the devicetree. Above, ``a-sub-node``
|
|
has label ``subnode_label``.
|
|
|
|
Devicetree nodes have *paths* identifying their locations in the tree. Like
|
|
Unix file system paths, devicetree paths are strings separated by slashes
|
|
(``/``), and the root node's path is a single slash: ``/``. Otherwise, each
|
|
node's path is formed by concatenating the node's ancestors' names with the
|
|
node's own name, separated by slashes. For example, the full path to
|
|
``a-sub-node`` is ``/a-node/a-sub-node``.
|
|
|
|
Devicetree nodes can also have *properties*. Properties are name/value
|
|
pairs. The values are simple byte arrays. Node ``a-sub-node`` has a property
|
|
named ``foo``, whose value is a 32-bit big-endian unsigned integer with value
|
|
3. The size and type of ``foo``\ 's value are implied by the enclosing angle
|
|
brackets (``<`` and ``>``) in the DTS. Refer to the Devicetree Specification
|
|
for a complete list of ways to write a property value in a DTS file.
|
|
|
|
In practice, devicetree nodes correspond to some hardware, and the node
|
|
hierarchy reflects the hardware's physical layout. For example, let's consider
|
|
a board with three I2C peripherals connected to an I2C bus master on an SoC,
|
|
like this:
|
|
|
|
.. figure:: zephyr_dt_i2c_high_level.png
|
|
:alt: representation of a board with three I2C peripherals
|
|
:figclass: align-center
|
|
|
|
Nodes corresponding to the I2C bus master and each I2C peripheral would be
|
|
present in this board's devicetree. Reflecting the hardware layout, the
|
|
devicetree's peripheral nodes would be children of the bus master node. Similar
|
|
conventions exist for representing other types of hardware in devicetree.
|
|
|
|
The corresponding DTS would look something like this:
|
|
|
|
.. code-block:: none
|
|
|
|
/ {
|
|
soc {
|
|
i2c-bus-master {
|
|
i2c-peripheral-1 {
|
|
};
|
|
i2c-peripheral-2 {
|
|
};
|
|
i2c-peripheral-3 {
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
Properties are used in practice to describe or configure the hardware the node
|
|
represents. For example, an I2C peripheral's node has a property whose value is
|
|
the peripheral's address on the bus.
|
|
|
|
Here's a tree representing the same example, but with real-world node
|
|
names and properties you might see when working with I2C devices.
|
|
|
|
.. figure:: zephyr_dt_i2c_example.png
|
|
:figclass: align-center
|
|
|
|
I2C devicetree example with real-world names and properties
|
|
|
|
Above, node names -- like ``i2c@40003000`` -- are at the top of each node, with
|
|
a gray background, except for the root node, which is shown using its path
|
|
``/``. Properties are shown as ``name=value`` pairs below the node names.
|
|
|
|
Some important properties are:
|
|
|
|
compatible
|
|
Says what kind of device the node represents. The value is a
|
|
string in the format "vendor,device", like ``"avago,apds9960"``, or a
|
|
sequence of these, like ``"ti,hdc", "ti,hdc1010"``. The build system uses
|
|
the compatible property to find the right :ref:`binding <bindings>` for the
|
|
node.
|
|
|
|
label
|
|
The device's name according to Zephyr's :ref:`device_drivers`. The value
|
|
can be passed to :c:func:`device_get_binding()` to retrieve the
|
|
corresponding driver-level :ref:`struct device* <device_struct>`. This
|
|
pointer can then be passed to the correct driver API by application code to
|
|
interact with the device. For example, calling
|
|
``device_get_binding("I2C_0")`` would return a pointer to a device
|
|
structure which could be passed to :ref:`I2C API <i2c_api>` functions like
|
|
:c:func:`i2c_transfer()`. The generated C header will also contain a macro
|
|
which expands to this string.
|
|
|
|
reg
|
|
Information used to address the device. This could be a memory-mapped I/O
|
|
address range (as with ``i2c@40003000``\ 's reg property), an I2C bus
|
|
address (as with ``apds9960@39`` and its devicetree siblings), a SPI chip
|
|
select line, or some other value depending on the kind of device the node
|
|
represents.
|
|
|
|
This tree has the following DTS.
|
|
|
|
.. code-block:: none
|
|
|
|
/ {
|
|
soc {
|
|
i2c@40003000 {
|
|
compatible = "nordic,nrf-twim";
|
|
label = "I2C_0";
|
|
reg = <0x40003000 0x1000>;
|
|
|
|
apds9960@39 {
|
|
compatible = "avago,apds9960";
|
|
label = "APDS9960";
|
|
reg = <0x39>;
|
|
};
|
|
ti_hdc@43 {
|
|
compatible = "ti,hdc", "ti,hdc1010";
|
|
label = "HDC1010;
|
|
reg = <0x43>;
|
|
};
|
|
mma8652fc@1d {
|
|
compatible = "nxp,fxos8700", "nxp,mma8652fc";
|
|
label = "MMA8652FC";
|
|
reg = <0x1d>;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
Input and output files
|
|
**********************
|
|
|
|
The first figure in the :ref:`device-tree-intro` shows how devicetree fits into
|
|
the Zephyr build system. This section describes the input and output files in
|
|
more detail.
|
|
|
|
.. figure:: zephyr_dt_inputs_outputs.png
|
|
:figclass: align-center
|
|
|
|
Devicetree input (green) and output (yellow) files
|
|
|
|
DTS files usually have a :file:`.dts`, :file:`.dtsi` (*i* for *include*), or
|
|
:file:`.overlay` extension. The C preprocessor is run on all devicetree files
|
|
to expand macro references. :file:`.dts` files usually include :file:`.dtsi`
|
|
files via the C preprocessor with ``#include``.
|
|
|
|
.. note::
|
|
|
|
DTS also also has a native mechanism, ``/include/ "<filename>"``, for
|
|
including other files, though it is less commonly used.
|
|
|
|
Each board has a base devicetree, stored in the board's directory in
|
|
:file:`boards/` as :file:`<BOARD>.dts`. This base devicetree can be extended or
|
|
modified with one or more *overlays* -- DTS files with a :file:`.overlay`
|
|
extension. Overlays adapt the base devicetree for different board variants or
|
|
applications. Along with :ref:`kconfig`, this makes it possible to reconfigure
|
|
the kernel and device drivers without modifying source code.
|
|
|
|
The build system automatically picks up :file:`.overlay` files stored in
|
|
certain locations. It is also possible to explicitly list the overlays to
|
|
include, via the :makevar:`DTC_OVERLAY_FILE` CMake variable. See
|
|
:ref:`application_dt` and :ref:`important-build-vars` for details.
|
|
|
|
After running the C preprocessor, the resulting :file:`<BOARD>.dts` and
|
|
:file:`.overlay` files are combined by concatenating them, with the overlays
|
|
put last. This relies on DTS merging multiple definitions of nodes. See
|
|
:ref:`dt_k6x_example` for an example of how this works (in the context of
|
|
``.dtsi`` files, but the principle is the same for overlays). Putting the
|
|
contents of the :file:`.overlay` files last allows them to override properties
|
|
from the base devicetree, if needed.
|
|
|
|
.. note::
|
|
|
|
The preprocessed and concatenated DTS sources are stored in
|
|
:file:`zephyr/<BOARD>.dts.pre.tmp` in the build directory. Looking at this
|
|
file can be handy for debugging.
|
|
|
|
The merged devicetree, along with any :ref:`bindings <bindings>` referenced
|
|
from it, is used to generate C preprocessor macros. This is handled by the
|
|
libraries and scripts listed below, located in :zephyr_file:`scripts/dts/`.
|
|
Note that the source code has extensive comments and documentation.
|
|
|
|
:zephyr_file:`dtlib.py <scripts/dts/dtlib.py>`
|
|
A low-level DTS parsing library
|
|
|
|
:zephyr_file:`edtlib.py <scripts/dts/edtlib.py>`
|
|
A library layered on top of dtlib that uses bindings to interpret
|
|
properties and give a higher-level view of the devicetree. Uses dtlib to do
|
|
the DTS parsing.
|
|
|
|
:zephyr_file:`gen_defines.py <scripts/dts/gen_defines.py>`
|
|
A script that uses edtlib to generate C preprocessor macros from the
|
|
devicetree and bindings.
|
|
|
|
The output from :file:`gen_defines.py` is stored in
|
|
:file:`include/generated/generated_dts_board_unfixed.h` in the build directory.
|
|
|
|
.. note::
|
|
|
|
In addition to the Python code above, the standard ``dtc`` DTS compiler is
|
|
also run on the devicetree. This is just to catch any errors or warnings it
|
|
generates. The output is unused.
|
|
|
|
Most devices currently use :file:`dts_fixup.h` files that rename macros from
|
|
:file:`generated_dts_board_unfixed.h` to names that are more meaningful for the
|
|
device. By default, these fixup files are in the :file:`board/` and
|
|
:file:`soc/` directories. Any :file:`dts_fixup.h` files are concatenated and
|
|
stored as :file:`include/generated_dts_board_fixups.h` in the build directory.
|
|
|
|
Fixup files exist for historical reasons, and Zephyr might move away from using
|
|
them. When writing new code, feel free to create any macro aliases you need in
|
|
whatever way is handiest for the code.
|
|
|
|
To reference macros generated from devicetree, code should include the
|
|
:file:`generated_dts_board.h` header, which appears on the C preprocessor
|
|
include path. This file appears at :zephyr_file:`include/generated_dts_board.h`
|
|
and is not a generated file. It includes the generated
|
|
:file:`include/generated_dts_board_unfixed.h` and
|
|
:file:`include/generated_dts_board_fixups.h` files.
|
|
|
|
.. warning::
|
|
|
|
Do not include the generated C headers from the build directory directly.
|
|
Include ``generated_dts_board.h`` instead.
|
|
|
|
Generated macros
|
|
================
|
|
|
|
Take the DTS node below as an example.
|
|
|
|
.. code-block:: none
|
|
|
|
sim@40047000 {
|
|
compatible = "nxp,kinetis-sim";
|
|
reg = <0x40047000 0x1060>;
|
|
label = "SIM";
|
|
...
|
|
};
|
|
|
|
Below is sample header content generated for this node, in
|
|
:file:`include/generated_dts_board_unfixed.h` in the build directory.
|
|
|
|
.. code-block:: c
|
|
|
|
/*
|
|
* Devicetree node:
|
|
* /soc/sim@40047000
|
|
*
|
|
* Binding (compatible = nxp,kinetis-sim):
|
|
* $ZEPHYR_BASE/dts/bindings/arm/nxp,kinetis-sim.yaml
|
|
*
|
|
* Dependency Ordinal: 24
|
|
*
|
|
* Requires:
|
|
* 7 /soc
|
|
*
|
|
* Supports:
|
|
* 25 /soc/i2c@40066000
|
|
* 26 /soc/i2c@40067000
|
|
* ...
|
|
*
|
|
* Description:
|
|
* Kinetis System Integration Module (SIM) IP node
|
|
*/
|
|
#define DT_NXP_KINETIS_SIM_40047000_BASE_ADDRESS 0x40047000
|
|
#define DT_INST_0_NXP_KINETIS_SIM_BASE_ADDRESS DT_NXP_KINETIS_SIM_40047000_BASE_ADDRESS
|
|
#define DT_NXP_KINETIS_SIM_40047000_SIZE 4192
|
|
#define DT_INST_0_NXP_KINETIS_SIM_SIZE DT_NXP_KINETIS_SIM_40047000_SIZE
|
|
/* Human readable string describing the device (used by Zephyr for API name) */
|
|
#define DT_NXP_KINETIS_SIM_40047000_LABEL "SIM"
|
|
#define DT_INST_0_NXP_KINETIS_SIM_LABEL DT_NXP_KINETIS_SIM_40047000_LABEL
|
|
#define DT_INST_0_NXP_KINETIS_SIM 1
|
|
|
|
Generated macro names follow the format ``DT_<node>_<property>``. For
|
|
``DT_NXP_KINETIS_SIM_40047000_BASE_ADDRESS``, the node part is
|
|
``NXP_KINETIS_SIM_40047000``, based on the compatible string that matched a
|
|
binding for the node (``nxp,kinetis-sim``) and the node's unit address
|
|
(``...@4004700``).
|
|
|
|
The ``*_BASE_ADDRESS`` part of the identifier is a fixed identifier generated
|
|
from the special ``reg`` property on the node. ``*_SIZE`` is also generated
|
|
from ``reg``. Other suffixes, like ``*_LABEL``, are generated directly from the
|
|
property name.
|
|
|
|
The second macro (``DT_INST_0_NXP_KINETIS_SIM_BASE_ADDRESS``) is an alias for
|
|
the first macro. The node identifier ``...INST_0_NXP_KINETIS_SIM_...`` means
|
|
"the first node with compatible string ``nxp,kinetis-sim``.
|
|
|
|
Aliases are also generated from any properties in the ``/aliases`` node. Take
|
|
the DTS fragment below as an example.
|
|
|
|
.. code-block:: none
|
|
|
|
aliases {
|
|
i2c-1 = &i2c;
|
|
};
|
|
|
|
This would generate additional ``DT_ALIAS_I2C_1_...`` aliases for all
|
|
properties in the output for the node with the devicetree label ``i2c``.
|
|
|
|
Aliases that replace the property name part can also be generated, e.g. via
|
|
``*-names = "foo", "bar"`` properties. For example, ``reg-names = "control",
|
|
"mem"`` will generate ``DT_<node>_CONTROL_BASE_ADDRESS/SIZE`` and
|
|
``DT_<node>_MEM_BASE_ADDRESS_SIZE`` aliases.
|
|
|
|
.. note::
|
|
|
|
The above is just a short overview of common ways macro names get generated,
|
|
and not complete. For the nitty-gritty, see the source code in
|
|
:zephyr_file:`gen_defines.py <scripts/dts/gen_defines.py>`, and check the
|
|
output generated for some existing boards and applications.
|
|
|
|
Zephyr device drivers typically use information from ``generated_dts_board.h``
|
|
to statically allocate and initialize :ref:`struct device <device_struct>`
|
|
instances. Property values from ``generated_dts_board.h`` are usually stored in
|
|
ROM in the value pointed to by a ``device->config->config_info`` field. For
|
|
example, a ``struct device`` corresponding to an I2C peripheral would store the
|
|
peripheral address in its ``reg`` property there.
|
|
|
|
Application source code with a pointer to the ``struct device`` can then pass
|
|
it to driver APIs in :zephyr_file:`include/drivers/`. These API functions
|
|
usually take a ``struct device*`` as their first argument. This allows the
|
|
driver API to use information from devicetree to interact with the device
|
|
hardware.
|
|
|
|
.. _dt_k6x_example:
|
|
|
|
Example: FRDM-K64F and Hexiwear K64
|
|
===================================
|
|
|
|
.. Give the filenames instead of the full paths below, as it's easier to read.
|
|
The cramped 'foo.dts<path>' style avoids extra spaces before commas.
|
|
|
|
The FRDM-K64F and Hexiwear K64 board devicetrees are defined in
|
|
:zephyr_file:`frdm_k64fs.dts <boards/arm/frdm_k64f/frdm_k64f.dts>` and
|
|
:zephyr_file:`hexiwear_k64.dts <boards/arm/hexiwear_k64/hexiwear_k64.dts>`
|
|
respectively. Both boards have NXP SoCs from the same Kinetis SoC family, the
|
|
K6X.
|
|
|
|
Common devicetree definitions for K6X are stored in :zephyr_file:`nxp_k6x.dtsi
|
|
<dts/arm/nxp/nxp_k6x.dtsi>`, which is included by both board :file:`.dts`
|
|
files. :zephyr_file:`nxp_k6x.dtsi<dts/arm/nxp/nxp_k6x.dtsi>` in turn includes
|
|
:zephyr_file:`armv7-m.dtsi<dts/arm/armv7-m.dtsi>`, which has common definitions
|
|
for Arm v7-M cores.
|
|
|
|
Since :zephyr_file:`nxp_k6x.dtsi<dts/arm/nxp/nxp_k6x.dtsi>` is meant to be
|
|
generic across K6X-based boards, it leaves many devices disabled by default
|
|
using ``status`` properties. For example, there is a CAN controller defined as
|
|
follows (with unimportant parts skipped):
|
|
|
|
.. code-block:: none
|
|
|
|
can0: can@40024000 {
|
|
...
|
|
status = "disabled";
|
|
...
|
|
};
|
|
|
|
It is up to the board :file:`.dts` or application overlay files to enable these
|
|
devices as desired, by setting ``status = "okay"``. The board :file:`.dts`
|
|
files are also responsible for any board-specific configuration of the device,
|
|
such as adding nodes for on-board sensors, LEDs, buttons, etc.
|
|
|
|
For example, FRDM-K64 (but not Hexiwear K64) :file:`.dts` enables the CAN
|
|
controller and sets the bus speed:
|
|
|
|
.. code-block:: none
|
|
|
|
&can0 {
|
|
status = "okay";
|
|
bus-speed = <125000>;
|
|
};
|
|
|
|
The ``&can0 { ... };`` syntax adds/overrides properties on the node with label
|
|
``can0``, i.e. the ``can@4002400`` node defined in the :file:`.dtsi` file.
|
|
|
|
Other examples of board-specific customization is pointing properties in
|
|
``aliases`` and ``chosen`` to the right nodes (see :ref:`dt-alias-chosen`), and
|
|
making GPIO/pinmux assignments.
|
|
|
|
.. _dt_vs_kconfig:
|
|
|
|
Devicetree vs Kconfig
|
|
*********************
|
|
|
|
Along with devicetree, Zephyr also uses the Kconfig language to configure the
|
|
source code. Whether to use devicetree or Kconfig for a particular purpose can
|
|
sometimes be confusing. This section should help you decide which one to use.
|
|
|
|
In short:
|
|
|
|
* Use devicetree to describe **hardware** and its **boot-time configuration**.
|
|
Examples include peripherals on a board, boot-time clock frequencies,
|
|
interrupt lines, etc.
|
|
* Use Kconfig to configure **software support** to build into the final
|
|
image. Examples include whether to add networking support, which drivers are
|
|
needed by the application, etc.
|
|
|
|
In other words, devicetree mainly deals with hardware, and Kconfig with
|
|
software.
|
|
|
|
For example, consider a board containing a SoC with 2 UART, or serial port,
|
|
instances.
|
|
|
|
* The fact that the board has this UART **hardware** is described with two UART
|
|
nodes in the devicetree. These provide the UART type (via the ``compatible``
|
|
property) and certain settings such as the address range of the hardware
|
|
peripheral registers in memory (via the ``reg`` property).
|
|
* Additionally, the UART **boot-time configuration** is also described with
|
|
devicetree. This could include configuration such as the RX IRQ line's
|
|
priority and the UART baud rate. These may be modifiable at runtime, but
|
|
their boot-time configuration is described in devicetree.
|
|
* Whether or not to include **software support** for UART in the build is
|
|
controlled via Kconfig. Applications which do not need to use the UARTs can
|
|
remove the driver source code from the build using Kconfig, even though the
|
|
board's devicetree still includes UART nodes.
|
|
|
|
As another example, consider a device with a 2.4GHz, multi-protocol radio
|
|
supporting both the Bluetooth Low Energy and 802.15.4 wireless technologies.
|
|
|
|
* Devicetree should be used to describe the presence of the radio **hardware**,
|
|
what driver or drivers it's compatible with, etc.
|
|
* **Boot-time configuration** for the radio, such as TX power in dBm, should
|
|
also be specified using devicetree.
|
|
* Kconfig should determine which **software features** should be built for the
|
|
radio, such as selecting a BLE or 802.15.4 protocol stack.
|
|
|
|
There are two noteworthy **exceptions** to these rules:
|
|
|
|
* Devicetree's ``chosen`` keyword, which allows the user to select a specific
|
|
instance of a hardware device to be used for a particular purpose. An example
|
|
of this is selecting a particular UART for use as the system's console.
|
|
* Devicetree's ``status`` keyword, which allows the user to enable or disable a
|
|
particular instance of a hardware device. This takes precedence over related
|
|
Kconfig options which serve a similar purpose.
|
|
|
|
Currently supported boards
|
|
**************************
|
|
|
|
Devicetree is currently supported on all embedded targets except posix
|
|
(boards/posix).
|
|
|
|
Adding support for a board
|
|
**************************
|
|
|
|
Adding devicetree support for a given board requires adding a number of files.
|
|
These files will contain the DTS information that describes a platform, the
|
|
bindings in YAML format, and any fixup files required to support the platform.
|
|
|
|
It is best practice to separate common peripheral information that could be
|
|
used across multiple cores, SoC families, or boards in :file:`.dtsi` files,
|
|
reserving the :file:`.dts` suffix for the primary DTS file for a given board.
|
|
|
|
Devicetree Source File Template
|
|
===============================
|
|
|
|
A board's :file:`.dts` file contains at least a version line, optional
|
|
includes, and a root node definition with ``model`` and ``compatible``
|
|
properties. These property values denote the particular board.
|
|
|
|
.. code-block:: none
|
|
|
|
/dts-v1/;
|
|
|
|
#include <vendor/soc.dtsi>
|
|
|
|
/ {
|
|
model = "Human readable board name";
|
|
compatible = "vendor,soc-on-your-board's-mcu";
|
|
/* rest of file */
|
|
};
|
|
|
|
You can use other board :file:`.dts` files as a starting point.
|
|
|
|
The following is a more precise list of required files:
|
|
|
|
* Base architecture support
|
|
|
|
* Add architecture-specific DTS directory, if not already present.
|
|
Example: dts/arm for Arm.
|
|
* Add target specific devicetree files for base SoC. These should be
|
|
.dtsi files to be included in the board-specific devicetree files.
|
|
* Add target specific YAML binding files in the dts/bindings/ directory.
|
|
Create the yaml directory if not present.
|
|
|
|
* SoC family support
|
|
|
|
* Add one or more SoC family .dtsi files that describe the hardware
|
|
for a set of devices. The file should contain all the relevant
|
|
nodes and base configuration that would be applicable to all boards
|
|
utilizing that SoC family.
|
|
* Add SoC family YAML binding files that describe the nodes present in the .dtsi file.
|
|
|
|
* Board specific support
|
|
|
|
* Add a board level .dts file that includes the SoC family .dtsi files
|
|
and enables the nodes required for that specific board.
|
|
* Board .dts file should specify the SRAM and FLASH devices, if present.
|
|
|
|
* Flash device node might specify flash partitions. For more details see
|
|
:ref:`flash_partitions`
|
|
|
|
* Add board-specific YAML binding files, if required. This would occur if the
|
|
board has additional hardware that is not covered by the SoC family
|
|
.dtsi/.yaml files.
|
|
|
|
* Fixup files
|
|
|
|
* Fixup files contain mappings from existing Kconfig options to the actual
|
|
underlying DTS derived configuration #defines. Fixup files are temporary
|
|
artifacts until additional DTS changes are made to make them unnecessary.
|
|
|
|
* Overlay Files (optional)
|
|
|
|
* Overlay files contain tweaks or changes to the SoC and Board support files
|
|
described above. They can be used to modify devicetree configurations
|
|
without having to change the SoC and Board files. See
|
|
:ref:`application_dt` for more information on overlay files and the Zephyr
|
|
build system.
|
|
|
|
.. _dt-alias-chosen:
|
|
|
|
``aliases`` and ``chosen`` nodes
|
|
================================
|
|
|
|
Using an alias with a common name for a particular node makes it easier for you
|
|
to write board-independent source code. Devicetree ``aliases`` nodes are used
|
|
for this purpose, by mapping certain generic, commonly used names to specific
|
|
hardware resources:
|
|
|
|
.. code-block:: yaml
|
|
|
|
aliases {
|
|
led0 = &led0;
|
|
sw0 = &button0;
|
|
sw1 = &button1;
|
|
uart-0 = &uart0;
|
|
uart-1 = &uart1;
|
|
};
|
|
|
|
Certain software subsystems require a specific hardware resource to bind to in
|
|
order to function properly. Some of those subsystems are used with many
|
|
different boards, which makes using the devicetree ``chosen`` nodes very
|
|
convenient. By doing, so the software subsystem can rely on having the specific
|
|
hardware peripheral assigned to it. In the following example we bind the shell
|
|
to ``uart1`` in this board:
|
|
|
|
.. code-block:: yaml
|
|
|
|
chosen {
|
|
zephyr,shell-uart = &uart1;
|
|
};
|
|
|
|
The table below lists Zephyr-specific ``chosen`` properties. The macro
|
|
identifiers that start with ``CONFIG_*`` are generated from Kconfig symbols
|
|
that reference devicetree data via the :ref:`Kconfig preprocessor
|
|
<kconfig-functions>`.
|
|
|
|
.. note::
|
|
|
|
Since the particular devicetree isn't known while generating Kconfig
|
|
documentation, the Kconfig symbol reference pages linked below do not
|
|
include information derived from devicetree. Instead, you might see e.g. an
|
|
empty default:
|
|
|
|
.. code-block:: none
|
|
|
|
default "" if HAS_DTS
|
|
|
|
To see how the preprocessor is used for a symbol, look it up directly in the
|
|
:file:`Kconfig` file where it is defined instead. The reference page for the
|
|
symbol gives the definition location.
|
|
|
|
.. list-table::
|
|
:header-rows: 1
|
|
|
|
* - ``chosen`` node name
|
|
- Generated macros
|
|
|
|
* - ``zephyr,flash``
|
|
- ``DT_FLASH_BASE_ADDRESS``/``DT_FLASH_SIZE``/``DT_FLASH_ERASE_BLOCK_SIZE``/``DT_FLASH_WRITE_BLOCK_SIZE``
|
|
* - ``zephyr,code-partition``
|
|
- ``DT_CODE_PARTITION_OFFSET``/``DT_CODE_PARTITION_SIZE``
|
|
* - ``zephyr,sram``
|
|
- :option:`CONFIG_SRAM_BASE_ADDRESS`/:option:`CONFIG_SRAM_SIZE`
|
|
* - ``zephyr,ccm``
|
|
- ``DT_CCM_BASE_ADDRESS``/``DT_CCM_SIZE``
|
|
* - ``zephyr,dtcm``
|
|
- ``DT_DTCM_BASE_ADDRESS``/``DT_DTCM_SIZE``
|
|
* - ``zephyr,ipc_shm``
|
|
- ``DT_IPC_SHM_BASE_ADDRESS``/``DT_IPC_SHM_SIZE``
|
|
* - ``zephyr,console``
|
|
- :option:`CONFIG_UART_CONSOLE_ON_DEV_NAME`
|
|
* - ``zephyr,shell-uart``
|
|
- :option:`CONFIG_UART_SHELL_ON_DEV_NAME`
|
|
* - ``zephyr,bt-uart``
|
|
- :option:`CONFIG_BT_UART_ON_DEV_NAME`
|
|
* - ``zephyr,uart-pipe``
|
|
- :option:`CONFIG_UART_PIPE_ON_DEV_NAME`
|
|
* - ``zephyr,bt-mon-uart``
|
|
- :option:`CONFIG_BT_MONITOR_ON_DEV_NAME`
|
|
* - ``zephyr,bt-c2h-uart``
|
|
- :option:`CONFIG_BT_CTLR_TO_HOST_UART_DEV_NAME`
|
|
* - ``zephyr,uart-mcumgr``
|
|
- :option:`CONFIG_UART_MCUMGR_ON_DEV_NAME`
|
|
|
|
|
|
Adding support for devicetree in drivers
|
|
****************************************
|
|
|
|
As drivers and other source code is converted over to make use of devicetree
|
|
generated information, these drivers may require changes to match the generated
|
|
#define information.
|
|
|
|
|
|
Source Tree Hierarchy
|
|
*********************
|
|
|
|
The devicetree files are located in a couple of different directories. The
|
|
directory split is done based on architecture, and there is also a common
|
|
directory where architecture agnostic devicetree and YAML binding files are
|
|
located.
|
|
|
|
Assuming the current working directory is the ZEPHYR_BASE, the directory
|
|
hierarchy looks like the following::
|
|
|
|
dts/common/
|
|
dts/<ARCH>/
|
|
dts/bindings/
|
|
boards/<ARCH>/<BOARD>/
|
|
|
|
The common directory contains a ``skeleton.dtsi`` which provides devicetree root
|
|
node definition. The bindings subdirectory contains YAML binding files used
|
|
to instruct how the python DTS parsing script should extract nodes information
|
|
in a format that will be usable by the system.
|
|
|
|
Example: Subset of DTS/YAML files for NXP FRDM K64F (Subject to Change)::
|
|
|
|
dts/arm/armv7-m.dtsi
|
|
dts/arm/k6x/nxp_k6x.dtsi
|
|
boards/arm/frdm_k64f/frdm_k64f.dts
|
|
dts/bindings/interrupt-controller/arm,v7m-nvic.yaml
|
|
dts/bindings/gpio/nxp,kinetis-gpio.yaml
|
|
dts/bindings/pinctrl/nxp,kinetis-pinmux.yaml
|
|
dts/bindings/serial/nxp,kinetis-uart.yaml
|
|
|
|
.. _bindings:
|
|
|
|
Devicetree Bindings
|
|
*******************
|
|
|
|
``.dts`` files describe the available hardware devices, but don't tell the
|
|
system which pieces of information are useful, or what kind of configuration
|
|
output (``#define``'s) should be generated. *Bindings* provide this
|
|
information. Bindings are files in YAML format.
|
|
|
|
Configuration output is only generated for devices that have bindings.
|
|
|
|
Nodes are mapped to bindings via their ``compatible`` string(s). Take
|
|
the following node as an example:
|
|
|
|
.. code-block:: none
|
|
|
|
bar-device {
|
|
compatible = "foo-company,bar-device";
|
|
...
|
|
};
|
|
|
|
This node would get mapped to a binding with this in it:
|
|
|
|
.. code-block:: yaml
|
|
|
|
compatible: "foo-company,bar-device"
|
|
|
|
You might also run across this legacy syntax, which works the same way:
|
|
|
|
.. code-block:: yaml
|
|
|
|
...
|
|
|
|
properties:
|
|
compatible:
|
|
constraint: "foo-company,bar-device"
|
|
|
|
...
|
|
|
|
Bindings are stored in :zephyr_file:`dts/bindings/`. The filename usually
|
|
matches the ``compatible`` string.
|
|
|
|
If a node has more than one ``compatible`` string, then the first binding found
|
|
is used, going from the first string to the last. For example, a node with
|
|
``compatible = "foo-company,bar-device", "generic-bar-device"`` would get
|
|
mapped to the binding for ``generic-bar-device`` if there is no binding for
|
|
``foo-company,bar-device``.
|
|
|
|
If a node appears on a bus (e.g. I2C or SPI), then the bus type is also taken
|
|
into account when mapping nodes to bindings. See the description of ``parent``
|
|
and ``child`` in the template below.
|
|
|
|
Below is a template that shows the format of binding files, stored in
|
|
:zephyr_file:`dts/binding-template.yaml`.
|
|
|
|
.. literalinclude:: ../../../dts/binding-template.yaml
|
|
:language: yaml
|
|
|
|
.. _legacy_binding_syntax:
|
|
|
|
Legacy binding syntax
|
|
=====================
|
|
|
|
Various parts of the binding syntax were simplified and generalized for the
|
|
Zephyr 2.1 release.
|
|
|
|
The binding below shows various legacy syntax.
|
|
|
|
.. code-block:: yaml
|
|
|
|
title: ...
|
|
description: ...
|
|
|
|
inherits:
|
|
!include foo.yaml
|
|
|
|
parent:
|
|
bus: spi
|
|
|
|
parent-bus: spi
|
|
|
|
properties:
|
|
compatible:
|
|
constraint: "company,device"
|
|
type: string-array
|
|
|
|
frequency:
|
|
type: int
|
|
category: optional
|
|
|
|
sub-node:
|
|
properties:
|
|
child-prop:
|
|
type: int
|
|
category: required
|
|
|
|
# Assume this is a binding for an interrupt controller
|
|
"#cells":
|
|
- irq
|
|
- priority
|
|
- flags
|
|
|
|
This should now be written like this:
|
|
|
|
.. code-block:: yaml
|
|
|
|
description: ...
|
|
|
|
compatible: "company,device"
|
|
|
|
include: foo.yaml
|
|
|
|
bus: spi
|
|
|
|
properties:
|
|
frequency:
|
|
type: int
|
|
required: false
|
|
|
|
child-binding:
|
|
description: ...
|
|
|
|
properties:
|
|
child-prop:
|
|
type: int
|
|
required: true
|
|
|
|
interrupt-cells:
|
|
- irq
|
|
- priority
|
|
- cells
|
|
|
|
The legacy syntax is still supported for backwards compatibility, but generates
|
|
deprecation warnings. Support will be dropped in the Zephyr 2.3 release.
|
|
|
|
GPIO Nexus Nodes
|
|
****************
|
|
|
|
Each board has a set of General Purpose Input/Output (GPIO)
|
|
peripherals that can be accessed through the :ref:`GPIO<gpio>` module.
|
|
Many boards provide headers that allow :ref:`shields<shields>` from
|
|
other vendors to be mounted on their boards. Each shield identifies
|
|
its hardware in a devicetree overlay.
|
|
|
|
GPIOs accessed by the shield peripherals must be identified using the
|
|
shield GPIO abstraction, for example from the ``arduino-r3-header``
|
|
compatible. Boards that provide the header must map the header pins
|
|
to SOC-specific pins. This is accomplished by including a `nexus
|
|
node`_ that looks like the following into the board devicetree file:
|
|
|
|
.. _nexus node:
|
|
https://github.com/devicetree-org/devicetree-specification/blob/4b1dac80eaca45b4babf5299452a951008a5d864/source/devicetree-basics.rst#nexus-nodes-and-specifier-mapping
|
|
|
|
.. code-block:: none
|
|
|
|
arduino_header: connector {
|
|
compatible = "arduino-header-r3";
|
|
#gpio-cells = <2>;
|
|
gpio-map-mask = <0xffffffff 0xffffffc0>;
|
|
gpio-map-pass-thru = <0 0x3f>;
|
|
gpio-map = <0 0 &gpioa 0 0>, /* A0 */
|
|
<1 0 &gpioa 1 0>, /* A1 */
|
|
<2 0 &gpioa 4 0>, /* A2 */
|
|
<3 0 &gpiob 0 0>, /* A3 */
|
|
<4 0 &gpioc 1 0>, /* A4 */
|
|
<5 0 &gpioc 0 0>, /* A5 */
|
|
<6 0 &gpioa 3 0>, /* D0 */
|
|
<7 0 &gpioa 2 0>, /* D1 */
|
|
<8 0 &gpioa 10 0>, /* D2 */
|
|
<9 0 &gpiob 3 0>, /* D3 */
|
|
<10 0 &gpiob 5 0>, /* D4 */
|
|
<11 0 &gpiob 4 0>, /* D5 */
|
|
<12 0 &gpiob 10 0>, /* D6 */
|
|
<13 0 &gpioa 8 0>, /* D7 */
|
|
<14 0 &gpioa 9 0>, /* D8 */
|
|
<15 0 &gpioc 7 0>, /* D9 */
|
|
<16 0 &gpiob 6 0>, /* D10 */
|
|
<17 0 &gpioa 7 0>, /* D11 */
|
|
<18 0 &gpioa 6 0>, /* D12 */
|
|
<19 0 &gpioa 5 0>, /* D13 */
|
|
<20 0 &gpiob 9 0>, /* D14 */
|
|
<21 0 &gpiob 8 0>; /* D15 */
|
|
};
|
|
|
|
This specifies how Arduino pin references like ``<&arduino_header 11
|
|
0>`` are converted to SOC gpio pin references like ``<&gpiob 4 0>``.
|
|
|
|
In Zephyr GPIO specifiers generally have two parameters (indicated by
|
|
``#gpio-cells = <2>``): the pin number and a set of flags. The low 6
|
|
bits of the flags correspond to features that can be configured in
|
|
devicetree. In some cases it's necessary to use a non-zero flag value
|
|
to tell the driver how a particular pin behaves, as with:
|
|
|
|
.. code-block:: none
|
|
|
|
drdy-gpios = <&arduino_header 11 GPIO_ACTIVE_LOW>;
|
|
|
|
After preprocessing this becomes ``<&arduino_header 11 1>``. Normally
|
|
the presence of such a flag would cause the map lookup to fail,
|
|
because there is no map entry with a non-zero flags value. The
|
|
``gpio-map-mask`` property specifies that, for lookup, all bits of the
|
|
pin and all but the low 6 bits of the flags are used to identify the
|
|
specifier. Then the ``gpio-map-pass-thru`` specifies that the low 6
|
|
bits of the flags are copied over, so the SOC GPIO reference becomes
|
|
``<&gpiob 4 1>`` as intended.
|
|
|
|
See `nexus node`_ for more information about this capability.
|
|
|
|
.. include:: flash_partitions.inc
|