zephyr/doc/build/dts/api-usage.rst

400 lines
13 KiB
ReStructuredText

.. _dt-from-c:
Devicetree access from C/C++
############################
This guide describes Zephyr's ``<zephyr/devicetree.h>`` API for reading the
devicetree from C source files. It assumes you're familiar with the concepts in
:ref:`devicetree-intro` and :ref:`dt-bindings`. See :ref:`dt-reference` for
reference material.
A note for Linux developers
***************************
Linux developers familiar with devicetree should be warned that the API
described here differs significantly from how devicetree is used on Linux.
Instead of generating a C header with all the devicetree data which is then
abstracted behind a macro API, the Linux kernel would instead read the
devicetree data structure in its binary form. The binary representation is
parsed at runtime, for example 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.
.. _dt-node-identifiers:
Node identifiers
****************
To get information about a particular devicetree node, you need a *node
identifier* for it. This is a just a C macro that refers to the node.
These are the main ways to get a node identifier:
By path
Use :c:macro:`DT_PATH()` along with the node's full path in the devicetree,
starting from the root node. This is mostly useful if you happen to know the
exact node you're looking for.
By node label
Use :c:macro:`DT_NODELABEL()` to get a node identifier from a :ref:`node
label <dt-node-labels>`. Node labels are often provided by SoC :file:`.dtsi`
files to give nodes names that match the SoC datasheet, like ``i2c1``,
``spi2``, etc.
By alias
Use :c:macro:`DT_ALIAS()` to get a node identifier for a property of the
special ``/aliases`` node. This is sometimes done by applications (like
:zephyr:code-sample:`blinky`, which uses the ``led0`` alias) that need to
refer to *some* device of a particular type ("the board's user LED") but
don't care which one is used.
By instance number
This is done primarily by device drivers, as instance numbers are a way to
refer to individual nodes based on a matching compatible. Get these with
:c:macro:`DT_INST()`, but be careful doing so. See below.
By chosen node
Use :c:macro:`DT_CHOSEN()` to get a node identifier for ``/chosen`` node
properties.
By parent/child
Use :c:macro:`DT_PARENT()` and :c:macro:`DT_CHILD()` to get a node identifier
for a parent or child node, starting from a node identifier you already have.
Two node identifiers which refer to the same node are identical and can be used
interchangeably.
.. _dt-node-main-ex:
Here's a DTS fragment for some imaginary hardware we'll return to throughout
this file for examples:
.. literalinclude:: main-example.dts
:language: devicetree
:start-after: start-after-here
Here are a few ways to get node identifiers for the ``i2c@40002000`` node:
- ``DT_PATH(soc, i2c_40002000)``
- ``DT_NODELABEL(i2c1)``
- ``DT_ALIAS(sensor_controller)``
- ``DT_INST(x, vnd_soc_i2c)`` for some unknown number ``x``. See the
:c:macro:`DT_INST()` documentation for details.
.. important::
Non-alphanumeric characters like dash (``-``) and the at sign (``@``) in
devicetree names are converted to underscores (``_``). The names in a DTS
are also converted to lowercase.
.. _node-ids-are-not-values:
Node identifiers are not values
*******************************
There is no way to store one in a variable. You cannot write:
.. code-block:: c
/* These will give you compiler errors: */
void *i2c_0 = DT_INST(0, vnd_soc_i2c);
unsigned int i2c_1 = DT_INST(1, vnd_soc_i2c);
long my_i2c = DT_NODELABEL(i2c1);
If you want something short to save typing, use C macros:
.. code-block:: c
/* Use something like this instead: */
#define MY_I2C DT_NODELABEL(i2c1)
#define INST(i) DT_INST(i, vnd_soc_i2c)
#define I2C_0 INST(0)
#define I2C_1 INST(1)
Property access
***************
The right API to use to read property values depends on the node and property.
- :ref:`dt-checking-property-exists`
- :ref:`simple-properties`
- :ref:`reg-properties`
- :ref:`interrupts-properties`
- :ref:`phandle-properties`
.. _dt-checking-property-exists:
Checking properties and values
==============================
You can use :c:macro:`DT_NODE_HAS_PROP()` to check if a node has a property. For
the :ref:`example devicetree <dt-node-main-ex>` above:
.. code-block:: c
DT_NODE_HAS_PROP(DT_NODELABEL(i2c1), clock_frequency) /* expands to 1 */
DT_NODE_HAS_PROP(DT_NODELABEL(i2c1), not_a_property) /* expands to 0 */
.. _simple-properties:
Simple properties
=================
Use ``DT_PROP(node_id, property)`` to read basic integer, boolean, string,
numeric array, and string array properties.
For example, to read the ``clock-frequency`` property's value in the
:ref:`above example <dt-node-main-ex>`:
.. code-block:: c
DT_PROP(DT_PATH(soc, i2c_40002000), clock_frequency) /* This is 100000, */
DT_PROP(DT_NODELABEL(i2c1), clock_frequency) /* and so is this, */
DT_PROP(DT_ALIAS(sensor_controller), clock_frequency) /* and this. */
.. important::
The DTS property ``clock-frequency`` is spelled ``clock_frequency`` in C.
That is, properties also need special characters converted to underscores.
Their names are also forced to lowercase.
Properties with ``string`` and ``boolean`` types work the exact same way. The
``DT_PROP()`` macro expands to a string literal in the case of strings, and the
number 0 or 1 in the case of booleans. For example:
.. code-block:: c
#define I2C1 DT_NODELABEL(i2c1)
DT_PROP(I2C1, status) /* expands to the string literal "okay" */
.. note::
Don't use DT_NODE_HAS_PROP() for boolean properties. Use DT_PROP() instead
as shown above. It will expand to either 0 or 1 depending on if the property
is present or absent.
Properties with type ``array``, ``uint8-array``, and ``string-array`` work
similarly, except ``DT_PROP()`` expands to an array initializer in these cases.
Here is an example devicetree fragment:
.. code-block:: devicetree
foo: foo@1234 {
a = <1000 2000 3000>; /* array */
b = [aa bb cc dd]; /* uint8-array */
c = "bar", "baz"; /* string-array */
};
Its properties can be accessed like this:
.. code-block:: c
#define FOO DT_NODELABEL(foo)
int a[] = DT_PROP(FOO, a); /* {1000, 2000, 3000} */
unsigned char b[] = DT_PROP(FOO, b); /* {0xaa, 0xbb, 0xcc, 0xdd} */
char* c[] = DT_PROP(FOO, c); /* {"foo", "bar"} */
You can use :c:macro:`DT_PROP_LEN()` to get logical array lengths in number of
elements.
.. code-block:: c
size_t a_len = DT_PROP_LEN(FOO, a); /* 3 */
size_t b_len = DT_PROP_LEN(FOO, b); /* 4 */
size_t c_len = DT_PROP_LEN(FOO, c); /* 2 */
``DT_PROP_LEN()`` cannot be used with the special ``reg`` or ``interrupts``
properties. These have alternative macros which are described next.
.. _reg-properties:
reg properties
==============
See :ref:`dt-important-props` for an introduction to ``reg``.
Given a node identifier ``node_id``, ``DT_NUM_REGS(node_id)`` is the
total number of register blocks in the node's ``reg`` property.
You **cannot** read register block addresses and lengths with ``DT_PROP(node,
reg)``. Instead, if a node only has one register block, use
:c:macro:`DT_REG_ADDR` or :c:macro:`DT_REG_SIZE`:
- ``DT_REG_ADDR(node_id)``: the given node's register block address
- ``DT_REG_SIZE(node_id)``: its size
Use :c:macro:`DT_REG_ADDR_BY_IDX` or :c:macro:`DT_REG_SIZE_BY_IDX` instead if the
node has multiple register blocks:
- ``DT_REG_ADDR_BY_IDX(node_id, idx)``: address of register block at index
``idx``
- ``DT_REG_SIZE_BY_IDX(node_id, idx)``: size of block at index ``idx``
The ``idx`` argument to these must be an integer literal or a macro that
expands to one without requiring any arithmetic. In particular, ``idx`` cannot
be a variable. This won't work:
.. code-block:: c
/* This will cause a compiler error. */
for (size_t i = 0; i < DT_NUM_REGS(node_id); i++) {
size_t addr = DT_REG_ADDR_BY_IDX(node_id, i);
}
.. _interrupts-properties:
interrupts properties
=====================
See :ref:`dt-important-props` for a brief introduction to ``interrupts``.
Given a node identifier ``node_id``, ``DT_NUM_IRQS(node_id)`` is the total
number of interrupt specifiers in the node's ``interrupts`` property.
The most general purpose API macro for accessing these is
:c:macro:`DT_IRQ_BY_IDX`:
.. code-block:: c
DT_IRQ_BY_IDX(node_id, idx, val)
Here, ``idx`` is the logical index into the ``interrupts`` array, i.e. it is
the index of an individual interrupt specifier in the property. The ``val``
argument is the name of a cell within the interrupt specifier. To use this
macro, check the bindings file for the node you are interested in to find the
``val`` names.
Most Zephyr devicetree bindings have a cell named ``irq``, which is the
interrupt number. You can use :c:macro:`DT_IRQN` as a convenient way to get a
processed view of this value.
.. warning::
Here, "processed" reflects Zephyr's devicetree :ref:`dt-scripts`, which
change the ``irq`` number in :ref:`zephyr.dts <devicetree-in-out-files>` to
handle hardware constraints on some SoCs and in accordance with Zephyr's
multilevel interrupt numbering.
This is currently not very well documented, and you'll need to read the
scripts' source code and existing drivers for more details if you are writing
a device driver.
.. _phandle-properties:
phandle properties
==================
.. note::
See :ref:`dt-phandles` for a detailed guide to phandles.
Property values can refer to other nodes using the ``&another-node`` phandle
syntax introduced in :ref:`dt-writing-property-values`. Properties which
contain phandles have type ``phandle``, ``phandles``, or ``phandle-array`` in
their bindings. We'll call these "phandle properties" for short.
You can convert a phandle to a node identifier using :c:macro:`DT_PHANDLE`,
:c:macro:`DT_PHANDLE_BY_IDX`, or :c:macro:`DT_PHANDLE_BY_NAME`, depending on the
type of property you are working with.
One common use case for phandle properties is referring to other hardware in
the tree. In this case, you usually want to convert the devicetree-level
phandle to a Zephyr driver-level :ref:`struct device <device_model_api>`.
See :ref:`dt-get-device` for ways to do that.
Another common use case is accessing specifier values in a phandle array. The
general purpose APIs for this are :c:macro:`DT_PHA_BY_IDX` and :c:macro:`DT_PHA`.
There are also hardware-specific shorthands like :c:macro:`DT_GPIO_CTLR_BY_IDX`,
:c:macro:`DT_GPIO_CTLR`,
:c:macro:`DT_GPIO_PIN_BY_IDX`, :c:macro:`DT_GPIO_PIN`,
:c:macro:`DT_GPIO_FLAGS_BY_IDX`, and :c:macro:`DT_GPIO_FLAGS`.
See :c:macro:`DT_PHA_HAS_CELL_AT_IDX` and :c:macro:`DT_PROP_HAS_IDX` for ways to
check if a specifier value is present in a phandle property.
.. _other-devicetree-apis:
Other APIs
**********
Here are pointers to some other available APIs.
- :c:macro:`DT_CHOSEN`, :c:macro:`DT_HAS_CHOSEN`: for properties
of the special ``/chosen`` node
- :c:macro:`DT_HAS_COMPAT_STATUS_OKAY`, :c:macro:`DT_NODE_HAS_COMPAT`: global- and
node-specific tests related to the ``compatible`` property
- :c:macro:`DT_BUS`: get a node's bus controller, if there is one
- :c:macro:`DT_ENUM_IDX`: for properties whose values are among a fixed list of
choices
- :ref:`devicetree-flash-api`: APIs for managing fixed flash partitions.
Also see :ref:`flash_map_api`, which wraps this in a more user-friendly API.
Device driver conveniences
**************************
Special purpose macros are available for writing device drivers, which usually
rely on :ref:`instance identifiers <dt-node-identifiers>`.
To use these, you must define ``DT_DRV_COMPAT`` to the ``compat`` value your
driver implements support for. This ``compat`` value is what you would pass to
:c:macro:`DT_INST`.
If you do that, you can access the properties of individual instances of your
compatible with less typing, like this:
.. code-block:: c
#include <zephyr/devicetree.h>
#define DT_DRV_COMPAT my_driver_compat
/* This is same thing as DT_INST(0, my_driver_compat): */
DT_DRV_INST(0)
/*
* This is the same thing as
* DT_PROP(DT_INST(0, my_driver_compat), clock_frequency)
*/
DT_INST_PROP(0, clock_frequency)
See :ref:`devicetree-inst-apis` for a generic API reference.
Hardware specific APIs
**********************
Convenience macros built on top of the above APIs are also defined to help
readability for hardware specific code. See :ref:`devicetree-hw-api` for
details.
Generated macros
****************
While the :file:`zephyr/devicetree.h` API is not generated, it does rely on a
generated C header which is put into every application build directory:
:ref:`devicetree_generated.h <dt-outputs>`. This file contains macros with
devicetree data.
These macros have tricky naming conventions which the :ref:`devicetree_api` API
abstracts away. They should be considered an implementation detail, but it's
useful to understand them since they will frequently be seen in compiler error
messages.
This section contains an Augmented Backus-Naur Form grammar for these
generated macros, with examples and more details in comments. See `RFC 7405`_
(which extends `RFC 5234`_) for a syntax specification.
.. literalinclude:: macros.bnf
:language: abnf
.. _RFC 7405: https://tools.ietf.org/html/rfc7405
.. _RFC 5234: https://tools.ietf.org/html/rfc5234