mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-09-02 03:22:30 +00:00
Give an example for an interrupt controller, where 'interrupt-cells' should be used instead. Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
568 lines
20 KiB
ReStructuredText
568 lines
20 KiB
ReStructuredText
.. _device-tree:
|
|
|
|
Device Tree
|
|
###########
|
|
|
|
.. contents::
|
|
:local:
|
|
:depth: 2
|
|
|
|
Introduction
|
|
************
|
|
|
|
Device tree is a way of describing hardware and configuration information
|
|
for boards. Device tree was adopted for use in the Linux kernel for the
|
|
PowerPC architecture. However, it is now in use for ARM and other
|
|
architectures.
|
|
|
|
The device tree is a data structure for dynamically describing hardware
|
|
using a Device Tree Source (DTS) data structure language, instead of
|
|
hard coding every detail of a board's hardware into the operating system.
|
|
|
|
In Linux, DTS is compiled into a compact Device Tree Blob (DTB) using a Device
|
|
Tree Compiler (DTC), then the hardware-describing DTB is passed to the operating
|
|
system at boot time. This allows the same compiled Linux kernel to support
|
|
different hardware configurations within an architecture family (e.g., ARM,
|
|
x86, PowerPC) and moves a significant part of the hardware description out of
|
|
the kernel binary itself.
|
|
|
|
For larger systems, the flexibility this offers offsets the extra runtime memory
|
|
overhead incurred. But the primary targets for Zephyr
|
|
applications are small micro-controller systems with limited memory
|
|
resources. So instead of requiring the additional runtime memory
|
|
to store the DTB blob and the code to parse it, the DTS information
|
|
is used at compile time.
|
|
|
|
Device tree uses a specific format to describe the device nodes in a system.
|
|
This format is described in the `Device Tree Specification`_.
|
|
|
|
.. _Device Tree Specification: https://github.com/devicetree-org/devicetree-specification/releases
|
|
|
|
More device tree information can be found at the `device tree repository`_.
|
|
|
|
.. _device tree repository: https://git.kernel.org/pub/scm/utils/dtc/dtc.git
|
|
|
|
|
|
System build requirements
|
|
*************************
|
|
|
|
The Zephyr device tree feature requires a device tree compiler (DTC) and Python
|
|
YAML packages. Refer to the installation guide for your specific host OS:
|
|
|
|
* :ref:`installing_zephyr_win`
|
|
* :ref:`installation_linux`
|
|
* :ref:`installing_zephyr_mac`
|
|
|
|
|
|
Zephyr and Device Tree
|
|
**********************
|
|
|
|
Device Tree provides a unified description of a hardware system used in an
|
|
application. It is used in Zephyr to describe hardware and provide a boot-time
|
|
configuration of this hardware.
|
|
|
|
In Zephyr, the device tree is also used to describe Zephyr-specific
|
|
configuration information. This Zephyr-specific information augments the device
|
|
tree descriptions and sits on top of it, rather than diverging from it. The
|
|
main reason for this is to leverage existing device tree files that a SoC vendor
|
|
may already have defined for a given platform.
|
|
|
|
The device tree files are compiled using the device tree compiler. The compiler
|
|
runs the .dts file through the C preprocessor to resolve any macro or #defines
|
|
utilized in the file. The output of the compile is another dts formatted file.
|
|
|
|
After compilation, a Python script extracts information from the compiled
|
|
device tree file using rules specified in *bindings* (see the :ref:`bindings`
|
|
section). The extracted information is placed in a header file that is used by
|
|
the rest of the code as the project is compiled.
|
|
|
|
Temporary fixup files are required for device tree support on most devices.
|
|
These fixup files by default reside in the board and soc directories and are
|
|
named ``dts_fixup.h``. These fixup files map the generated include information to
|
|
the current driver/source usage.
|
|
|
|
The Python code that deals with device tree and bindings is in
|
|
:zephyr_file:`scripts/dts/`.
|
|
|
|
.. _dt_vs_kconfig:
|
|
|
|
Device Tree vs Kconfig
|
|
**********************
|
|
|
|
As mentioned above there are several tools used to configure the build with
|
|
Zephyr.
|
|
The two main ones, Device Tree and Kconfig, might seem to overlap in purpose,
|
|
but in fact they do not. This section serves as a reference to help you decide
|
|
whether to place configuration items in Device Tree or Kconfig.
|
|
|
|
The scope of each configuration tool can be summarized as follows:
|
|
|
|
* Device Tree is used to describe the **hardware** and its **boot-time
|
|
configuration**.
|
|
* Kconfig is used to describe which **software features** will be built into
|
|
the final image, and their **configuration**.
|
|
|
|
Hence Device Tree deals mostly with hardware and Kconfig with software.
|
|
A couple of noteworthy exceptions are:
|
|
|
|
* Device Tree's ``chosen`` keyword, which allows the user to select a
|
|
particular instance of a hardware device to be used for a concrete purpose
|
|
by the software. An example of this is selecting a particular UART for use as
|
|
the system's console.
|
|
* Device Tree's ``status`` keyword, which allows the user to enable or disable
|
|
a particular instance of a hardware device in the Device Tree file itself.
|
|
This takes precedence over Kconfig.
|
|
|
|
To further clarify this separation, let's use a particular, well-known
|
|
example to illustrate this: serial ports a.k.a. UARTs. Let's consider a
|
|
board containing a SoC with 2 UART instances:
|
|
|
|
* The fact that the target hardware **contains** 2 UARTs is described with Device
|
|
Tree. This includes the UART type, its driver compatibility and certain
|
|
immutable (i.e. not software-configurable) settings such as the base address
|
|
of the hardware peripheral in memory or its interrupt line.
|
|
* Additionally, **hardware boot-time configuration** is also described with Device
|
|
Tree. This includes things such as the IRQ priority and boot-time UART
|
|
baudrate. These may also be modifiable at runtime later, but their boot-time
|
|
default configuration is described in Device Tree.
|
|
* The fact that the user intends to include **software support** for UART in the
|
|
build is described in Kconfig. Through the use of Kconfig, users can opt not
|
|
to include support for this particular hardware item if they don't require it.
|
|
|
|
Another example is a device with a 2.4GHz, multi-protocol radio supporting
|
|
both the Bluetooth Low Energy and 802.15.4 wireless technologies. In this case:
|
|
|
|
* Device Tree describes the presence of a radio peripheral compatible with a
|
|
certain radio driver.
|
|
* Additional hardware boot-time configuration settings may also be present
|
|
in the Device Tree files. In this particular case it could be a
|
|
default TX power in dBm if the hardware does have a simple, cross-wireless
|
|
technology register for that.
|
|
* Kconfig will describe which **protocol stack** is to be used with that radio.
|
|
The user may decide to select BLE or 802.15.4, which will both depend on
|
|
the presence of a compatible radio in the Device Tree files.
|
|
|
|
Device tree file formats
|
|
************************
|
|
|
|
Hardware and software is described inside of device tree files in clear text format.
|
|
These files have the file suffix of .dtsi or .dts. The .dtsi files are meant to
|
|
be included by other files. Typically for a given board you have some number of
|
|
.dtsi include files that pull in common device descriptions that are used across
|
|
a given SoC family.
|
|
|
|
Example: FRDM K64F Board 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.
|
|
|
|
These boards 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>`. They are based on the same NXP
|
|
Kinetis SoC family, the K6X.
|
|
|
|
Common 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 ARMv7-M-based systems.
|
|
|
|
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.
|
|
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` files to enable devices (by setting
|
|
``status = "okay"``). The board :file:`.dts` files are also responsible for any
|
|
board-specific configuration of the device.
|
|
|
|
For example, FRDM K64 (but not Hexiwear K64) enables the CAN controller, also
|
|
setting 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.
|
|
|
|
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.
|
|
|
|
Currently supported boards
|
|
**************************
|
|
|
|
Device tree is currently supported on all embedded targets except posix
|
|
(boards/posix) and qemu_x86_64 (boards/x86_64).
|
|
|
|
Adding support for a board
|
|
**************************
|
|
|
|
Adding device tree support for a given board requires adding a number of files.
|
|
These files will contain the DTS information that describes a platform, the
|
|
YAML descriptions that define the contents of a given Device Tree peripheral
|
|
node, and also any fixup files required to support the platform.
|
|
|
|
When writing Device Tree Source files, it is good to separate out common
|
|
peripheral information that could be used across multiple SoC families or
|
|
boards. DTS files are identified by their file suffix. A .dtsi suffix denotes
|
|
a DTS file that is used as an include in another DTS file. A .dts suffix
|
|
denotes the primary DTS file for a given board.
|
|
|
|
The primary DTS file will contain at a minimum a version line, optional
|
|
includes, and the root node definition. The root node will contain a model and
|
|
compatible that denotes the unique board described by the .dts file.
|
|
|
|
Device Tree Source File Template
|
|
================================
|
|
|
|
.. code-block:: none
|
|
|
|
/dts-v1/;
|
|
|
|
/ {
|
|
model = "Model name for your board";
|
|
compatible = "compatible for your board";
|
|
/* rest of file */
|
|
};
|
|
|
|
|
|
One suggestion for starting from scratch on a platform/board is to examine other
|
|
boards and their device tree source files.
|
|
|
|
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 device tree files for base SoC. These should be
|
|
.dtsi files to be included in the board-specific device tree 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 Device Tree 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. Device Tree ``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 Device Tree ``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 full set of Zephyr-specific ``chosen`` nodes follows:
|
|
|
|
.. list-table::
|
|
:header-rows: 1
|
|
|
|
* - ``chosen`` node name
|
|
- Generated symbol
|
|
|
|
* - ``zephyr,flash``
|
|
- ``CONFIG_FLASH``
|
|
* - ``zephyr,sram``
|
|
- ``CONFIG_SRAM_SIZE``/``CONFIG_SRAM_BASE_ADDRESS``
|
|
(via ``DT_SRAM_SIZE``/``DT_SRAM_BASE_ADDRESS``)
|
|
* - ``zephyr,ccm``
|
|
- ``DT_CCM``
|
|
* - ``zephyr,console``
|
|
- ``DT_UART_CONSOLE_ON_DEV_NAME``
|
|
* - ``zephyr,shell-uart``
|
|
- ``DT_UART_SHELL_ON_DEV_NAME``
|
|
* - ``zephyr,bt-uart``
|
|
- ``DT_BT_UART_ON_DEV_NAME``
|
|
* - ``zephyr,uart-pipe``
|
|
- ``DT_UART_PIPE_ON_DEV_NAME``
|
|
* - ``zephyr,bt-mon-uart``
|
|
- ``DT_BT_MONITOR_ON_DEV_NAME``
|
|
* - ``zephyr,uart-mcumgr``
|
|
- ``DT_UART_MCUMGR_ON_DEV_NAME``
|
|
|
|
|
|
Adding support for device tree in drivers
|
|
*****************************************
|
|
|
|
As drivers and other source code is converted over to make use of device tree
|
|
generated information, these drivers may require changes to match the generated
|
|
#define information.
|
|
|
|
|
|
Source Tree Hierarchy
|
|
*********************
|
|
|
|
The device tree 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 device tree 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 device tree 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:
|
|
|
|
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
|
|
|
|
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
|
|
|
|
title: ...
|
|
description: ...
|
|
|
|
compatible: "company,device"
|
|
|
|
include: foo.yaml
|
|
|
|
parent-bus: spi
|
|
|
|
properties:
|
|
frequency:
|
|
type: int
|
|
required: false
|
|
|
|
child-binding:
|
|
title: ...
|
|
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.
|
|
|
|
Include files generation
|
|
************************
|
|
|
|
At build time, after a board's ``.dts`` file has been processed by the DTC
|
|
(Device Tree Compiler), a corresponding ``.dts_compiled`` file is generated
|
|
under the ``zephyr`` directory.
|
|
This ``.dts_compiled`` file is processed by the python DTS parsing script
|
|
and generates an include file named
|
|
``include/generated/generated_dts_board_unfixed.h``
|
|
that holds all the information extracted from the DTS file with
|
|
the format specified by the YAML bindings. For example:
|
|
|
|
.. code-block:: c
|
|
|
|
/* gpio_keys */
|
|
#define DT_GPIO_KEYS_0 1
|
|
|
|
/* button_0 */
|
|
#define DT_GPIO_KEYS_BUTTON_0_GPIOS_CONTROLLER "GPIO_2"
|
|
#define DT_GPIO_KEYS_BUTTON_0_GPIOS_FLAGS 0
|
|
#define DT_GPIO_KEYS_BUTTON_0_GPIOS_PIN 6
|
|
#define DT_GPIO_KEYS_BUTTON_0_LABEL "User SW2"
|
|
|
|
#define DT_GPIO_KEYS_SW1_GPIOS_CONTROLLER DT_GPIO_KEYS_BUTTON_0_GPIOS_CONTROLLER
|
|
#define DT_GPIO_KEYS_SW1_GPIOS_FLAGS DT_GPIO_KEYS_BUTTON_0_GPIOS_FLAGS
|
|
#define DT_GPIO_KEYS_SW1_GPIOS_PIN DT_GPIO_KEYS_BUTTON_0_GPIOS_PIN
|
|
#define DT_ALIAS_SW1_GPIOS_CONTROLLE DT_GPIO_KEYS_BUTTON_0_GPIOS_CONTROLLER
|
|
#define DT_ALIAS_SW1_GPIOS_FLAGS DT_GPIO_KEYS_BUTTON_0_GPIOS_FLAGS
|
|
#define DT_ALIAS_SW1_GPIOS_PIN DT_GPIO_KEYS_BUTTON_0_GPIOS_PIN
|
|
#define DT_ALIAS_SW1_LABEL DT_GPIO_KEYS_BUTTON_0_LABEL
|
|
|
|
|
|
Additionally, a file named ``generated_dts_board_fixups.h`` is
|
|
generated in the same directory concatenating all board-related fixup files.
|
|
|
|
The include file ``include/generated_dts_board.h`` includes both these generated
|
|
files, giving Zephyr C source files access to the board's device tree information.
|
|
|
|
.. include:: flash_partitions.inc
|