zephyr/doc/guides/dts/howtos.rst

907 lines
29 KiB
ReStructuredText

.. _dt-howtos:
Devicetree HOWTOs
#################
This page has step-by-step advice for getting things done with devicetree.
.. _get-devicetree-outputs:
Get your devicetree and generated header
****************************************
A board's devicetree (:ref:`BOARD.dts <devicetree-in-out-files>`) pulls in
common node definitions via ``#include`` preprocessor directives. This at least
includes the SoC's ``.dtsi``. One way to figure out the devicetree's contents
is by opening these files, e.g. by looking in
``dts/<ARCH>/<vendor>/<soc>.dtsi``, but this can be time consuming.
Furthermore, you might want to see the actual generated header file. You might
also be working with a board definition outside of the zephyr repository,
making it unclear where ``BOARD.dts`` is in the first place.
Luckily, there is an easy way to do both: build your application.
For example, using west and the :ref:`qemu_cortex_m3` board to build
:ref:`hello_world`, forcing CMake to re-run:
.. code-block:: sh
west build -b qemu_cortex_m3 -s samples/hello_world --cmake
The build system prints the output file locations:
.. code-block:: none
-- Found BOARD.dts: .../zephyr/boards/arm/qemu_cortex_m3/qemu_cortex_m3.dts
-- Generated zephyr.dts: .../zephyr/build/zephyr/zephyr.dts
-- Generated devicetree_unfixed.h: .../zephyr/build/zephyr/include/generated/devicetree_unfixed.h
Change ``qemu_cortex_m3`` to the board you are using, of course.
.. _dt-get-device:
Get a struct device from a devicetree node
******************************************
When writing Zephyr applications, you'll often want to get a driver-level
:ref:`struct device <device_model_api>` corresponding to a devicetree node.
For example, with this devicetree fragment, you might want the struct device
for ``serial@40002000``:
.. code-block:: DTS
/ {
soc {
serial0: serial@40002000 {
status = "okay";
current-speed = <115200>;
/* ... */
};
};
aliases {
my-serial = &serial0;
};
chosen {
zephyr,console = &serial0;
};
};
Start by making a :ref:`node identifier <dt-node-identifiers>` for the device
you are interested in. There are different ways to do this; pick whichever one
works best for your requirements. Here are some examples:
.. code-block:: c
/* Option 1: by node label */
#define MY_SERIAL DT_NODELABEL(serial0)
/* Option 2: by alias */
#define MY_SERIAL DT_ALIAS(my_serial)
/* Option 3: by chosen node */
#define MY_SERIAL DT_CHOSEN(zephyr_console)
/* Option 4: by path */
#define MY_SERIAL DT_PATH(soc, serial_40002000)
Once you have a node identifier, get the ``struct device`` by combining
:c:func:`DT_LABEL` with :c:func:`device_get_binding`:
.. code-block:: c
struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL));
You can then use ``uart_dev`` with :ref:`uart_api` API functions like
:c:func:`uart_configure`. Similar code will work for other device types; just
make sure you use the correct API for the device.
There's no need to override the ``label`` property to something else: just make
a node identifier and pass it to ``DT_LABEL`` to get the right string to pass
to ``device_get_binding()``.
If you're having trouble, see :ref:`dt-trouble`. The first thing to check is
that the node has ``status = "okay"``, like this:
.. code-block:: c
#define MY_SERIAL DT_NODELABEL(my_serial)
#if DT_NODE_HAS_STATUS(MY_SERIAL, okay)
struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL));
#else
#error "Node is disabled"
#endif
If you see the ``#error`` output, make sure to enable the node in your
devicetree. If you don't see the ``#error`` but ``uart_dev`` is NULL, then
there's likely either a Kconfig issue preventing the device driver from
creating the device, or the device's initialization function failed.
.. _dts-find-binding:
Find a devicetree binding
*************************
Devicetree binding YAML files document what you can do with the nodes they
describe, so it's critical to be able to find them for the nodes you are using.
If you don't have them already, :ref:`get-devicetree-outputs`. To find a node's
binding, open the generated header file, which starts with a list of nodes in a
block comment:
.. code-block:: c
/*
* [...]
* Nodes in dependency order (ordinal and path):
* 0 /
* 1 /aliases
* 2 /chosen
* 3 /flash@0
* 4 /memory@20000000
* (etc.)
* [...]
*/
Make note of the path to the node you want to find, like ``/flash@0``. Search
for the node's output in the file, which starts with something like this if the
node has a matching binding:
.. code-block:: c
/*
* Devicetree node:
* /flash@0
*
* Binding (compatible = soc-nv-flash):
* $ZEPHYR_BASE/dts/bindings/mtd/soc-nv-flash.yaml
* [...]
*/
See :ref:`missing-dt-binding` for troubleshooting.
.. _set-devicetree-overlays:
Set devicetree overlays
***********************
Devicetree overlays are explained in :ref:`devicetree-intro`. The CMake
variable :makevar:`DTC_OVERLAY_FILE` contains a space- or semicolon-separated
list of overlays. If :makevar:`DTC_OVERLAY_FILE` specifies multiple files, they
are included in that order by the C preprocessor.
Here are some ways to set it:
1. on the cmake build command line
(``-DDTC_OVERLAY_FILE="file1.overlay;file2.overlay"``)
#. with the CMake ``set()`` command in the application ``CMakeLists.txt``,
before including zephyr's :file:`boilerplate.cmake` file
#. using a ``DTC_OVERLAY_FILE`` environment variable (deprecated)
#. create a ``boards/<BOARD>.overlay`` file in the application
folder, for the current board
#. create a ``<BOARD>.overlay`` file in the application folder
Here is an example :ref:`using west build <west-building-dtc-overlay-file>`.
However you set the value, it is saved in the CMake cache between builds.
The :ref:`build system <build_overview>` prints all the devicetree overlays it
finds in the configuration phase, like this:
.. code-block:: none
-- Found devicetree overlay: .../some/file.overlay
.. _use-dt-overlays:
Use devicetree overlays
***********************
See :ref:`set-devicetree-overlays` for how to add an overlay to the build.
Overlays can override node property values in multiple ways.
For example, if your BOARD.dts contains this node:
.. code-block:: DTS
/ {
soc {
serial0: serial@40002000 {
status = "okay";
current-speed = <115200>;
/* ... */
};
};
};
These are equivalent ways to override the ``current-speed`` value in an
overlay:
.. code-block:: none
/* Option 1 */
&serial0 {
current-speed = <9600>;
};
/* Option 2 */
&{/soc/serial@40002000} {
current-speed = <9600>;
};
We'll use the ``&serial0`` style for the rest of these examples.
You can add aliases to your devicetree using overlays: an alias is just a
property of the ``/aliases`` node. For example:
.. code-block:: none
/ {
aliases {
my-serial = &serial0;
};
};
Chosen nodes work the same way. For example:
.. code-block:: none
/ {
chosen {
zephyr,console = &serial0;
};
};
To delete a property (in addition to deleting properties in general, this is
how to set a boolean property to false if it's true in BOARD.dts):
.. code-block:: none
&serial0 {
/delete-property/ some-unwanted-property;
};
You can add subnodes using overlays. For example, to configure a SPI or I2C
child device on an existing bus node, do something like this:
.. code-block:: none
/* SPI device example */
&spi1 {
my_spi_device: temp-sensor@0 {
compatible = "...";
label = "TEMP_SENSOR_0";
/* reg is the chip select number, if needed;
* If present, it must match the node's unit address. */
reg = <0>;
/* Configure other SPI device properties as needed.
* Find your device's DT binding for details. */
spi-max-frequency = <4000000>;
};
};
/* I2C device example */
&i2c2 {
my_i2c_device: touchscreen@76 {
compatible = "...";
label = "TOUCHSCREEN";
/* reg is the I2C device address.
* It must match the node's unit address. */
reg = <76>;
/* Configure other I2C device properties as needed.
* Find your device's DT binding for details. */
};
};
Other bus devices can be configured similarly:
- create the device as a subnode of the parent bus
- set its properties according to its binding
Assuming you have a suitable device driver associated with the
``my_spi_device`` and ``my_i2c_device`` compatibles, you should now be able to
enable the driver via Kconfig and :ref:`get the struct device <dt-get-device>`
for your newly added bus node, then use it with that driver API.
.. _dt-create-devices:
Write device drivers using devicetree APIs
******************************************
"Devicetree-aware" :ref:`device drivers <device_model_api>` should create a
``struct device`` for each ``status = "okay"`` devicetree node with a
particular :ref:`compatible <dt-important-props>` (or related set of
compatibles) supported by the driver.
.. note::
Historically, Zephyr has used Kconfig options like :option:`CONFIG_SPI_0` and
:option:`CONFIG_I2C_1` to enable driver support for individual devices of
some type. For example, if ``CONFIG_I2C_1=y``, the SoC's I2C peripheral
driver would create a ``struct device`` for "I2C bus controller number 1".
This style predates support for devicetree in Zephyr and its use is now
discouraged. Existing device drivers may be made "devicetree-aware"
in future releases.
Writing a devicetree-aware driver begins by defining a :ref:`devicetree binding
<dt-bindings>` for the devices supported by the driver. Use existing bindings
from similar drivers as a starting point. A skeletal binding to get started
needs nothing more than this:
.. code-block:: yaml
description: <Human-readable description of your binding>
compatible: "foo-company,bar-device"
include: base.yaml
See :ref:`dts-find-binding` for more advice on locating existing bindings.
After writing your binding, your driver C file can then use the devicetree API
to find ``status = "okay"`` nodes with the desired compatible, and instantiate
a ``struct device`` for each one. There are two options for instantiating each
``struct device``: using instance numbers, and using node labels.
In either case:
- Each ``struct device``\ 's name should be set to its devicetree node's
``label`` property. This allows the driver's users to :ref:`dt-get-device` in
the usual way.
- Each device's initial configuration should use values from devicetree
properties whenever practical. This allows users to configure the driver
using :ref:`devicetree overlays <use-dt-overlays>`.
Examples for how to do this follow. They assume you've already implemented the
device-specific configuration and data structures and API functions, like this:
.. code-block:: c
/* my_driver.c */
#include <drivers/some_api.h>
/* Define data (RAM) and configuration (ROM) structures: */
struct my_dev_data {
/* per-device values to store in RAM */
};
struct my_dev_cfg {
uint32_t freq; /* Just an example: initial clock frequency in Hz */
/* other configuration to store in ROM */
};
/* Implement driver API functions (drivers/some_api.h callbacks): */
static int my_driver_api_func1(const struct device *dev, uint32_t *foo) { /* ... */ }
static int my_driver_api_func2(const struct device *dev, uint64_t bar) { /* ... */ }
static struct some_api my_api_funcs = {
.func1 = my_driver_api_func1,
.func2 = my_driver_api_func2,
};
.. _dt-create-devices-inst:
Option 1: create devices using instance numbers
===============================================
Use this option, which uses :ref:`devicetree-inst-apis`, if possible. However,
they only work when devicetree nodes for your driver's ``compatible`` are all
equivalent, and you do not need to be able to distinguish between them.
To use instance-based APIs, begin by defining ``DT_DRV_COMPAT`` to the
lowercase-and-underscores version of the compatible that the device driver
supports. For example, if your driver's compatible is ``"vnd,my-device"`` in
devicetree, you would define ``DT_DRV_COMPAT`` to ``vnd_my_device`` in your
driver C file:
.. code-block:: c
/*
* Put this near the top of the file. After the includes is a good place.
* (Note that you can therefore run "git grep DT_DRV_COMPAT drivers" in
* the zephyr Git repository to look for example drivers using this style).
*/
#define DT_DRV_COMPAT vnd_my_device
.. important::
As shown, the DT_DRV_COMPAT macro should have neither quotes nor special
characters. Remove quotes and convert special characters to underscores
when creating ``DT_DRV_COMPAT`` from the compatible property.
Finally, define an instantiation macro, which creates each ``struct device``
using instance numbers. Do this after defining ``my_api_funcs``.
.. code-block:: c
/*
* This instantiation macro is named "CREATE_MY_DEVICE".
* Its "inst" argument is an arbitrary instance number.
*
* Put this near the end of the file, e.g. after defining "my_api_funcs".
*/
#define CREATE_MY_DEVICE(inst) \
static struct my_dev_data my_data_##inst = { \
/* initialize RAM values as needed, e.g.: */ \
.freq = DT_INST_PROP(inst, clock_frequency), \
}; \
static const struct my_dev_cfg my_cfg_##inst = { \
/* initialize ROM values as needed. */ \
}; \
DEVICE_AND_API_INIT(my_dev_##inst, \
DT_INST_LABEL(inst), \
my_dev_init_function, \
&my_data_##inst, \
&my_cfg_##inst, \
MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY, \
&my_api_funcs);
Notice the use of APIs like :c:func:`DT_INST_LABEL` and :c:func:`DT_INST_PROP`
to access devicetree node data. These APIs retrieve data from the devicetree
for instance number ``inst`` of the node with compatible determined by
``DT_DRV_COMPAT``.
Finally, pass the instantiation macro to :c:func:`DT_INST_FOREACH_STATUS_OKAY`:
.. code-block:: c
/* Call the device creation macro for each instance: */
DT_INST_FOREACH_STATUS_OKAY(CREATE_MY_DEVICE)
``DT_INST_FOREACH_STATUS_OKAY`` expands to code which calls
``CREATE_MY_DEVICE`` once for each enabled node with the compatible determined
by ``DT_DRV_COMPAT``. It does not append a semicolon to the end of the
expansion of ``CREATE_MY_DEVICE``, so the macro's expansion must end in a
semicolon or function definition to support multiple devices.
Option 2: create devices using node labels
==========================================
Some device drivers cannot use instance numbers. One example is an SoC
peripheral driver which relies on vendor HAL APIs specialized for individual IP
blocks to implement Zephyr driver callbacks. Cases like this should use
:c:func:`DT_NODELABEL` to refer to individual nodes in the devicetree
representing the supported peripherals on the SoC. The devicetree.h
:ref:`devicetree-generic-apis` can then be used to access node data.
For this to work, your :ref:`SoC's dtsi file <dt-input-files>` must define node
labels like ``mydevice0``, ``mydevice1``, etc. appropriately for the IP blocks
your driver supports. The resulting devicetree usually looks something like
this:
.. code-block:: DTS
/ {
soc {
mydevice0: dev@... {
compatible = "vnd,my-device";
};
mydevice1: dev@... {
compatible = "vnd,my-device";
};
};
};
The driver can use the ``mydevice0`` and ``mydevice1`` node labels in the
devicetree to operate on specific device nodes:
.. code-block:: c
/*
* This is a convenience macro for creating a node identifier for
* the relevant devices. An example use is MYDEV(0) to refer to
* the node with label "mydevice0".
*/
#define MYDEV(idx) DT_NODELABEL(mydevice ## idx)
/*
* Define your instantiation macro; "idx" is a number like 0 for mydevice0
* or 1 for mydevice1. It uses MYDEV() to create the node label from the
* index.
*/
#define CREATE_MY_DEVICE(idx) \
static struct my_dev_data my_data_##idx = { \
/* initialize RAM values as needed, e.g.: */ \
.freq = DT_PROP(MYDEV(idx), clock_frequency), \
}; \
static const struct my_dev_cfg my_cfg_##idx = { /* ... */ }; \
DEVICE_AND_API_INIT(my_dev_##idx, \
DT_LABEL(MYDEV(idx)), \
my_dev_init_function, \
&my_data_##idx, \
&my_cfg_##idx, \
MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY, \
&my_api_funcs)
Notice the use of APIs like :c:func:`DT_LABEL` and :c:func:`DT_PROP` to access
devicetree node data.
Finally, manually detect each enabled devicetree node and use
``CREATE_MY_DEVICE`` to instantiate each ``struct device``:
.. code-block:: c
#if DT_NODE_HAS_STATUS(DT_NODELABEL(mydevice0), okay)
CREATE_MY_DEVICE(0)
#endif
#if DT_NODE_HAS_STATUS(DT_NODELABEL(mydevice1), okay)
CREATE_MY_DEVICE(1)
#endif
Since this style does not use ``DT_INST_FOREACH_STATUS_OKAY()``, the driver
author is responsible for calling ``CREATE_MY_DEVICE()`` for every possible
node, e.g. using knowledge about the peripherals available on supported SoCs.
.. _dt-drivers-that-depend:
Device drivers that depend on other devices
*******************************************
At times, one ``struct device`` depends on another ``struct device`` and
requires a pointer to it. For example, a sensor device might need a pointer to
its SPI bus controller device. Some advice:
- Write your devicetree binding in a way that permits use of
:ref:`devicetree-hw-api` from devicetree.h if possible.
- In particular, for bus devices, your driver's binding should include a
file like :zephyr_file:`dts/bindings/spi/spi-device.yaml` which provides
common definitions for devices addressable via a specific bus. This enables
use of APIs like :c:func:`DT_BUS` to obtain a node identifier for the bus
node. You can then :ref:`dt-get-device` for the bus in the usual way.
Search existing bindings and device drivers for examples.
.. _dt-apps-that-depend:
Applications that depend on board-specific devices
**************************************************
One way to allow application code to run unmodified on multiple boards is by
supporting a devicetree alias to specify the hardware specific portions, as is
done in the :ref:`blinky-sample`. The application can then be configured in
:ref:`BOARD.dts <devicetree-in-out-files>` files or via :ref:`devicetree
overlays <use-dt-overlays>`.
.. _dt-migrate-legacy:
Migrate from the legacy macros
******************************
This section shows how to migrate from the :ref:`dt-legacy-macros` to the
:ref:`devicetree.h API <dt-from-c>`. (Please feel free to :ref:`ask for help
<help>` if a use case you need is missing here and existing documentation is
not enough to figure out what to do.)
This DTS is used for examples:
.. literalinclude:: ../../../tests/lib/devicetree/legacy_api/app.overlay
:language: DTS
:start-after: start-after-here
:end-before: end-before-here
The following shows equivalent ways to access this devicetree, using legacy
macros and the new devicetree.h API.
.. warning::
The INST numbers below were carefully chosen to work. Instance numbering
properties have changed in the devicetree.h API compared to the legacy
macros, and are not guaranteed to be the same in all cases. See
:c:func:`DT_INST` for details.
.. code-block:: c
/*
* label
*
* These use the label property in /migration/gpio@1000.
* They all expand to "MGR_GPIO".
*/
/* Legacy: */
DT_VND_GPIO_1000_LABEL
DT_INST_0_VND_GPIO_LABEL
DT_ALIAS_MGR_GPIO_LABEL
/* Use these instead: */
DT_LABEL(DT_PATH(migration, gpio_1000))
DT_LABEL(DT_INST(0, vnd_gpio))
DT_LABEL(DT_ALIAS(mgr_gpio))
DT_LABEL(DT_NODELABEL(migration_gpio))
/*
* reg base addresses and sizes
*
* These use the reg property in /migration/gpio@1000.
* The base addresses all expand to 0x1000, and sizes to 0x2000.
*/
/* Legacy addresses: */
DT_VND_GPIO_1000_BASE_ADDRESS
DT_INST_0_VND_GPIO_BASE_ADDRESS
DT_ALIAS_MGR_GPIO_BASE_ADDRESS
/* Use these instead: */
DT_REG_ADDR(DT_PATH(migration, gpio_1000))
DT_REG_ADDR(DT_INST(0, vnd_gpio))
DT_REG_ADDR(DT_ALIAS(mgr_gpio))
DT_REG_ADDR(DT_NODELABEL(migration_gpio))
/* Legacy sizes: */
DT_VND_GPIO_1000_SIZE
DT_INST_0_VND_GPIO_SIZE
DT_ALIAS_MGR_GPIO_SIZE
/* Use these instead: */
DT_REG_SIZE(DT_PATH(migration, gpio_1000))
DT_REG_SIZE(DT_INST(0, vnd_gpio))
DT_REG_SIZE(DT_ALIAS(mgr_gpio))
DT_REG_SIZE(DT_NODELABEL(migration_gpio))
/*
* interrupts IRQ numbers and priorities
*
* These use the interrupts property in /migration/gpio@1000.
* The interrupt number is 0, and the priority is 1.
*/
/* Legacy interrupt numbers: */
DT_VND_GPIO_1000_IRQ_0
DT_INST_0_VND_GPIO_IRQ_0
DT_ALIAS_MGR_GPIO_IRQ_0
/* Use these instead: */
DT_IRQN(DT_PATH(migration, gpio_1000))
DT_IRQN(DT_INST(0, vnd_gpio))
DT_IRQN(DT_ALIAS(mgr_gpio))
DT_IRQN(DT_NODELABEL(migration_gpio))
/* Legacy priorities: */
DT_VND_GPIO_1000_IRQ_0_PRIORITY,
DT_INST_0_VND_GPIO_IRQ_0_PRIORITY,
DT_ALIAS_MGR_GPIO_IRQ_0_PRIORITY,
/* Use these instead: */
DT_IRQ(DT_PATH(migration, gpio_1000), priority)
DT_IRQ(DT_INST(0, vnd_gpio), priority)
DT_IRQ(DT_ALIAS(mgr_gpio), priority)
DT_IRQ(DT_NODELABEL(migration_gpio), priority)
/*
* Other property access
*
* These use the baud-rate property in /migration/serial@3000.
* They all expand to 115200.
*/
/* Legacy: */
DT_VND_SERIAL_3000_BAUD_RATE
DT_ALIAS_MGR_SERIAL_BAUD_RATE
DT_INST_0_VND_SERIAL_BAUD_RATE
/* Use these instead: */
DT_PROP(DT_PATH(migration, serial_3000), baud_rate)
DT_PROP(DT_ALIAS(mgr_serial), baud_rate)
DT_PROP(DT_NODELABEL(migration_serial), baud_rate)
DT_PROP(DT_INST(0, vnd_serial), baud_rate)
/*
* I2C bus controller label access for an I2C peripheral device.
*
* These are different ways to get the bus controller label property
* from the peripheral device /migration/i2c@1000/i2c-dev-10.
*
* They all expand to "MGR_I2C".
*/
/* Legacy: */
DT_VND_I2C_10000_VND_I2C_DEVICE_10_BUS_NAME
DT_ALIAS_MGR_I2C_DEV_BUS_NAME
DT_INST_0_VND_I2C_DEVICE_BUS_NAME
/* Use these instead (the extra #defines are just for readability): */
#define I2C_DEV_PATH DT_PATH(migration, i2c_10000, i2c_dev_10)
#define I2C_DEV_ALIAS DT_ALIAS(mgr_i2c_dev)
#define I2C_DEV_NODELABEL DT_NODELABEL(mgr_i2c_device)
#define I2C_DEV_INST DT_INST(0, vnd_i2c_device)
DT_LABEL(DT_BUS(I2C_DEV_PATH))
DT_LABEL(DT_BUS(I2C_DEV_ALIAS)))
DT_LABEL(DT_BUS(I2C_DEV_NODELABEL)))
DT_LABEL(DT_BUS(I2C_DEV_INST)))
/*
* SPI device chip-select controller.
*
* These use /migration/spi@2000/spi-dev@0. They all expand to
* "MGR_GPIO", which is the label property of /migration/gpio@1000,
* which is the SPI device's chip select pin GPIO controller. This is
* taken from the parent node's cs-gpios property.
*/
/* Legacy */
DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_CONTROLLER
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_CONTROLLER
DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_CONTROLLER
/* Use these instead (extra #defines just for readability): */
#define SPI_DEV_PATH DT_PATH(migration, spi_20000, migration_spi_dev_0)
#define SPI_DEV_ALIAS DT_ALIAS(mgr_spi_dev)
#define SPI_DEV_NODELABEL DT_NODELABEL(mgr_spi_device)
#define SPI_DEV_INST DT_INST(0, vnd_spi_device)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_PATH)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_ALIAS)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_NODELABEL)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_INST)
/*
* SPI device chip-select pin.
*
* These use /migration/spi@2000/spi-dev@0.
* They all expand to 17, which is also from cs-gpios.
*/
/* Legacy: */
DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_PIN
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_PIN
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_PIN
DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_PIN
/* Use these instead (extra #defines from above): */
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_PATH)
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_ALIAS)
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_NODEPIN)
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_INST)
/*
* SPI device chip-select pin's flags for the gpio.h API.
*
* These use /migration/spi@2000/spi-dev@0. They all expand to
* GPIO_ACTIVE_LOW (technically, its numeric value after
* preprocessing), which is also from cs-gpios.
*/
/* Legacy: */
DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_FLAGS
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_FLAGS
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_FLAGS
DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_FLAGS
/* Use these instead (extra #defines from above): */
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_PATH)
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_ALIAS)
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_NODEFLAGS)
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_INST)
.. _dt-trouble:
Troubleshoot devicetree issues
******************************
Here are some tips for fixing misbehaving devicetree code.
Try again with a pristine build directory
=========================================
See :ref:`west-building-pristine` for examples, or just delete the build
directory completely and retry.
This is general advice which is especially applicable to debugging devicetree
issues, because the outputs are created at CMake configuration time, and are
not always regenerated when one of their inputs changes.
Make sure <devicetree.h> is included
====================================
Unlike Kconfig symbols, the :file:`devicetree.h` header must be included
explicitly.
Many Zephyr header files rely on information from devicetree, so including some
other API may transitively include :file:`devicetree.h`, but that's not
guaranteed.
.. _dt-use-the-right-names:
Make sure you're using the right names
======================================
Remember that:
- In C/C++, devicetree names must be lowercased and special characters must be
converted to underscores. Zephyr's generated devicetree header has DTS names
converted in this way into the C tokens used by the preprocessor-based
``<devicetree.h>`` API.
- In overlays, use devicetree node and property names the same way they
would appear in any DTS file. Zephyr overlays are just DTS fragments.
For example, if you're trying to **get** the ``clock-frequency`` property of a
node with path ``/soc/i2c@12340000`` in a C/C++ file:
.. code-block:: c
/*
* foo.c: lowercase-and-underscores names
*/
/* Don't do this: */
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c@1234000), clock-frequency)
/* ^ ^
* @ should be _ - should be _ */
/* Do this instead: */
#define MY_CLOCK_FREQ DT_PROP(DT_PATH(soc, i2c_1234000), clock_frequency)
/* ^ ^ */
And if you're trying to **set** that property in a devicetree overlay:
.. code-block:: DTS
/*
* foo.overlay: DTS names with special characters, etc.
*/
/* Don't do this; you'll get devicetree errors. */
&{/soc/i2c_12340000/} {
clock_frequency = <115200>;
};
/* Do this instead. Overlays are just DTS fragments. */
&{/soc/i2c@12340000/} {
clock-frequency = <115200>;
};
Validate properties
===================
If you're getting a compile error reading a node property, check your node
identifier and property. For example, if you get a build error on a line that
looks like this:
.. code-block:: c
int baud_rate = DT_PROP(DT_NODELABEL(my_serial), current_speed);
Try checking the node by adding this to the file and recompiling:
.. code-block:: c
#if !DT_NODE_EXISTS(DT_NODELABEL(my_serial))
#error "whoops"
#endif
If you see the "whoops" error message when you rebuild, the node identifier
isn't referring to a valid node. :ref:`get-devicetree-outputs` and debug from
there.
Some hints for what to check next if you don't see the "whoops" error message:
- did you :ref:`dt-use-the-right-names`?
- does the :ref:`property exist <dt-checking-property-exists>`?
- does the node have a :ref:`matching binding <dt-bindings>`?
.. _missing-dt-binding:
Check for missing bindings
==========================
If the build fails to :ref:`dts-find-binding` for a node, then either the
node's ``compatible`` property is not defined, or its value has no matching
binding. If the property is set, check for typos in its name. In a devicetree
source file, ``compatible`` should look like ``"vnd,some-device"`` --
:ref:`dt-use-the-right-names`.
If your binding file is not under :file:`zephyr/dts`, you may need to set
:ref:`DTS_ROOT <dts_root>`.
Errors with DT_INST_() APIs
===========================
If you're using an API like :c:func:`DT_INST_PROP`, you must define
``DT_DRV_COMPAT`` to the lowercase-and-underscores version of the compatible
you are interested in. See :ref:`dt-create-devices-inst`.