blob: e99909c16a976938ff39c5f780100b0d9f219ba6 [file] [log] [blame]
Devicetree HOWTOs
#################
This page has advice for getting things done with :ref:`devicetree` in
Zephyr.
.. This page could use some more love, especially giving advice to
driver writers about how to allocate their struct devices.
Adding support for a board
**************************
Devicetree is currently supported on all embedded targets except posix
(boards/posix).
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.
.. _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.
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 a device driver
**********************************
Zephyr device drivers typically use information from :file:`devicetree.h` to
statically allocate and initialize :ref:`struct device <device_struct>`
instances. :ref:`dt-macros` are usually included via :file:`devicetree.h`, then
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.
Driver writers should allocate a struct device for each enabled instance of a
particular compatible using ``DT_INST_<instance-number>_<compatible>``
:ref:`dt-existence-macros`.
.. _flash_partitions:
Managing flash partitions
*************************
Devicetree can be used to describe a partition layout for any flash
device in the system.
Two important uses for this mechanism are:
#. To force the Zephyr image to be linked into a specific area on
Flash.
This is useful, for example, if the Zephyr image must be linked at
some offset from the flash device's start, to be loaded by a
bootloader at runtime.
#. To generate compile-time definitions for the partition layout,
which can be shared by Zephyr subsystems and applications to
operate on specific areas in flash.
This is useful, for example, to create areas for storing file
systems or other persistent state. These defines only describe the
boundaries of each partition. They don't, for example, initialize a
partition's flash contents with a file system.
Partitions are generally managed using device tree overlays. Refer to
:ref:`application_dt` for details on using overlay files.
Defining Partitions
===================
The partition layout for a flash device is described inside the
``partitions`` child node of the flash device's node in the device
tree.
You can define partitions for any flash device on the system.
Most Zephyr-supported SoCs with flash support in device tree
will define a label ``flash0``. This label refers to the primary
on-die flash programmed to run Zephyr. To generate partitions
for this device, add the following snippet to a device tree overlay
file:
.. We can't highlight dts at time of writing:
.. https://github.com/zephyrproject-rtos/zephyr/issues/6029
.. code-block:: none
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Define your partitions here; see below */
};
};
To define partitions for another flash device, modify the above to
either use its label or provide a complete path to the flash device
node in the device tree.
The content of the ``partitions`` node looks like this:
.. code-block:: none
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition1_label: partition@START_OFFSET_1 {
label = "partition1_name";
reg = <0xSTART_OFFSET_1 0xSIZE_1>;
};
/* ... */
partitionN_label: partition@START_OFFSET_N {
label = "partitionN_name";
reg = <0xSTART_OFFSET_N 0xSIZE_N>;
};
};
Where:
- ``partitionX_label`` are device tree labels that can be used
elsewhere in the device tree to refer to the partition
- ``partitionX_name`` controls how defines generated by the Zephyr
build system for this partition will be named
- ``START_OFFSET_x`` is the start offset in hexadecimal notation of
the partition from the beginning of the flash device
- ``SIZE_x`` is the hexadecimal size, in bytes, of the flash partition
The partitions do not have to cover the entire flash device. The
device tree compiler currently does not check if partitions overlap;
you must ensure they do not when defining them.
Example Primary Flash Partition Layout
======================================
Here is a complete (but hypothetical) example device tree overlay
snippet illustrating these ideas. Notice how the partitions do not
overlap, but also do not cover the entire device.
.. code-block:: none
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
code_dts_label: partition@8000 {
label = "zephyr-code";
reg = <0x00008000 0x34000>;
};
data_dts_label: partition@70000 {
label = "application-data";
reg = <0x00070000 0xD000>;
};
};
};
Linking Zephyr Within a Partition
=================================
To force the linker to output a Zephyr image within a given flash
partition, add this to a device tree overlay:
.. code-block:: none
/ {
chosen {
zephyr,code-partition = &slot0_partition;
};
};
Then, enable the :option:`CONFIG_USE_DT_CODE_PARTITION` Kconfig option.
Flash Partition Macros
======================
The Zephyr build system generates definitions for each flash device
partition. These definitions are available to any files which
include ``<zephyr.h>``.
Consider this flash partition:
.. code-block:: none
dts_label: partition@START_OFFSET {
label = "def-name";
reg = <0xSTART_OFFSET 0xSIZE>;
};
The build system will generate the following corresponding defines:
.. code-block:: c
#define FLASH_AREA_DEF_NAME_LABEL "def-name"
#define FLASH_AREA_DEF_NAME_OFFSET_0 0xSTART_OFFSET
#define FLASH_AREA_DEF_NAME_SIZE_0 0xSIZE
#define FLASH_AREA_DEF_NAME_OFFSET FLASH_AREA_MCUBOOT_OFFSET_0
#define FLASH_AREA_DEF_NAME_SIZE FLASH_AREA_MCUBOOT_SIZE_0
As you can see, the ``label`` property is capitalized when forming the
macro names. Other simple conversions to ensure it is a valid C
identifier, such as converting "-" to "_", are also performed. The
offsets and sizes are available as well.
.. _mcuboot_partitions:
MCUboot Partitions
==================
`MCUboot`_ is a secure bootloader for 32-bit microcontrollers.
Some Zephyr boards provide definitions for the flash partitions which
are required to build MCUboot itself, as well as any applications
which must be chain-loaded by MCUboot.
The device tree labels for these partitions are:
**boot_partition**
This is the partition where the bootloader is expected to be
placed. MCUboot's build system will attempt to link the MCUboot
image into this partition.
**slot0_partition**
MCUboot loads the executable application image from this
partition. Any application bootable by MCUboot must be linked to run
from this partition.
**slot1_partition**
This is the partition which stores firmware upgrade images. Zephyr
applications which receive firmware updates must ensure the upgrade
images are placed in this partition (the Zephyr DFU subsystem can be
used for this purpose). MCUboot checks for upgrade images in this
partition, and can move them to ``slot0_partition`` for execution.
The ``slot0_partition`` and ``slot1_partition`` must be the same
size.
**scratch_partition**
This partition is used as temporary storage while swapping the
contents of ``slot0_partition`` and ``slot1_partition``.
.. important::
Upgrade images are only temporarily stored in ``slot1_partition``.
They must be linked to execute of out of ``slot0_partition``.
See the `MCUboot documentation`_ for more details on these partitions.
.. _MCUboot: https://mcuboot.com/
.. _MCUboot documentation:
https://github.com/runtimeco/mcuboot/blob/master/docs/design.md#image-slots
File System Partitions
======================
**storage_partition**
This is the area where e.g. LittleFS or NVS or FCB expects its partition.