diff --git a/CODEOWNERS b/CODEOWNERS index 1ac415f9a253b..72cb46bfbf17d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -119,7 +119,9 @@ /doc/CMakeLists.txt @carlescufi /doc/scripts/ @carlescufi /doc/guides/bluetooth/ @joerchan @jhedberg @Vudentz +/doc/guides/dts/ @galak @mbolivar-nordic /doc/reference/bluetooth/ @joerchan @jhedberg @Vudentz +/doc/reference/devicetree/ @galak @mbolivar-nordic /doc/reference/kernel/other/resource_mgmt.rst @pabigot /doc/reference/networking/can* @alexanderwachter /drivers/debug/ @nashif @@ -310,6 +312,7 @@ /include/tracing/ @wentongwu @nashif /include/debug/ @nashif /include/device.h @wentongwu @nashif +/include/devicetree.h @galak /include/display/ @vanwinkeljan /include/dt-bindings/clock/kinetis_mcg.h @henrikbrixandersen /include/dt-bindings/clock/kinetis_scg.h @henrikbrixandersen diff --git a/boards/arm/nucleo_f429zi/doc/index.rst b/boards/arm/nucleo_f429zi/doc/index.rst index 41d22660997f4..dbd44b7ecd10a 100644 --- a/boards/arm/nucleo_f429zi/doc/index.rst +++ b/boards/arm/nucleo_f429zi/doc/index.rst @@ -186,7 +186,7 @@ Flash partitions for MCUBoot bootloader *************************************** The on-board STM32F429ZI MCU has 2MBs of internal flash memory. To use `MCUboot`_, -define a :ref:`Zephyr partition table ` for the flash memory in +define a :ref:`Zephyr partition table ` for the flash memory in its devicetree file ``nucleo_f429zi.dts``. As a reference, a partition table for MCUBoot is already defined in the devicetree file, with these settings: diff --git a/cmake/dts.cmake b/cmake/dts.cmake index dec9c256e64d1..6dbd2acca05dc 100644 --- a/cmake/dts.cmake +++ b/cmake/dts.cmake @@ -4,16 +4,22 @@ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/generated) # Zephyr code can configure itself based on a KConfig'uration with the # header file autoconf.h. There exists an analogous file devicetree_unfixed.h -# that allows configuration based on information encoded in DTS. +# that allows configuration based on information encoded in DTS, and a similar +# file with legacy contents called devicetree_unfixed_legacy.h. # -# Here we call on dtc, the gcc preprocessor, and -# scripts/dts/gen_defines.py to generate this header file at -# CMake configure-time. +# Here we call on dtc, the gcc preprocessor, +# scripts/dts/gen_defines.py, and scripts/dts/gen_legacy_defines.py to +# generate various DT-related files at CMake configure-time. # -# See ~/zephyr/doc/dts -set(DEVICETREE_UNFIXED_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_unfixed.h) -set(DEVICETREE_CONF ${PROJECT_BINARY_DIR}/include/generated/devicetree.conf) -set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp) +# The devicetree.conf file is still needed by some deprecated +# functions in kconfigfunctions.py. +# +# See the Devicetree user guide in the Zephyr documentation for details. +set(ZEPHYR_DTS ${PROJECT_BINARY_DIR}/zephyr.dts) +set(DEVICETREE_UNFIXED_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_unfixed.h) +set(DEVICETREE_UNFIXED_LEGACY_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_legacy_unfixed.h) +set(DEVICETREE_CONF ${PROJECT_BINARY_DIR}/include/generated/devicetree.conf) +set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp) set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts) @@ -67,9 +73,9 @@ if(SUPPORTS_DTS) -include ${dts_file}) if(i EQUAL 0) - message(STATUS "Loading ${dts_file} as base") + message(STATUS "Found BOARD.dts: ${dts_file}") else() - message(STATUS "Overlaying ${dts_file}") + message(STATUS "Found devicetree overlay: ${dts_file}") endif() math(EXPR i "${i}+1") @@ -190,25 +196,49 @@ if(SUPPORTS_DTS) endif(DTC) # - # Run gen_defines.py to create a .conf file and a header file + # Run gen_defines.py to create a header file and zephyr.dts. # - set(CMD_NEW_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_defines.py + set(CMD_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_defines.py --dts ${BOARD}.dts.pre.tmp --dtc-flags '${EXTRA_DTC_FLAGS}' --bindings-dirs ${DTS_ROOT_BINDINGS} - --conf-out ${DEVICETREE_CONF} --header-out ${DEVICETREE_UNFIXED_H} - --dts-out ${PROJECT_BINARY_DIR}/zephyr.dts # As a debugging aid + --dts-out ${ZEPHYR_DTS} # As a debugging aid + ) + + # + # Run gen_legacy_defines.py to create a header file with legacy contents + # and a .conf file. + # + + set(CMD_LEGACY_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_legacy_defines.py + --dts ${BOARD}.dts.pre.tmp + --dtc-flags '${EXTRA_DTC_FLAGS}' + --bindings-dirs ${DTS_ROOT_BINDINGS} + --header-out ${DEVICETREE_UNFIXED_LEGACY_H} + --conf-out ${DEVICETREE_CONF} ) execute_process( - COMMAND ${CMD_NEW_EXTRACT} + COMMAND ${CMD_EXTRACT} + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + RESULT_VARIABLE ret + ) + if(NOT "${ret}" STREQUAL "0") + message(FATAL_ERROR "gen_defines.py failed with return code: ${ret}") + else() + message(STATUS "Generated zephyr.dts: ${ZEPHYR_DTS}") + message(STATUS "Generated devicetree_unfixed.h: ${DEVICETREE_UNFIXED_H}") + endif() + + execute_process( + COMMAND ${CMD_LEGACY_EXTRACT} WORKING_DIRECTORY ${PROJECT_BINARY_DIR} RESULT_VARIABLE ret ) if(NOT "${ret}" STREQUAL "0") - message(FATAL_ERROR "new extractor failed with return code: ${ret}") + message(FATAL_ERROR "gen_legacy_defines.py failed with return code: ${ret}") endif() # A file that used to be generated by 'dtc'. zephyr.dts is the new @@ -218,4 +248,5 @@ if(SUPPORTS_DTS) else() file(WRITE ${DEVICETREE_UNFIXED_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */") + file(WRITE ${DEVICETREE_UNFIXED_LEGACY_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */") endif(SUPPORTS_DTS) diff --git a/doc/application/index.rst b/doc/application/index.rst index 60dfd5b38444f..06b2afc8ddae9 100644 --- a/doc/application/index.rst +++ b/doc/application/index.rst @@ -120,7 +120,7 @@ subdirectories which are not described here. Device driver code. :file:`dts` - :ref:`devicetree` source files used to describe non-discoverable + :ref:`devicetree ` source files used to describe non-discoverable board-specific hardware details. :file:`ext` @@ -225,8 +225,8 @@ Follow these steps to create a new application directory. (Refer to #. Set Kconfig configuration options. See :ref:`application-kconfig`. -#. Optionally, you can also configure any devicetree overlays needed by your - application. See :ref:`application_dt` below for details. +#. Configure any devicetree overlays needed by your application. + See :ref:`set-devicetree-overlays`. .. _important-build-vars: @@ -268,11 +268,10 @@ should know about. See :ref:`initial-conf` for more information. -* :makevar:`DTC_OVERLAY_FILE`: Indicates the name of one or more devicetree - overlay files. Multiple filenames can be separated with either spaces or - semicolons. Each file includes devicetree values that override the default - DT values. See :ref:`application_dt` below for details on devicetree - overlays, and :ref:`devicetree` for an overview on devicetree and Zephyr. +* :makevar:`DTC_OVERLAY_FILE`: One or more devicetree overlay files to use. + Multiple files can be separated with spaces or semicolons. + See :ref:`set-devicetree-overlays` for examples and :ref:`devicetree-intro` + for information about devicetree and Zephyr. * :makevar:`ZEPHYR_MODULES`: A CMake list containing absolute paths of additional directories with source code, Kconfig, etc. that should be used in @@ -721,6 +720,8 @@ Zephyr binary into your application directory. You can also define the ``SOC_ROOT`` variable in the application :file:`CMakeLists.txt` file. +.. _dts_root: + DeviceTree Definitions ====================== @@ -1085,11 +1086,9 @@ Make sure to follow these steps in order. See :ref:`initial-conf` for more information. -#. If your application uses a devicetree overlay file or files other than - the usual :file:`.overlay`, add lines setting the - :makevar:`DTC_OVERLAY_FILE` variable to these files appropriately. - - More details are available below in :ref:`application_dt`. +#. If your application uses devicetree overlays, you may need to set + :ref:`DTC_OVERLAY_FILE `. + See :ref:`set-devicetree-overlays`. #. If your application has its own kernel configuration options, create a :file:`Kconfig` file in the same directory as your @@ -1205,59 +1204,10 @@ The other pages in the :ref:`Kconfig section of the manual ` are also worth going through, especially if you planning to add new configuration options. -.. _application_dt: - Devicetree Overlays =================== -As described in :ref:`devicetree`, Zephyr uses devicetree to describe the -hardware it runs on. This section describes how you can modify an application -build's devicetree using overlay files. For additional information regarding -the relationship between devicetree and Kconfig see :ref:`dt_vs_kconfig`. For -an example of how to use custom overlays with ``west build``, see -:ref:`west-building-cmake-args`. - -In some cases the information contained in devicetree files is closely -connected to the software and might need to be modified using the overlay file -concept. This can be relevant for many of the different devicetree nodes, but -is particularly useful for :ref:`certain types of nodes `. - -Overlay files, which customarily have the :file:`.overlay` extension, -contain devicetree fragments which add to or modify the devicetree -used while building a Zephyr application. To add an overlay file or -files to the build, set the CMake variable :makevar:`DTC_OVERLAY_FILE` -to a whitespace-separated list of your overlay files. - -The Zephyr build system begins creation of a devicetree by running -the C preprocessor on a file which includes the following: - -#. The board's devicetree source file, which by default is the Zephyr - file :file:`boards///.dts`. (This location - can be overridden by setting the :makevar:`DTS_SOURCE` CMake - variable.) - -#. Any file or files given by the :makevar:`DTC_OVERLAY_FILE` CMake - variable. - -The Zephyr build system determines the ``DTC_OVERLAY_FILE`` value by -looking at these potential definition locations, in order, until a value -is determined, and then stops looking: - -1. the cmake command line (``-DDTC_OVERLAY_FILE=filename``) -#. the cmake variable cache (from a previous cmake run) -#. a ``CMakeLists.txt`` file in your application folder -#. a ``DTC_OVERLAY_FILE`` environment variable (deprecated) -#. a ``boards/.overlay`` file in your application folder, - for your specified ```` -#. a ``.overlay`` file in your application folder, for - your specified ```` - -If :makevar:`DTC_OVERLAY_FILE` specifies multiple files, they are -included in order by the C preprocessor. - -After running the preprocessor, the final devicetree used in the -build is created by running the devicetree compiler, ``dtc``, on the -preprocessor output. +See :ref:`set-devicetree-overlays`. Application-Specific Code ************************* diff --git a/doc/getting_started/toolchain_custom_cmake.rst b/doc/getting_started/toolchain_custom_cmake.rst index 2ae4fe98cfbbe..29955f856f10e 100644 --- a/doc/getting_started/toolchain_custom_cmake.rst +++ b/doc/getting_started/toolchain_custom_cmake.rst @@ -14,8 +14,8 @@ Zephyr will then include the toolchain cmake files located in the :file:`TOOLCHAIN_ROOT` directory: - :file:`cmake/toolchain/generic.cmake`: configures the toolchain for "generic" - use, which mostly means running the C preprocessor on the generated - :ref:`devicetree` file. + use, which mostly means running the C preprocessor on :ref:`devicetree + input files `. - :file:`cmake/toolchain/target.cmake`: configures the toolchain for "target" use, i.e. building Zephyr and your application's source code. diff --git a/doc/guides/build/index.rst b/doc/guides/build/index.rst index fef217fb305e7..0c7fbf145216f 100644 --- a/doc/guides/build/index.rst +++ b/doc/guides/build/index.rst @@ -51,7 +51,7 @@ Devicetree from ``dtc`` is unused otherwise. The above is just a brief overview. For more information on devicetree, see - :ref:`devicetree`. + :ref:`dt-guide`. Devicetree fixups Files named :file:`dts_fixup.h` from the target’s architecture, SoC, board, diff --git a/doc/guides/device_mgmt/dfu.rst b/doc/guides/device_mgmt/dfu.rst index a7795d672c470..015b26f508ea7 100644 --- a/doc/guides/device_mgmt/dfu.rst +++ b/doc/guides/device_mgmt/dfu.rst @@ -35,7 +35,7 @@ is the boot loader used with Zephyr. The source code itself is hosted in the In order to use MCUboot with Zephyr you need to take the following into account: 1. You will need to define the :ref:`mcuboot_partitions` required by MCUboot in - the :ref:`flash_partitions`. + the :ref:`legacy_flash_partitions`. 2. Your application's :file:`.conf` file needs to enable the :option:`CONFIG_BOOTLOADER_MCUBOOT` Kconfig option in order for Zephyr to be built in an MCUboot-compatible manner diff --git a/doc/guides/dts/api-usage.rst b/doc/guides/dts/api-usage.rst new file mode 100644 index 0000000000000..104d41baeb0a6 --- /dev/null +++ b/doc/guides/dts/api-usage.rst @@ -0,0 +1,386 @@ +.. _dt-from-c: + +Devicetree access from C/C++ +############################ + +This guide describes Zephyr's ```` 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:`devicetree_api` for +API reference documentation. + +.. _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. +There are four types, each of which is created with a different macro: + +Path identifiers + These use the node's full path in the devicetree, starting from the + root node. They are mostly useful if you happen to know the exact node + you're looking for. Create these with :c:func:`DT_PATH()`. + +Node label identifiers + Unique names which can be given to each node where it is + defined. These are often used when the SoC :file:`.dtsi` gives nodes + names that match the SoC datasheet, like ``i2c_1``, ``spi_2``, etc. + Create these with :c:func:`DT_NODELABEL()`. + +Alias identifiers + These are created for properties of the special ``/aliases`` node. They are + useful for supporting applications (like :ref:`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 exact one is + used. Create these with :c:func:`DT_ALIAS()`. + +Instance identifiers + These are used primarily by device drivers, as they're a way to refer to + nodes based on their matching compatible. Create these with + :c:func:`DT_INST()`, but be careful doing so. + +.. _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: DTS + :start-after: start-after-here + +Here are node identifiers of each type 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:func:`DT_INST()` documentation for details. + +These identifiers are precisely equivalent: they refer to the same node, and +are otherwise interchangeable in the following examples. + +.. 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) + +.. _not-all-dt-nodes: + +Not all nodes are usable +************************ + +Just because :ref:`zephyr.dts ` has a node doesn't mean you can use +it from a C or C++ source file. The node has to have a matching binding, and +its ``status`` property must be ``"okay"`` (instead of, say, ``"disabled"``). +Use :c:func:`DT_HAS_NODE` to check if a node identifier is valid for use. The +value is 1 if yes, and 0 if no. + +Here are some examples from the :ref:`above devicetree `: + +.. code-block:: c + + DT_HAS_NODE(DT_PATH(soc, i2c_40002000)) /* 1: node has binding and is enabled */ + DT_HAS_NODE(DT_ALIAS(sensor_controller)) /* 1: that's an alias for the same node */ + DT_HAS_NODE(DT_NODELABEL(i2c1)) /* 1: that's also the same node */ + + DT_HAS_NODE(DT_PATH(i2c_40002000)) /* 0: there's no such node */ + DT_HAS_NODE(DT_PATH(soc)) /* 0: the /soc node has no binding */ + +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:func:`DT_NODE_HAS_PROP()` to check if a node has a property. For +the :ref:`example devicetree ` 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 `: + +.. 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" */ + +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:: DTS + + 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:func:`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()`` takes devicetree semantics into account for special +properties also. This means that it returns logical lengths when used with the +``reg`` and ``interrupts`` properties described next. + +.. _reg-properties: + +reg properties +============== + +See :ref:`dt-important-props` for an introduction to ``reg``. + +Given a node identifier ``node_id``, ``DT_PROP_LEN(node_id, reg)`` is the +total number of register blocks in the node's ``reg``. However, you **cannot** +read register block addresses and lengths with ``DT_PROP(node, reg)``. + +Instead, if a node only has one register block, use :c:func:`DT_REG_ADDR` or +:c:func:`DT_REG_SIZE`: + +- ``DT_REG_ADDR(node_id)``: the given node's register block address +- ``DT_REG_SIZE(node_id)``: its size + +Use :c:func:`DT_REG_ADDR_BY_IDX` or :c:func:`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_PROP_LEN(node_id, reg); 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``. + +The most general purpose API macro for accessing these is :c:func:`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:func:`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 ` 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 +================== + +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:func:`DT_PHANDLE`, +:c:func:`DT_PHANDLE_BY_IDX`, or :c:func:`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 `. +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:func:`DT_PHA_BY_IDX` and :c:func:`DT_PHA`. +There also hardware-specific shorthands like :c:func:`DT_GPIO_LABEL_BY_IDX`, +:c:func:`DT_GPIO_LABEL`, :c:func:`DT_GPIO_PIN_BY_IDX`, :c:func:`DT_GPIO_PIN`, +:c:func:`DT_GPIO_FLAGS_BY_IDX`, and :c:func:`DT_GPIO_FLAGS`. + +See :c:func:`DT_PHA_HAS_CELL_AT_IDX` and :c:func:`DT_PROP_HAS_IDX` for ways to +check if a specifier value is present. + +.. _other-devicetree-apis: + +Other APIs +********** + +Here are pointers to some other available APIs. + +- :c:func:`DT_CHOSEN`, :c:func:`DT_HAS_CHOSEN`: for properties + of the special ``/chosen`` node +- :c:func:`DT_HAS_COMPAT`: test if a compatible has a binding and at least one + enabled node +- :c:func:`DT_BUS`: get a node's bus controller, if there is one +- :c:func:`DT_ENUM_IDX`: for properties whose values are among a fixed list of + choices + +Flash partitions +**************** + +These currently must be managed via the legacy API. See +:ref:`legacy_flash_partitions`. + +Device driver conveniences +************************** + +Special purpose macros are available for writing device drivers, which usually +rely on :ref:`instance 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:func:`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 + + #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:`devicetree.h` API is not generated, it does rely on a +generated C header which is put into every application build directory: +:ref:`devicetree_unfixed.h `. This file contains macros with +devicetree data. + +These macros have tricky naming conventions which the `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 diff --git a/doc/guides/dts/bindings.rst b/doc/guides/dts/bindings.rst index 978e8451b234b..19d2a91e5d5d4 100644 --- a/doc/guides/dts/bindings.rst +++ b/doc/guides/dts/bindings.rst @@ -3,16 +3,16 @@ Devicetree bindings ################### -A :ref:`devicetree` on its own is only half the story for describing the -available hardware devices. The tree itself doesn't tell the :ref:`build system -` which pieces of information are useful to :ref:`device -drivers `, or what :ref:`C macros ` to generate from -the devicetree itself. +A devicetree on its own is only half the story for describing hardware. The +devicetree format itself is relatively unstructured, and doesn't tell the +:ref:`build system ` which pieces of information in a +particular devicetree are useful to :ref:`device drivers ` or +:ref:`applications `. *Devicetree bindings* provide the other half of this information. Zephyr -devicetree bindings are files in YAML format. Each binding describes the -contents of a devicetree node in a way that lets the build system decide which -macros to generate for it. +devicetree bindings are YAML files in a custom format (Zephyr does not use the +dt-schema tools used by the Linux kernel). The build system uses bindings +when generating code for :ref:`dt-from-c`. .. _dt-binding-compat: diff --git a/doc/guides/dts/design.rst b/doc/guides/dts/design.rst index 65f5e2fffec95..975f2c83955ab 100644 --- a/doc/guides/dts/design.rst +++ b/doc/guides/dts/design.rst @@ -16,7 +16,7 @@ Zephyr shall obtain its hardware descriptions exclusively from devicetree. Examples ======== -- New device drivers shall use :ref:`existence macros ` to +- New device drivers shall use devicetree APIs such as :c:func:`DT_HAS_NODE` to determine whether a device is available and enabled. - In-tree sample applications shall use :ref:`aliases ` to @@ -27,7 +27,7 @@ Examples Example remaining work ====================== -- Zephyr's :ref:`sanitycheck_script` currently uses :file:`board.yaml` files to +- Zephyr's :ref:`sanitycheck_script` currently use :file:`board.yaml` files to determine the hardware supported by a board. This should be obtained from devicetree instead. @@ -79,8 +79,8 @@ Example remaining work capable of representing in Zephyr's own bindings. - Due to namespace collisions and inflexibility in the bindings language, - Zephyr-specific :ref:`dt-macros` do not support the full set of possible node - paths and bindings supported by Linux. + Zephyr cannot support the full set of possible node paths and bindings + supported by Linux. - Devicetree source sharing between Zephyr and Linux is not done. diff --git a/doc/guides/dts/howtos.rst b/doc/guides/dts/howtos.rst index e99909c16a976..20b6bb0b384c0 100644 --- a/doc/guides/dts/howtos.rst +++ b/doc/guides/dts/howtos.rst @@ -1,479 +1,548 @@ +.. _dt-howtos: + Devicetree HOWTOs ################# -This page has advice for getting things done with :ref:`devicetree` in -Zephyr. +This page has step-by-step advice for getting things done with devicetree. -.. This page could use some more love, especially giving advice to - driver writers about how to allocate their struct devices. +.. _get-devicetree-outputs: -Adding support for a board -************************** +Get your devicetree and generated header +**************************************** -Devicetree is currently supported on all embedded targets except posix -(boards/posix). +A board's devicetree (:ref:`BOARD.dts `) 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///.dtsi``, but this can be time consuming. -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. +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. -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. +Luckily, there is an easy way to do both: build your application. -.. _dt_k6x_example: +For example, using west and the :ref:`qemu_cortex_m3` board to build +:ref:`hello_world`, forcing CMake to re-run: -Example: FRDM-K64F and Hexiwear K64 -=================================== +.. code-block:: sh -.. Give the filenames instead of the full paths below, as it's easier to read. - The cramped 'foo.dts' style avoids extra spaces before commas. + west build -b qemu_cortex_m3 -s samples/hello_world --cmake -The FRDM-K64F and Hexiwear K64 board devicetrees are defined in -:zephyr_file:`frdm_k64fs.dts ` and -:zephyr_file:`hexiwear_k64.dts ` -respectively. Both boards have NXP SoCs from the same Kinetis SoC family, the -K6X. +The build system prints the output file locations: -Common devicetree definitions for K6X are stored in :zephyr_file:`nxp_k6x.dtsi -`, which is included by both board :file:`.dts` -files. :zephyr_file:`nxp_k6x.dtsi` in turn includes -:zephyr_file:`armv7-m.dtsi`, which has common definitions -for Arm v7-M cores. +.. code-block:: none -Since :zephyr_file:`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): + -- 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 -.. code-block:: none +Change ``qemu_cortex_m3`` to the board you are using, of course. - can0: can@40024000 { - ... - status = "disabled"; - ... - }; +.. _dt-get-device: -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. +Get a struct device from a devicetree node +****************************************** -For example, FRDM-K64 (but not Hexiwear K64) :file:`.dts` enables the CAN -controller and sets the bus speed: +When writing Zephyr applications, you'll often want to get a driver-level +:ref:`struct device ` corresponding to a devicetree node. -.. code-block:: none +For example, with this devicetree fragment, you might want the struct device +for ``serial@40002000``: + +.. code-block:: DTS - &can0 { - status = "okay"; - bus-speed = <125000>; + / { + soc { + serial0: serial@40002000 { + status = "okay"; + current-speed = <115200>; + /* ... */ + }; + }; + + aliases { + my-serial = &serial0; + }; + + chosen { + zephyr,console = &serial0; + }; }; -The ``&can0 { ... };`` syntax adds/overrides properties on the node with label -``can0``, i.e. the ``can@4002400`` node defined in the :file:`.dtsi` file. +Start by making a :ref:`node identifier ` 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: -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. +.. code-block:: c -Devicetree Source File Template -=============================== + /* Option 1: by node label */ + #define MY_SERIAL DT_NODELABEL(serial0) -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. + /* Option 2: by alias */ + #define MY_SERIAL DT_ALIAS(my_serial) -.. code-block:: none + /* Option 3: by chosen node */ + #define MY_SERIAL DT_CHOSEN(zephyr_console) - /dts-v1/; + /* Option 4: by path */ + #define MY_SERIAL DT_PATH(soc, serial_40002000) - #include +Once you have a node identifier, get the ``struct device`` by combining +:c:func:`DT_LABEL` with :c:func:`device_get_binding`: - / { - model = "Human readable board name"; - compatible = "vendor,soc-on-your-board's-mcu"; - /* rest of file */ - }; +.. code-block:: c -You can use other board :file:`.dts` files as a starting point. + struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL)); -The following is a more precise list of required files: +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. -* Base architecture support +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()``. - * 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. +If you're having trouble, see :ref:`dt-trouble`. The first thing to check is +that the node is enabled (``status = "okay"``) and has a matching binding, like +this: -* SoC family support +.. code-block:: c + + #define MY_SERIAL DT_NODELABEL(my_serial) + + #if DT_HAS_NODE(MY_SERIAL) + struct device *uart_dev = device_get_binding(DT_LABEL(MY_SERIAL)); + #else + #error "Node is disabled or has no matching binding" + #endif - * 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. +If you see the ``#error`` output, something is wrong with either your +devicetree or bindings. -* Board specific support +.. _dts-find-binding: - * 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. +Find a devicetree binding +************************* - * Flash device node might specify flash partitions. For more details see - :ref:`flash_partitions` +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. - * 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. +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: -* Fixup files +.. code-block:: c - * 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. + /* + * [...] + * 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: -* Overlay Files (optional) +.. code-block:: c - * 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. + /* + * Devicetree node: + * /flash@0 + * + * Binding (compatible = soc-nv-flash): + * $ZEPHYR_BASE/dts/bindings/mtd/soc-nv-flash.yaml + * [...] + */ -.. _dt-alias-chosen: +See :ref:`missing-dt-binding` for troubleshooting. -``aliases`` and ``chosen`` nodes -================================ +.. _set-devicetree-overlays: -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: +Set devicetree overlays +*********************** -.. code-block:: yaml +Devicetree overlays are explained in :ref:`devicetree-intro`. The CMake +variable :makevar:`DTC_OVERLAY_FILE` contains a space- or colon-separated list +of overlays. If :makevar:`DTC_OVERLAY_FILE` specifies multiple files, they are +included in that order by the C preprocessor. - aliases { - led0 = &led0; - sw0 = &button0; - sw1 = &button1; - uart-0 = &uart0; - uart-1 = &uart1; +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/.overlay`` file in the application + folder, for the current board +#. create a ``.overlay`` file in the application folder + +Here is an example :ref:`using west build `. +However you set the value, it is saved in the CMake cache between builds. + +The :ref:`build system ` 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>; + /* ... */ + }; + }; }; -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: +These are equivalent ways to override the ``current-speed`` value in an +overlay: -.. code-block:: yaml +.. code-block:: none - chosen { - zephyr,shell-uart = &uart1; + /* Option 1 */ + &serial0 { + current-speed = <9600>; }; -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 -`. - -.. 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 ` -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__`` -:ref:`dt-existence-macros`. - -.. _flash_partitions: - -Managing flash partitions -************************* + /* Option 2 */ + &{/soc/serial@40002000} { + current-speed = <9600>; + }; -Devicetree can be used to describe a partition layout for any flash -device in the system. +We'll use the ``&serial0`` style for the rest of these examples. -Two important uses for this mechanism are: +You can add aliases to your devicetree using overlays: an alias is just a +property of the ``/aliases`` node. For example: -#. To force the Zephyr image to be linked into a specific area on - Flash. +.. code-block:: none - 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. + / { + aliases { + my-serial = &serial0; + }; + }; -#. 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. +Chosen nodes work the same way. For example: - 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. +.. code-block:: none -Partitions are generally managed using device tree overlays. Refer to -:ref:`application_dt` for details on using overlay files. + / { + chosen { + zephyr,console = &serial0; + }; + }; -Defining Partitions -=================== +To delete a property (this is how you override a true boolean property to a +false value): -The partition layout for a flash device is described inside the -``partitions`` child node of the flash device's node in the device -tree. +.. code-block:: none -You can define partitions for any flash device on the system. + /* Option 1 */ + &serial0 { + /delete-property/ some-unwanted-property; + }; -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: +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: -.. 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>; + /* 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>; + }; + }; - /* Define your partitions here; see below */ - }; + /* 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. */ }; + }; -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. +Other bus devices can be configured similarly: -The content of the ``partitions`` node looks like this: +- create the device as a subnode of the parent bus +- set its properties according to its binding -.. code-block:: none +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 ` +for your newly added bus node, then use it with that driver API. - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; +.. _dt-driver-howto: - partition1_label: partition@START_OFFSET_1 { - label = "partition1_name"; - reg = <0xSTART_OFFSET_1 0xSIZE_1>; - }; +Create struct devices in a driver +********************************* - /* ... */ +If you're writing a device driver, it should be devicetree aware so that +applications can configure it and access devices as described above. In short, +you must create a ``struct device`` for every enabled instance of the +compatible that the device driver supports, and set each device's name to the +``DT_LABEL()`` of its devicetree node. - partitionN_label: partition@START_OFFSET_N { - label = "partitionN_name"; - reg = <0xSTART_OFFSET_N 0xSIZE_N>; - }; - }; +The :file:`devicetree.h` API has helpers for writing device drivers based on +:ref:`DT_INST node identifiers ` for each of the possible +instance numbers on your SoC. -Where: +Assuming you're using instances, start by defining ``DT_DRV_COMPAT`` at the top +of the file to the lowercase-and-underscores version of the :ref:`compatible +` that the device driver is handling. For example, if your +driver is handling nodes with compatible ``"vnd,my-device"``, you should put +this at the top of your driver: -- ``partitionX_label`` are device tree labels that can be used - elsewhere in the device tree to refer to the partition +.. code-block:: c -- ``partitionX_name`` controls how defines generated by the Zephyr - build system for this partition will be named + #define DT_DRV_COMPAT vnd_my_device -- ``START_OFFSET_x`` is the start offset in hexadecimal notation of - the partition from the beginning of the flash device +.. important:: -- ``SIZE_x`` is the hexadecimal size, in bytes, of the flash partition + The DT_DRV_COMPAT macro should have neither quotes nor special characters. + Remove quotes and convert special characters to underscores. -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. +The typical pattern after that is to define the API functions, then define a +macro which creates the device by instance number, and then call it for each +enabled instance. Currently, this looks like this: -Example Primary Flash Partition Layout -====================================== +.. code-block:: c -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. + #include -.. code-block:: none + #include + #define DT_DRV_COMPAT vnd_my_device - &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>; - }; - }; - }; + /* + * Define RAM and ROM structures: + */ + + struct my_dev_data { + /* per-device values to store in RAM */ + }; -Linking Zephyr Within a Partition -================================= + struct my_dev_cfg { + u32_t freq; /* Just an example: clock frequency in Hz */ + /* other device configuration to store in ROM */ + }; -To force the linker to output a Zephyr image within a given flash -partition, add this to a device tree overlay: + /* + * Implement some_api.h callbacks: + */ -.. code-block:: none + struct some_api my_api_funcs = { /* ... */ }; - / { - chosen { - zephyr,code-partition = &slot0_partition; - }; - }; + /* + * Now use DT_INST APIs to create a struct device for each enabled node: + */ -Then, enable the :option:`CONFIG_USE_DT_CODE_PARTITION` Kconfig option. + #define CREATE_MY_DEVICE(inst) \ + static struct my_dev_data my_dev_data_##inst = { \ + /* initialize RAM values as needed */ \ + }; \ + static const struct my_dev_cfg my_dev_cfg_##inst = { \ + /* initialize ROM values, usually from devicetree */ \ + .freq = DT_INST_PROP(inst, clock_frequency), \ + /* ... */ \ + }; \ + DEVICE_AND_API_INIT(my_dev_##inst, \ + DT_INST_LABEL(inst), \ + my_dev_init_function, \ + &my_dev_data_##inst, \ + &my_dev_cfg_##inst, \ + MY_DEV_INIT_LEVEL, MY_DEV_INIT_PRIORITY, \ + &my_api_funcs) -Flash Partition Macros -====================== + #if DT_HAS_NODE(DT_DRV_INST(0)) + CREATE_MY_DEVICE(0); + #endif -The Zephyr build system generates definitions for each flash device -partition. These definitions are available to any files which -include ````. + #if DT_HAS_NODE(DT_DRV_INST(1)) + CREATE_MY_DEVICE(1); + #endif -Consider this flash partition: + /* And so on, for all "possible" instance numbers you need to support. */ -.. code-block:: none +Notice the use of :c:func:`DT_INST_PROP` and :c:func:`DT_DRV_INST`. These are +helpers which rely on ``DT_DRV_COMPAT`` to choose devicetree nodes of a chosen +compatible at a given index. - dts_label: partition@START_OFFSET { - label = "def-name"; - reg = <0xSTART_OFFSET 0xSIZE>; - }; +As shown above, the driver uses additional information from +:file:`devicetree.h` to create :ref:`struct device ` instances +than just the node label. Devicetree property values used to configure the +device at boot time are stored in ROM in the value pointed to by a +``device->config->config_info`` field. This allows users to configure your +driver using overlays. + +The Zephyr convention is to name each ``struct device`` using its devicetree +node's ``label`` property using ``DT_INST_LABEL()``. This allows applications +to :ref:`dt-get-device`. + +.. _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. -The build system will generate the following corresponding defines: +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 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 + ```` 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 - #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 + /* + * 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) + /* ^ ^ */ -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. +And if you're trying to **set** that property in a devicetree overlay: -.. _mcuboot_partitions: +.. code-block:: DTS -MCUboot Partitions -================== + /* + * foo.overlay: DTS names with special characters, etc. + */ -`MCUboot`_ is a secure bootloader for 32-bit microcontrollers. + /* Don't do this; you'll get devicetree errors. */ + &{/soc/i2c_12340000/} { + clock_frequency = <115200>; + }; -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. + /* Do this instead. Overlays are just DTS fragments. */ + &{/soc/i2c@12340000/} { + clock-frequency = <115200>; + }; -The device tree labels for these partitions are: +Validate properties +=================== -**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. +If you're getting a compile error reading a node property, remember +:ref:`not-all-dt-nodes`, then check your node identifier and property. +For example, if you get a build error on a line that looks like this: -**slot0_partition** - MCUboot loads the executable application image from this - partition. Any application bootable by MCUboot must be linked to run - from this partition. +.. code-block:: c -**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. + int baud_rate = DT_PROP(DT_NODELABEL(my_serial), current_speed); -**scratch_partition** - This partition is used as temporary storage while swapping the - contents of ``slot0_partition`` and ``slot1_partition``. +Try checking the node by adding this to the file and recompiling: -.. important:: +.. code-block:: c + + #if DT_HAS_NODE(DT_NODELABEL(my_serial)) == 0 + #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: + +- :ref:`dt-use-the-right-names` +- Is the node's ``status`` property set to ``"okay"``? If not, it's disabled. + The generated header will tell you if the node is disabled. +- Does the node have a matching binding? The generated header also tells you + this information for each node; see :ref:`dts-find-binding`. +- Does the property exist? See :ref:`dt-checking-property-exists`. + +If you're sure the property is defined but ``DT_NODE_HAS_PROP()`` disagrees, +check for a missing binding. - Upgrade images are only temporarily stored in ``slot1_partition``. - They must be linked to execute of out of ``slot0_partition``. +.. _missing-dt-binding: -See the `MCUboot documentation`_ for more details on these partitions. +Check for missing bindings +========================== -.. _MCUboot: https://mcuboot.com/ +If the build fails to :ref:`dts-find-binding` for a node, then either the +node's ``compatible`` property is missing, 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`. -.. _MCUboot documentation: - https://github.com/runtimeco/mcuboot/blob/master/docs/design.md#image-slots +If your binding file is not under :file:`zephyr/dts`, you may need to set +:ref:`DTS_ROOT `. -File System Partitions -====================== +Errors with DT_INST_() APIs +=========================== -**storage_partition** - This is the area where e.g. LittleFS or NVS or FCB expects its partition. +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-driver-howto`. diff --git a/doc/guides/dts/index.rst b/doc/guides/dts/index.rst index a875f69ba7386..db7476d968f2d 100644 --- a/doc/guides/dts/index.rst +++ b/doc/guides/dts/index.rst @@ -1,7 +1,7 @@ -.. _devicetree: +.. _dt-guide: -Devicetree -########## +Devicetree Guide +################ A *devicetree* is a hierarchical data structure which describes hardware\ [#dt_spelling]_. The `Devicetree specification`_ fully defines its source and @@ -9,6 +9,9 @@ binary representations. Zephyr uses devicetree to describe the hardware available on its :ref:`boards`, as well as that hardware's initial configuration. +This page is the index for a guide to devicetree and how to use it in Zephyr. +For an API reference, see :ref:`devicetree_api`. + .. _Devicetree specification: https://www.devicetree.org/ .. toctree:: @@ -17,7 +20,8 @@ configuration. intro.rst design.rst bindings.rst - macros.rst + api-usage.rst + legacy-macros.rst howtos.rst dt-vs-kconfig.rst diff --git a/doc/guides/dts/intro.rst b/doc/guides/dts/intro.rst index 205d096ad9f77..1f6aa2d35751c 100644 --- a/doc/guides/dts/intro.rst +++ b/doc/guides/dts/intro.rst @@ -3,8 +3,8 @@ Introduction to devicetree ########################## -This page provides an introduction to :ref:`devicetree` and how it is used in -Zephyr. +This page provides an introduction to :ref:`devicetree ` and +how it is used in Zephyr. The following figure shows how devicetree is used by :ref:`Zephyr's build system `: @@ -14,17 +14,17 @@ system `: Devicetree build flow -The build system generates a C header file which contains preprocessor -:ref:`macros with devicetree data `. These macros can be referenced -by :ref:`device drivers ` and other C code by including -:file:``. All macro identifiers that are directly generated by -the devicetree scripts start with ``DT_``. +The build system generates a C header file which contains preprocessor macros +with devicetree data. These macros can be referenced by :ref:`device drivers +`, applications, tests, etc., by including the +```` header file and using its API. The macro-based +:file:`devicetree.h` API has names that start with ``DT_``. -Sometimes, information from devicetree is available using ``CONFIG_`` macros -generated from :ref:`Kconfig `. This happens when devicetree-related -information is referenced from Kconfig symbol definitions via :ref:`Kconfig -functions `. See :ref:`dt_vs_kconfig` for some additional -comparisons with Kconfig. +Information from devicetree is also sometimes available using ``CONFIG_`` +macros generated from :ref:`Kconfig `. This only happens when +devicetree-related information is referenced from Kconfig symbol definitions +via :ref:`Kconfig functions `. See :ref:`dt_vs_kconfig` for +some additional comparisons with Kconfig. This differs significantly from how devicetree is used on Linux. The Linux kernel would instead read the entire devicetree data structure in its @@ -80,12 +80,14 @@ 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. +Devicetree nodes can also have *properties*. Properties are name/value pairs. +Property values can be any sequence of bytes. In some cases, the values are an +array of what are called *cells*. A cell is just a 32-bit unsigned integer. + +Node ``a-sub-node`` has a property named ``foo``, whose value is a cell with +value 3. The size and type of ``foo``\ 's value are implied by the enclosing +angle brackets (``<`` and ``>``) in the DTS. See +:ref:`dt-writing-property-values` below for more example property values. In practice, devicetree nodes correspond to some hardware, and the node hierarchy reflects the hardware's physical layout. For example, let's consider @@ -259,17 +261,34 @@ label 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. - - Unlike a node's unit address, which is a simple number, the reg property is - an array of 32-bit unsigned integers. This is often used to describe the - size of a register map. In the case of the ``i2c@40003000`` node above, - ``reg = <0x40003000 0x1000>;`` means the register map occupies 0x1000 bytes - in the memory map. + Information used to address the device. The value is specific to the device + (i.e. is different depending on the compatible property). + + The ``reg`` property is a sequence of ``(address, length)`` pairs. Each + pair is called a "register block". Here are some common patterns: + + - Devices accessed via memory-mapped I/O registers (like ``i2c@40003000``): + ``address`` is usually the base address of the I/O register space, and + ``length`` is the number of bytes occupied by the registers. + - I2C devices (like ``apds9960@39`` and its siblings): + ``address`` is a slave address on the I2C bus. There is no ``length`` + value. + - SPI devices: ``address`` is a chip select line number; there is no + ``length``. + + You may notice some similarities between the ``reg`` property and common + unit addresses described above. This is not a coincidence. The ``reg`` + property can be seen as a more detailed view of the addressable resources + within a device than its unit address. + +interrupts + Information about interrupts generated by the device, encoded as an array + of one or more *interrupt specifiers*. Each interrupt specifier has some + number of cells. See section 2.4 Interrupts and Interrupt Mapping in the + devicetree specification release v0.3 for more details. + + Zephyr's devicetree bindings language lets you give a name to each cell in + an interrupt specifier. .. _devicetree-in-out-files: @@ -284,7 +303,12 @@ This section describes the input and output files shown in the figure at the Devicetree input (green) and output (yellow) files -There are four "types" of devicetree files: +.. _dt-input-files: + +Input files +=========== + +There are four types of devicetree input files: - sources (``.dts``) - includes (``.dtsi``) @@ -331,7 +355,7 @@ their purpose clear. Overlays adapt the base devicetree for different purposes: 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. +:ref:`set-devicetree-overlays` for details. The build system combines :file:`BOARD.dts` and any :file:`.overlay` files by concatenating them, with the overlays put last. This relies on DTS syntax which @@ -346,21 +370,20 @@ the contents of devicetree sources, includes, and overlays in a way that allows the build system to generate C macros usable by device drivers and applications. The :file:`dts/bindings` directory contains bindings. -These files in the build directory can be useful as a debugging aid when -working with devicetree: - -build/zephyr/.dts.pre.tmp - The preprocessed and concatenated DTS sources - -build/zephyr/zephyr.dts - The final merged devicetree. This file is specifically output as a - debugging aid, and is unused otherwise. +Zephyr currently uses :file:`dts_fixup.h` files to rename macros in +:file:`devicetree_unfixed.h` to names that are currently in use by C code. The +build system looks for fixup files in the :file:`zephyr/boards/` and +:file:`zephyr/soc/` directories by default. Fixup files exist for historical +reasons. New code should generally avoid them. .. _dt-scripts: +Scripts and tools +================= + The following libraries and scripts, located in :zephyr_file:`scripts/dts/`, -are used to generate C headers from the devicetree and its bindings. Note that -the source code has extensive comments and documentation. +create output files from input files. Their sources have extensive +documentation. :zephyr_file:`dtlib.py ` A low-level DTS parsing library. @@ -374,36 +397,109 @@ the source code has extensive comments and documentation. 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 the build directory as -:file:`build/zephyr/include/generated/devicetree_unfixed.h`. - -In addition to the Python code above, the standard ``dtc`` (devicetree -compiler) tool is also run on the final devicetree if it is installed on your -system. This is just to catch any errors or warnings it generates. The output -is unused. Boards may need to pass ``dtc`` additional flags, e.g. for warning -suppression. Board directories can contain a file named -:file:`pre_dt_board.cmake` which configures these extra flags, like this: +In addition to these, the standard ``dtc`` (devicetree compiler) tool is run on +the final devicetree if it is installed on your system. This is just to catch +errors or warnings. The output is unused. Boards may need to pass ``dtc`` +additional flags, e.g. for warning suppression. Board directories can contain a +file named :file:`pre_dt_board.cmake` which configures these extra flags, like +this: .. code-block:: cmake list(APPEND EXTRA_DTC_FLAGS "-Wno-simple_bus_reg") -Zephyr currently uses :file:`dts_fixup.h` files to rename macros in -:file:`devicetree_unfixed.h` to names that are currently in use by C code. The -build system looks for fixup files in the :file:`zephyr/boards/` and -:file:`zephyr/soc/` directories by default. Any :file:`dts_fixup.h` files are -concatenated and stored in the build directory as -:file:`build/zephyr/include/generated/devicetree_fixups.h`. +.. _dt-outputs: -Fixup files exist for historical reasons. New code should generally avoid them. +Output files +============ -To reference macros generated by :file:`gen_defines.py` from C, include -:file:`devicetree.h`. This file is :zephyr_file:`include/devicetree.h` in the -zephyr repository; it is not a generated file. It includes the generated -:file:`include/devicetree_unfixed.h` and :file:`include/devicetree_fixups.h` -files. +These are created in your application's build directory. .. warning:: - Do not include the generated C headers from the build directory directly. - Use :file:`devicetree.h` instead. + Don't include the header files directly. :ref:`dt-from-c` explains + what to do instead. + +:file:`/zephyr/include/generated/devicetree_unfixed.h` + The generated macros and additional comments describing the devicetree. + Included by ````. + +:file:`/zephyr/include/generated/devicetree_legacy_unfixed.h` + The generated :ref:`dt-legacy-macros`. + Included by ````. + +:file:`/zephyr/include/generated/devicetree_fixups.h` + The concatenated contents of any :file:`dts_fixup.h` files. + Included by ````. + +:file:`/zephyr/zephyr.dts` + The final merged devicetree. This file is output by :file:`gen_defines.py` + as a debugging aid, and is unused otherwise. + +:file:`/zephyr/.dts.pre.tmp` + The preprocessed and concatenated DTS sources and overlays. This is an + intermediate output file, which is used to create :file:`zephyr.dts` + and :file:`devicetree_unfixed.h`. + +.. highlight:: none + +.. _dt-writing-property-values: + +Writing property values +*********************** + +Here are some example ways to write property values in DTS format. Some +specifics are skipped in the interest of keeping things simple; if you're +curious about details, see the devicetree specification. + +Arrays of 32-bit unsigned integers, or *cells*, can be written between angle +brackets (``<`` and ``>``) and separated by spaces:: + + foo = <0xdeadbeef 1234 0>; + +The ``foo`` property value is three cells with values 0xdeadbeef, 1234, and 0, +in that order. Note that hexadecimal and decimal numbers are allowed and can be +intermixed. Since Zephyr transforms DTS to C sources, it is not necessary to +specify the endianness of an individual cell here. + +64-bit integers are written as two 32-bit cells in big-endian order. The value +0xaaaa0000bbbb1111 would be written ``<0xaaaa0000 0xbbbb1111>``. + +Parentheses, arithmetic operators, and bitwise operators are allowed. The +``bar`` property contains a single cell with value 64:: + + bar = <2 * (1 << 5)>; + +Strings are double quoted:: + + a-string = "hello, world!"; + +String arrays are separated by commas:: + + a-string-array = "string one", "string two", "string three"; + +Arrays of bytes are written in hexadecimal *without* leading ``0x`` between +square brackets (``[`` and ``]``). Property ``a-byte-array`` is the three bytes +0x00, 0x01, 0xab, in that order:: + + a-byte-array = [00 01 ab]; + +Properties can refer to other nodes in the devicetree by their *phandles*. You +can write a phandle using ``&label``, like in this devicetree fragment: + +.. code-block:: DTS + + baz: device@0 { + /* ... */ + }; + device@1 { + sibling = <&baz 1 2>; + /* ... */ + }; + +The ``sibling`` property of node ``device@1`` contains three cells: + +- The ``device@0`` node's phandle. Each phandle occupies an entire cell. The + ``baz`` label is used to write the phandle ``&baz`` inside the ``sibling`` + property value. +- The values 1 and 2, each in its own cell, in that order. diff --git a/doc/guides/dts/legacy-macros.bnf b/doc/guides/dts/legacy-macros.bnf new file mode 100644 index 0000000000000..05501d2b4bb34 --- /dev/null +++ b/doc/guides/dts/legacy-macros.bnf @@ -0,0 +1,106 @@ +; dt-macro is the top level nonterminal. It defines the possible +; macros generated by gen_defines.py. +; +; A dt-macro starts with uppercase "DT_" followed by either: +; +; - a property-macro, generated for a particular node +; property +; - some other-macro, a catch-all for other types of macros, +; which contain either global information about the tree or +; are special cases +; +; This does *not* cover macros pulled out of DT via Kconfig, +; like CONFIG_SRAM_BASE_ADDRESS, etc. +dt-macro = %s"DT_" ( property-macro / other-macro ) + +; -------------------------------------------------------------------- +; A property-macro is a sequence of: +; +; - node-id: a way to identify a node +; - property-id: a way to identify one of the node's properties +; - property-suf: an optional property-specific suffix +property-macro = node-id "_" property-id ["_" property-suf] + +; A node-id is a way to refer to a node within the devicetree. +; There are a few different flavors. + +node-id = compat-unit-id / inst-id / alias-id + +compat-unit-id = [bus-id-part "_"] compat-id-part "_" unit-addr-id-part +inst-id = %s"INST_" 1*DIGIT "_" compat-id-part +alias-id = %s"ALIAS_" alias-id-part + +; Various components of a property-macro are just c-idents, +; which are made of uppercase letters, numbers, and underscores. +; +; This is a problem, because it makes it possible for different nodes +; or properties in a devicetree to generate the same macro twice +; with different values. + +bus-id-part = c-ident ; ID for information about a node's bus +compat-id-part = c-ident ; ID for a node's compatible +unit-addr-id-part = c-ident ; ID for a node's unit-address +alias-id-part = c-ident ; ID for an /aliases node property +property-id = c-ident ; ID for a node property -- this also + ; covers special cases like "reg", + ; "interrupts", and "cs-gpios" for now, + ; as they all collide with non-special + ; cases. +property-suf = c-ident ; a suffix for part of a property value, + ; like an array index or a phandle + ; specifier name converted to a c-ident + +; -------------------------------------------------------------------- +; An other-macro is a grab bag for everything that isn't a +; property-macro. It reuses some of the nonterminals (namely node-id +; and compat-id-part) defined above. +other-macro = existence-flag / bus-macro / flash-macro / chosen-macro + +existence-flag = compat-existence-flag / inst-existence-flag +compat-flag = %s"COMPAT_" c-ident +inst-flag = %s"INST_" 1*DIGIT "_" c-ident + +bus-macro = bus-name-macro / on-bus-macro +bus-name-macro = node-id %s"_BUS_NAME" +on-bus-macro = compat-id-part %s"_BUS_" bus-name +bus-name = c-ident ; a bus name ("i2c") to a DT C + ; identifier ("I2C") + +flash-macro = %s"FLASH_AREA_" node-label-ident "_" flash-suf +flash-suf = %s"ID" / %s"READ_ONLY" / (%s"OFFSET" ["_" 1*DIGIT]) / + (%s"SIZE" ["_" 1*DIGIT]) / %s"DEV" + +; Macros generated from /chosen node properties. +chosen-macro = chosen-flash / + %s"CODE_PARTITION_OFFSET" / %s"CODE_PARTITION_SIZE" / + %s"CCM_BASE_ADDRESS" / %s"CCM_SIZE" / + %s"DTCM_BASE_ADDRESS" / %s"DTCM_SIZE" / + %s"IPC_SHM_BASE_ADDRESS" / %s"IPC_SHM_SIZE" +; These come from the /chosen/zephyr,flash property. +chosen-flash = %s"FLASH_BASE_ADDRESS" / + %s"FLASH_SIZE" / + %s"FLASH_ERASE_BLOCK_SIZE" / + %s"FLASH_WRITE_BLOCK_SIZE" + +; -------------------------------------------------------------------- +; Helper definitions. + +; A c-ident is one or more: +; - uppercase letters (A-Z) +; - numbers (0-9) +; - underscores ("_") +; +; They are the result of converting names or combinations of names +; from devicetree to a valid component of a C identifier by +; uppercasing letters and converting non-alphanumeric characters to +; underscores. +c-ident = 1*( UPPER / DIGIT / "_" ) + +; a node's "label" property value, as an identifier +node-label-ident = c-ident + +; "uppercase ASCII letter" turns out to be pretty annoying to specify +; in RFC-7405 syntax. +; +; This is just ASCII letters A (0x41) through Z (0x5A). +UPPER = %x41-5A diff --git a/doc/guides/dts/macros.rst b/doc/guides/dts/legacy-macros.rst similarity index 68% rename from doc/guides/dts/macros.rst rename to doc/guides/dts/legacy-macros.rst index a69a19bd338e8..a35b1a0224f12 100644 --- a/doc/guides/dts/macros.rst +++ b/doc/guides/dts/legacy-macros.rst @@ -1,25 +1,35 @@ -.. _dt-macros: +.. _dt-legacy-macros: -Macros generated from devicetree -################################ +Legacy devicetree macros +######################## -This page describes the C preprocessor macros which Zephyr's :ref:`build system -` generates from a :ref:`devicetree`. It assumes you're +.. warning:: + + You can still use the macros documented in this file, but support + for them will be removed eventually. + + See :ref:`dt-from-c` for a usage guide for the new API. + +This page describes legacy C preprocessor macros which Zephyr's :ref:`build +system ` generates from a devicetree. It assumes you're familiar with the concepts in :ref:`devicetree-intro` and :ref:`dt-bindings`. -The macros directly generated by the :ref:`devicetree scripts ` all -start with ``DT_`` and use all-uppercase. +These macros have somewhat inconsistent names, and their use in new code is +discouraged. See :ref:`dt-from-c` for the recommended API. -.. _dt-node-identifiers: +These macros are generated by the :ref:`devicetree scripts `, +start with ``DT_``, and use all-uppercase. -Node identifiers -**************** +.. _dt-legacy-node-identifiers: + +Legacy node identifiers +*********************** Macros generated from individual devicetree nodes or their properties start with ``DT_``, where ```` is a C identifier for the devicetree node. -This section describes the different ```` values. +This section describes the different ```` values in the legacy macros. -.. _dt-node-main-ex: +.. _dt-legacy-node-main-ex: We'll use the following DTS fragment from the :ref:`FRDM-K64F ` board's devicetree as the main example throughout this section. @@ -57,10 +67,11 @@ The binding for the "nxp,fxos8700" :ref:`compatible property The generated macros for this example can be found in a build directory for the :ref:`FXOS8700 sample application ` built for the ``frdm_k64f`` -board, in the file :file:`build/zephyr/include/generated/devicetree_unfixed.h`. +board, in the file +:file:`build/zephyr/include/generated/devicetree_legacy_unfixed.h`. -Here is part of :file:`devicetree_unfixed.h` showing some of the macros for the -node labeled ``i2c0``. Notice the comment with the node's path in the +Here is part of :file:`devicetree_legacy_unfixed.h` showing some of the macros +for the node labeled ``i2c0``. Notice the comment with the node's path in the devicetree and its dependency relationships with other nodes. .. code-block:: c @@ -185,7 +196,7 @@ node. starting from zero. The ``i2c@40066000`` node identifier in the :ref:`main example - ` is ``INST_0_NXP_KINETIS_I2C``: + ` is ``INST_0_NXP_KINETIS_I2C``: - ```` is 0 because it was the first node with compatible "nxp,kinetis-i2c" that the devicetree scripts happened to discover as they @@ -216,7 +227,7 @@ node. Generated from the names of any properties in the ``/aliases`` node. See :ref:`dt-alias-chosen` for an overview. - Here is simple example. + Here is a simple example. .. code-block:: DTS @@ -237,7 +248,7 @@ node. due to multiple names which have only minor differences. For a real-world example, the ``i2c@40066000`` node's alias identifier in - the :ref:`main example ` is ``ALIAS_I2C_0``: + the :ref:`main example ` is ``ALIAS_I2C_0``: ```` is ``I2C_0`` because the property ``i2c-0 = &i2c0;`` in the ``/aliases`` node "points at" ``i2c@40066000`` using its label ``i2c0``. The alias name ``i2c-0`` is converted to C identifier ``I2C_0``. @@ -257,40 +268,38 @@ node. .. note:: - Currently, an older deprecated ``DT__`` form is also + Another ``DT__`` form is also generated for aliases. For the example above, assuming the compatible string for the ``&uart1`` node is ``"foo,uart"``, this gives ``DT_FOO_UART_UART_1``. - Work is underway to replace this form with ``DT_ALIAS_*``. - -.. _property-macros: +.. _legacy-property-macros: Macros generated for properties ******************************* Macros for node property values have the form ``DT__``, where -```` is a :ref:`node identifier ` and ```` +```` is a :ref:`node identifier ` and ```` identifies the property. The macros generated for a property usually depend on its ``type:`` key in the matching devicetree binding. The following general purpose rules apply in most cases: -- :ref:`generic-macros` -- :ref:`phandle-array-macros` -- :ref:`enum-macros` +- :ref:`generic-legacy-macros` +- :ref:`phandle-array-legacy-macros` +- :ref:`enum-legacy-macros` However, some "special" properties get individual treatment: -- :ref:`reg_macros` -- :ref:`irq_macros` -- :ref:`clk_macros` -- :ref:`spi_cs_macros` +- :ref:`reg_legacy_macros` +- :ref:`irq_legacy_macros` +- :ref:`clk_legacy_macros` +- :ref:`spi_cs_legacy_macros` No macros are currently generated for properties with type ``phandle``, ``phandles``, ``path``, or ``compound``. -.. _generic-macros: +.. _generic-legacy-macros: Generic property macros ======================= @@ -340,7 +349,7 @@ For non-boolean types the property macros are not generated if the binding's ``category`` is ``optional`` and the property is not present in the devicetree source. -.. _phandle-array-macros: +.. _phandle-array-legacy-macros: Properties with type ``phandle-array`` ====================================== @@ -436,7 +445,7 @@ with type ``phandle-array``), it gives a list of strings that name each entry in ``pwms``. The names are used to generate extra macro names with the name instead of an index, like ``DT__FIRST_PWMS_CONTROLLER`` above. -.. _enum-macros: +.. _enum-legacy-macros: Properties with ``enum:`` in the binding ======================================== @@ -463,7 +472,7 @@ The property ``foo = "three"`` then generates this macro: #define DT__FOO_ENUM 2 -.. _reg_macros: +.. _reg_legacy_macros: ``reg`` property macros ======================= @@ -499,7 +508,7 @@ generates these macros: #define DT__FOO_BASE_ADDRESS 0x40047000 #define DT__FOO_SIZE 4192 -.. _irq_macros: +.. _irq_legacy_macros: ``interrupts`` property macros ============================== @@ -573,20 +582,16 @@ generates these macros: #define DT__IRQ_TIMER_B 2 #define DT__IRQ_TIMER_B_PRIORITY 6 -.. _clk_macros: +.. _clk_legacy_macros: ``clocks`` property macros ========================== -``clocks`` work the same as other :ref:`phandle-array-macros`, except the +``clocks`` work the same as other :ref:`phandle-array-legacy-macros`, except the generated macros have ``CLOCK`` in them instead of ``CLOCKS``, giving for example ``DT__CLOCK_CONTROLLER_0`` instead of ``DT__CLOCKS_CONTROLLER_0``. -.. note:: - - This inconsistency might be fixed in the future. - If a ``clocks`` controller node has a ``"fixed-clock"`` compatible, it must also have a ``clock-frequency`` property giving its frequency in Hertz. In this case, an additional macro is generated: @@ -595,7 +600,7 @@ In this case, an additional macro is generated: #define DT__CLOCKS_CLOCK_FREQUENCY -.. _spi_cs_macros: +.. _spi_cs_legacy_macros: ``cs-gpios`` property macros ============================ @@ -661,7 +666,7 @@ devicetree had looked like this: }; }; -See the ``phandle-array`` section in :ref:`generic-macros` for more +See the ``phandle-array`` section in :ref:`generic-legacy-macros` for more information. For example, since the node labeled ``gpioa`` has property @@ -679,13 +684,13 @@ Other macros ************ These are generated in addition to macros generated for :ref:`properties -`. +`. -- :ref:`dt-existence-macros` -- :ref:`bus-macros` -- :ref:`flash-macros` +- :ref:`dt-existence-legacy-macros` +- :ref:`bus-legacy-macros` +- :ref:`flash-legacy-macros` -.. _dt-existence-macros: +.. _dt-existence-legacy-macros: Node existence flags ==================== @@ -707,7 +712,7 @@ compatible: #define DT_INST__ 1 -For the ``i2c@40066000`` node in the :ref:`example ` above, +For the ``i2c@40066000`` node in the :ref:`example ` above, assuming the node is the first node with ``compatible = "nxp,kinetis-i2c"``, the following existence flags would be generated: @@ -728,7 +733,7 @@ flags would be generated: #define DT_INST_2_NXP_KINETIS_I2C 1 /* ... and so on, one for each node with this compatible. */ -.. _bus-macros: +.. _bus-legacy-macros: Bus-related macros ================== @@ -745,7 +750,7 @@ their binding): must exist. ```` is the identifier for the bus as given in ``on-bus:`` in the binding. -.. _flash-macros: +.. _flash-legacy-macros: Macros generated from flash partitions ====================================== @@ -819,14 +824,327 @@ for each partition, derived from ``reg``. The ``*_OFFSET`` and ``*_SIZE`` macros, with no index, are aliases that point to the first sector (with index 0). -An ABNF grammar -*************** +.. _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 +`. + +.. 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` + +.. _legacy_flash_partitions: + +Legacy 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. See +:ref:`set-devicetree-overlays` for examples. + +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: + +.. code-block:: DTS + + &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:: DTS + + 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:: DTS + + &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:: DTS + + / { + 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 ````. + +Consider this flash partition: + +.. code-block:: DTS + + 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 DT_FLASH_AREA_DEF_NAME_DEV "def-name" + #define DT_FLASH_AREA_DEF_NAME_OFFSET_0 0xSTART_OFFSET + #define DT_FLASH_AREA_DEF_NAME_SIZE_0 0xSIZE + #define DT_FLASH_AREA_DEF_NAME_OFFSET DT_FLASH_AREA_DEF_NAME_OFFSET_0 + #define DT_FLASH_AREA_DEF_NAME_SIZE DT_FLASH_AREA_DEF_NAME_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. + +ABNF grammar +************ This section contains an Augmented Backus-Naur Form grammar for the macros generated from a devicetree. See `RFC 7405`_ (which extends `RFC 5234`_) for a syntax specification. -.. literalinclude:: macros.bnf +.. literalinclude:: legacy-macros.bnf :language: abnf .. _RFC 7405: https://tools.ietf.org/html/rfc7405 diff --git a/doc/guides/dts/macros.bnf b/doc/guides/dts/macros.bnf index 05501d2b4bb34..52da1e9ccec80 100644 --- a/doc/guides/dts/macros.bnf +++ b/doc/guides/dts/macros.bnf @@ -1,106 +1,198 @@ -; dt-macro is the top level nonterminal. It defines the possible -; macros generated by gen_defines.py. -; -; A dt-macro starts with uppercase "DT_" followed by either: -; -; - a property-macro, generated for a particular node -; property -; - some other-macro, a catch-all for other types of macros, -; which contain either global information about the tree or -; are special cases +; An RFC 7405 ABNF grammar for devicetree macros. ; ; This does *not* cover macros pulled out of DT via Kconfig, -; like CONFIG_SRAM_BASE_ADDRESS, etc. -dt-macro = %s"DT_" ( property-macro / other-macro ) +; like CONFIG_SRAM_BASE_ADDRESS, etc. It only describes the +; ones that start with DT_ and are directly generated, not +; defined in a dts_fixup.h file. ; -------------------------------------------------------------------- -; A property-macro is a sequence of: +; dt-macro: the top level nonterminal for a devicetree macro ; -; - node-id: a way to identify a node -; - property-id: a way to identify one of the node's properties -; - property-suf: an optional property-specific suffix -property-macro = node-id "_" property-id ["_" property-suf] - -; A node-id is a way to refer to a node within the devicetree. -; There are a few different flavors. +; A dt-macro starts with uppercase "DT_", and is one of: +; +; - a , generated for a particular node +; - some , a catch-all for other types of macros +dt-macro = node-macro / other-macro -node-id = compat-unit-id / inst-id / alias-id +; -------------------------------------------------------------------- +; node-macro: a macro related to a node -compat-unit-id = [bus-id-part "_"] compat-id-part "_" unit-addr-id-part -inst-id = %s"INST_" 1*DIGIT "_" compat-id-part -alias-id = %s"ALIAS_" alias-id-part +; A macro about a property value +node-macro = property-macro +; EXISTS macro: node has matching binding and "okay" status. +node-macro =/ %s"DT_N" path-id %s"_EXISTS" +; Bus macros: the plain BUS is a way to access a node's bus controller. +; The additional dt-name suffix is added to match that node's bus type; +; the dt-name in this case is something like "spi" or "i2c". +node-macro =/ %s"DT_N" path-id %s"_BUS" ["_" dt-name] +; The reg property is special and has its own macros. +node-macro =/ %s"DT_N" path-id %s"_REG_NUM" +node-macro =/ %s"DT_N" path-id %s"_REG_IDX_" DIGIT + %s"_VAL_" ( %s"ADDRESS" / %s"SIZE") +node-macro =/ %s"DT_N" path-id %s"_REG_NAME_" dt-name + %s"_VAL_" ( %s"ADDRESS" / %s"SIZE") +; The interrupts property is also special. +node-macro =/ %s"DT_N" path-id %s"_IRQ_NUM" +node-macro =/ %s"DT_N" path-id %s"_IRQ_IDX_" DIGIT + %s"_VAL_" dt-name [ %s"_EXISTS" ] +node-macro =/ %s"DT_N" path-id %s"_IRQ_NAME_" dt-name + %s"_VAL_" dt-name [ %s"_EXISTS" ] +; Macros are generated for each of the node's compatibles. +node-macro =/ %s"DT_N" path-id %s"_COMPAT_MATCHES_" dt-name -; Various components of a property-macro are just c-idents, -; which are made of uppercase letters, numbers, and underscores. +; -------------------------------------------------------------------- +; property-macro: a macro related to a node property ; -; This is a problem, because it makes it possible for different nodes -; or properties in a devicetree to generate the same macro twice -; with different values. - -bus-id-part = c-ident ; ID for information about a node's bus -compat-id-part = c-ident ; ID for a node's compatible -unit-addr-id-part = c-ident ; ID for a node's unit-address -alias-id-part = c-ident ; ID for an /aliases node property -property-id = c-ident ; ID for a node property -- this also - ; covers special cases like "reg", - ; "interrupts", and "cs-gpios" for now, - ; as they all collide with non-special - ; cases. -property-suf = c-ident ; a suffix for part of a property value, - ; like an array index or a phandle - ; specifier name converted to a c-ident +; The "plain vanilla" macro for a property's value thus looks like: +; +; DT_N__P_ +; +; Components: +; +; - path-id: node's devicetree path converted to a C token +; - prop-id: node's property name converted to a C token +; - prop-suf: an optional property-specific suffix +property-macro = %s"DT_N" path-id %s"_P_" prop-id [prop-suf] ; -------------------------------------------------------------------- -; An other-macro is a grab bag for everything that isn't a -; property-macro. It reuses some of the nonterminals (namely node-id -; and compat-id-part) defined above. -other-macro = existence-flag / bus-macro / flash-macro / chosen-macro +; path-id: a node's path-based macro identifier +; +; The path of the node converted to a C token by changing: +; +; - each slash (/) to _S_ +; - all letters to lowercase +; - non-alphanumerics characters to underscores +; +; For example, the leaf node "bar-BAZ" in this devicetree: +; +; / { +; foo@123 { +; bar-BAZ {}; +; }; +; }; +; +; has path-id "_S_foo_123_S_bar_baz". +path-id = 1*( %s"_S_" dt-name ) -existence-flag = compat-existence-flag / inst-existence-flag -compat-flag = %s"COMPAT_" c-ident -inst-flag = %s"INST_" 1*DIGIT "_" c-ident +; ---------------------------------------------------------------------- +; prop-id: a property identifier +; +; A property name converted to a C token by changing: +; +; - all letters to lowercase +; - non-alphanumeric characters to underscores +; +; Example node: +; +; chosen { +; zephyr,console = &uart1; +; WHY,AM_I_SHOUTING = "unclear"; +; }; +; +; The 'zephyr,console' property has prop-id 'zephyr_console'. +; 'WHY,AM_I_SHOUTING' has prop-id 'why_am_i_shouting'. +prop-id = dt-name -bus-macro = bus-name-macro / on-bus-macro -bus-name-macro = node-id %s"_BUS_NAME" -on-bus-macro = compat-id-part %s"_BUS_" bus-name -bus-name = c-ident ; a bus name ("i2c") to a DT C - ; identifier ("I2C") +; ---------------------------------------------------------------------- +; prop-suf: a property-specific macro suffix +; +; Extra macros are generated for properties: +; +; - that are special to the specification ("reg", "interrupts", etc.) +; - with array types (uint8-array, phandle-array, etc.) +; - with "enum:" in their bindings +; - zephyr device API specific macros for phandle-arrays +; +; Here are some examples: +; +; - _EXISTS: property existence flag +; - _SIZE: logical property length +; - _IDX_: values of individual array elements +; - _IDX__VAL_: values of individual specifier +; cells within a phandle array +; - _ADDR_: for reg properties, the i-th register block address +; - _LEN_: for reg properties, the i-th register block length +; +; The different cases are not exhaustively documented here to avoid +; this file going stale. Please see devicetree.h if you need to know +; the details. +prop-suf = 1*( "_" gen-name ["_" dt-name] ) + +; -------------------------------------------------------------------- +; other-macro: grab bag for everything that isn't a node-macro. -flash-macro = %s"FLASH_AREA_" node-label-ident "_" flash-suf -flash-suf = %s"ID" / %s"READ_ONLY" / (%s"OFFSET" ["_" 1*DIGIT]) / - (%s"SIZE" ["_" 1*DIGIT]) / %s"DEV" +; See examples below. +other-macro = %s"DT_N_" alternate-id +; Total count of enabled instances of a compatible. +other-macro =/ %s"DT_N_INST_" dt-name %s"_NUM" +; E.g.: #define DT_CHOSEN_zephyr_flash +other-macro =/ %s"DT_CHOSEN_" dt-name -; Macros generated from /chosen node properties. -chosen-macro = chosen-flash / - %s"CODE_PARTITION_OFFSET" / %s"CODE_PARTITION_SIZE" / - %s"CCM_BASE_ADDRESS" / %s"CCM_SIZE" / - %s"DTCM_BASE_ADDRESS" / %s"DTCM_SIZE" / - %s"IPC_SHM_BASE_ADDRESS" / %s"IPC_SHM_SIZE" -; These come from the /chosen/zephyr,flash property. -chosen-flash = %s"FLASH_BASE_ADDRESS" / - %s"FLASH_SIZE" / - %s"FLASH_ERASE_BLOCK_SIZE" / - %s"FLASH_WRITE_BLOCK_SIZE" +; -------------------------------------------------------------------- +; alternate-id: another way to specify a node besides a path-id +; +; Example devicetree: +; +; / { +; aliases { +; dev = &dev_1; +; }; +; +; soc { +; dev_1: device@123 { +; compatible = "vnd,device"; +; }; +; }; +; }; +; +; Node device@123 has these alternate-id values: +; +; - ALIAS_dev +; - NODELABEL_dev_1 +; - INST_0_vnd_device +; +; The full alternate-id macros are: +; +; #define DT_N_INST_0_vnd_device DT_N_S_soc_S_device_123 +; #define DT_N_ALIAS_dev DT_N_S_soc_S_device_123 +; #define DT_N_NODELABEL_dev_1 DT_N_S_soc_S_device_123 +; +; These mainly exist to allow pasting an alternate-id macro onto a +; "_P_" to access node properties given a node's alias, etc. +; +; Notice that "inst"-type IDs have a leading instance identifier, +; which is generated by the devicetree scripts. The other types of +; alternate-id begin immediately with names taken from the devicetree. +alternate-id = ( %s"ALIAS" / %s"NODELABEL" ) dt-name +alternate-id =/ %s"INST_" 1*DIGIT "_" dt-name ; -------------------------------------------------------------------- -; Helper definitions. +; miscellaneous helper definitions -; A c-ident is one or more: -; - uppercase letters (A-Z) +; A dt-name is one or more: +; - lowercase ASCII letters (a-z) ; - numbers (0-9) ; - underscores ("_") ; ; They are the result of converting names or combinations of names ; from devicetree to a valid component of a C identifier by -; uppercasing letters and converting non-alphanumeric characters to -; underscores. -c-ident = 1*( UPPER / DIGIT / "_" ) +; lowercasing letters (in practice, this is a no-op) and converting +; non-alphanumeric characters to underscores. +dt-name = 1*( lower / DIGIT / "_" ) -; a node's "label" property value, as an identifier -node-label-ident = c-ident +; gen-name is used as a stand-in for a component of a generated macro +; name which does not come from devicetree (dt-name covers that case). +; +; - uppercase ASCII letters (a-z) +; - numbers (0-9) +; - underscores ("_") +gen-name = upper 1*( upper / DIGIT / "_" ) -; "uppercase ASCII letter" turns out to be pretty annoying to specify +; "lowercase ASCII letter" turns out to be pretty annoying to specify ; in RFC-7405 syntax. ; -; This is just ASCII letters A (0x41) through Z (0x5A). -UPPER = %x41-5A +; This is just ASCII letters a (0x61) through z (0x7a). +lower = %x61-7A + +; "uppercase ASCII letter" in RFC-7405 syntax +upper = %x41-5A diff --git a/doc/guides/dts/main-example.dts b/doc/guides/dts/main-example.dts new file mode 100644 index 0000000000000..f9ed9e09b8ee2 --- /dev/null +++ b/doc/guides/dts/main-example.dts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + * + * This is used multiple times in the documentation. + * If you change it for one example, you could break others, so be careful. + */ + +/* start-after-here */ + +/dts-v1/; + +/ { + + aliases { + sensor-controller = &i2c1; + }; + + soc { + i2c1: i2c@40002000 { + compatible = "vnd,soc-i2c"; + label = "I2C_1"; + reg = <0x40002000 0x1000>; + status = "okay"; + clock-frequency = < 100000 >; + }; + }; +}; diff --git a/doc/guides/kconfig/setting.rst b/doc/guides/kconfig/setting.rst index a77e71aae40e3..d99dffb5639d1 100644 --- a/doc/guides/kconfig/setting.rst +++ b/doc/guides/kconfig/setting.rst @@ -261,7 +261,7 @@ interfaces and make them harder to understand, and would make it easier to accidentally create broken configurations. When dealing with fixed board-specific settings, also consider whether they -should be handled via :ref:`devicetree` instead. +should be handled via :ref:`devicetree ` instead. Configuring choices diff --git a/doc/guides/porting/board_porting.rst b/doc/guides/porting/board_porting.rst index 3776059a03835..365eca4f83d6d 100644 --- a/doc/guides/porting/board_porting.rst +++ b/doc/guides/porting/board_porting.rst @@ -149,10 +149,10 @@ Replace ``plank`` with your board's name, of course. The mandatory files are: -#. :file:`plank.dts`: a hardware description in :ref:`devicetree` format. This - declares your SoC, connectors, and any other hardware components such as - LEDs, buttons, sensors, or communication peripherals (USB, BLE controller, - etc). +#. :file:`plank.dts`: a hardware description in :ref:`devicetree + ` format. This declares your SoC, connectors, and any + other hardware components such as LEDs, buttons, sensors, or communication + peripherals (USB, BLE controller, etc). #. :file:`Kconfig.board`, :file:`Kconfig.defconfig`, :file:`plank_defconfig`: software configuration in :ref:`kconfig` formats. This provides default @@ -246,6 +246,64 @@ followed by trial and error. If you want to understand details, you will need to read the rest of the devicetree documentation and the devicetree specification. +.. _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' style avoids extra spaces before commas. + +This section contains concrete examples related to writing your board's +devicetree. + +The FRDM-K64F and Hexiwear K64 board devicetrees are defined in +:zephyr_file:`frdm_k64fs.dts ` and +:zephyr_file:`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 +`, which is included by both board :file:`.dts` +files. :zephyr_file:`nxp_k6x.dtsi` in turn includes +:zephyr_file:`armv7-m.dtsi`, which has common definitions +for Arm v7-M cores. + +Since :zephyr_file:`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:: DTS + + 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:: DTS + + &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. + Write Kconfig files ******************* diff --git a/doc/guides/porting/shields.rst b/doc/guides/porting/shields.rst index 2d15de91393fc..834392c04769e 100644 --- a/doc/guides/porting/shields.rst +++ b/doc/guides/porting/shields.rst @@ -24,8 +24,8 @@ under :zephyr_file:`/boards/shields`: These files provides shield configuration as follows: * **.overlay**: This file provides a shield description in devicetree - format that is merged with the board's :ref:`devicetree` before - compilation. + format that is merged with the board's :ref:`devicetree ` + before compilation. * **Kconfig.shield**: This file defines shield Kconfig symbols that will be used for default shield configuration. To ease use with applications, @@ -49,10 +49,11 @@ This should be done at two different level: * Pinmux: Connector pins should be correctly configured to match shield pins -* Devicetree: A board :ref:`devicetree` file should define a node alias for - each connector interface. For example, for Arduino I2C: +* Devicetree: A board :ref:`devicetree ` file, + :file:`BOARD.dts` should define a node alias for each connector interface. + For example, for Arduino I2C: -.. code-block:: none +.. code-block:: DTS #define arduino_i2c i2c1 @@ -63,7 +64,7 @@ This should be done at two different level: Note: With support of dtc v1.4.2, above will be replaced with the recently introduced overriding node element: -.. code-block:: none +.. code-block:: DTS arduino_i2c:i2c1{}; diff --git a/doc/guides/west/build-flash-debug.rst b/doc/guides/west/build-flash-debug.rst index 2161d326499a3..04a59710a8231 100644 --- a/doc/guides/west/build-flash-debug.rst +++ b/doc/guides/west/build-flash-debug.rst @@ -202,8 +202,11 @@ Notice how the ``--`` only appears once, even though multiple CMake arguments are given. All command-line arguments to ``west build`` after a ``--`` are passed to CMake. -To set :ref:`DTC_OVERLAY_FILE ` to :file:`enable-modem.overlay`, -using that file as a :ref:`devicetree overlay `:: +.. _west-building-dtc-overlay-file: + +To set :ref:`DTC_OVERLAY_FILE ` to +:file:`enable-modem.overlay`, using that file as a +:ref:`devicetree overlay `:: west build -b reel_board -- -DDTC_OVERLAY_FILE=enable-modem.overlay diff --git a/doc/introduction/index.rst b/doc/introduction/index.rst index 70fd3980d2c94..04dcf2e341250 100644 --- a/doc/introduction/index.rst +++ b/doc/introduction/index.rst @@ -100,8 +100,8 @@ Zephyr offers a large and ever growing number of features including: platforms that have common devices/IP blocks **Devicetree Support** - Use of :ref:`devicetree` to describe hardware. Information from devicetree - is used to create the application image. + Use of :ref:`devicetree ` to describe hardware. + Information from devicetree is used to create the application image. **Native Networking Stack supporting multiple protocols** Networking support is fully featured and optimized, including LwM2M and BSD diff --git a/doc/reference/devicetree/index.rst b/doc/reference/devicetree/index.rst new file mode 100644 index 0000000000000..3a319a6fac21d --- /dev/null +++ b/doc/reference/devicetree/index.rst @@ -0,0 +1,98 @@ +.. _devicetree_api: + +Devicetree +########## + +This page contains reference documentation for ````. See +:ref:`dt-guide` for an introduction. Use of these macros has no impact on +scheduling. They can be used from any calling context and at file scope. + +Some of these require a special macro named ``DT_DRV_COMPAT`` to be defined +before they can be used; these are discussed individually below. These macros +are generally meant for use within device drivers. + +Generic APIs +************ + +These APIs can be used anywhere. + +Node identifiers +================ + +You can use node identifiers for devicetree nodes which are enabled (i.e. have +``status = "okay";`` properties) and have matching compatibles. This can be +tested with :c:func:`DT_HAS_NODE()`. + +.. doxygengroup:: devicetree-generic-id + :project: Zephyr + +Property access +=============== + +.. doxygengroup:: devicetree-generic-prop + :project: Zephyr + +Chosen nodes +============ + +.. doxygengroup:: devicetree-generic-chosen + :project: Zephyr + +Existence checks +================ + +.. doxygengroup:: devicetree-generic-exist + :project: Zephyr + +Bus helpers +=========== + +.. doxygengroup:: devicetree-generic-bus + :project: Zephyr + +.. _devicetree-inst-apis: + +Instance-based APIs +******************* + +These are recommended for use within device drivers. To use them, define +``DT_DRV_COMPAT`` to the lowercase-and-underscores compatible the device driver +implements support for. Note that there are also helpers available for +specific hardware; these are documented in the following sections. + +It is an error to use these macros without ``DT_DRV_COMPAT`` defined. + +.. doxygengroup:: devicetree-inst + :project: Zephyr + +.. _devicetree-hw-api: + +Hardware specific APIs +********************** + +The following APIs can also be used by including ````; +no additional include is needed. + +ADC +=== + +.. doxygengroup:: devicetree-adc + :project: Zephyr + +GPIO +==== + +.. doxygengroup:: devicetree-gpio + :project: Zephyr + +SPI +=== + +.. doxygengroup:: devicetree-spi + :project: Zephyr + +Clocks +====== + +.. doxygengroup:: devicetree-clocks + :project: Zephyr diff --git a/doc/reference/index.rst b/doc/reference/index.rst index 4a31ca357d64a..85cf257d1db8c 100644 --- a/doc/reference/index.rst +++ b/doc/reference/index.rst @@ -12,6 +12,7 @@ API Reference bluetooth/index.rst kconfig/index.rst crypto/index.rst + devicetree/index.rst drivers/index.rst display/index.rst file_system/index.rst diff --git a/doc/reference/overview.rst b/doc/reference/overview.rst index 1f83e843511b6..616fcd1884c03 100644 --- a/doc/reference/overview.rst +++ b/doc/reference/overview.rst @@ -44,6 +44,11 @@ current :ref:`stability level `. - 1.0 - 2.2 + * - :ref:`devicetree_api` + - Experimental + - 2.2 + - 2.2 + * - :ref:`display_api` - Unstable - 1.14 diff --git a/doc/reference/storage/flash_map/flash_map.rst b/doc/reference/storage/flash_map/flash_map.rst index 422ba28725ebb..eaf6e810a45bd 100644 --- a/doc/reference/storage/flash_map/flash_map.rst +++ b/doc/reference/storage/flash_map/flash_map.rst @@ -6,7 +6,7 @@ Flash map (flash_map) Flash map is a way for storing flash partitioning information in one central location in flash_area structures array form. -Flash map is generated from DTS based on content of :ref:`flash_partitions` +Flash map is generated from DTS based on content of :ref:`legacy_flash_partitions` nodes. The flash_area API provides a way to access data in the flash map. The flash_area_open() API is the interface for obtaining the flash partitions diff --git a/doc/releases/release-notes-2.3.rst b/doc/releases/release-notes-2.3.rst index 3cbac13d2b9d8..c9e6ef61dd7cd 100644 --- a/doc/releases/release-notes-2.3.rst +++ b/doc/releases/release-notes-2.3.rst @@ -227,8 +227,16 @@ Build and Infrastructure * +* Devicetree + + * A new :ref:`devicetree_api` was added. This API is not generated, but is + still included via ````. The :ref:`dt-legacy-macros` are now + deprecated; users should replace the generated macros with new API. The + :ref:`dt-howtos` page has been extended for the new API, and a new + :ref:`dt-from-c` API usage guide was also added. + Libraries / Subsystems -*********************** +********************** * Random diff --git a/drivers/sensor/fxos8700/fxos8700.c b/drivers/sensor/fxos8700/fxos8700.c index 28e3b0e2261c1..3e67b16ae1aab 100644 --- a/drivers/sensor/fxos8700/fxos8700.c +++ b/drivers/sensor/fxos8700/fxos8700.c @@ -5,6 +5,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#define DT_DRV_COMPAT nxp_fxos8700 + #include "fxos8700.h" #include #include @@ -521,12 +523,12 @@ static const struct sensor_driver_api fxos8700_driver_api = { }; static const struct fxos8700_config fxos8700_config = { - .i2c_name = DT_INST_0_NXP_FXOS8700_BUS_NAME, - .i2c_address = DT_INST_0_NXP_FXOS8700_BASE_ADDRESS, -#ifdef DT_INST_0_NXP_FXOS8700_RESET_GPIOS_CONTROLLER - .reset_name = DT_INST_0_NXP_FXOS8700_RESET_GPIOS_CONTROLLER, - .reset_pin = DT_INST_0_NXP_FXOS8700_RESET_GPIOS_PIN, - .reset_flags = DT_INST_0_NXP_FXOS8700_RESET_GPIOS_FLAGS, + .i2c_name = DT_INST_BUS_LABEL(0), + .i2c_address = DT_INST_REG_ADDR(0), +#if DT_INST_NODE_HAS_PROP(0, reset_gpios) + .reset_name = DT_INST_GPIO_LABEL(0, reset_gpios), + .reset_pin = DT_INST_GPIO_PIN(0, reset_gpios), + .reset_flags = DT_INST_GPIO_FLAGS(0, reset_gpios), #else .reset_name = NULL, .reset_pin = 0, @@ -566,13 +568,13 @@ static const struct fxos8700_config fxos8700_config = { #endif #ifdef CONFIG_FXOS8700_TRIGGER #ifdef CONFIG_FXOS8700_DRDY_INT1 - .gpio_name = DT_INST_0_NXP_FXOS8700_INT1_GPIOS_CONTROLLER, - .gpio_pin = DT_INST_0_NXP_FXOS8700_INT1_GPIOS_PIN, - .gpio_flags = DT_INST_0_NXP_FXOS8700_INT1_GPIOS_FLAGS, + .gpio_name = DT_INST_GPIO_LABEL(0, int1_gpios), + .gpio_pin = DT_INST_GPIO_PIN(0, int1_gpios), + .gpio_flags = DT_INST_GPIO_FLAGS(0, int1_gpios), #else - .gpio_name = DT_INST_0_NXP_FXOS8700_INT2_GPIOS_CONTROLLER, - .gpio_pin = DT_INST_0_NXP_FXOS8700_INT2_GPIOS_PIN, - .gpio_flags = DT_INST_0_NXP_FXOS8700_INT2_GPIOS_FLAGS, + .gpio_name = DT_INST_GPIO_LABEL(0, int2_gpios), + .gpio_pin = DT_INST_GPIO_PIN(0, int2_gpios), + .gpio_flags = DT_INST_GPIO_FLAGS(0, int2_gpios), #endif #endif #ifdef CONFIG_FXOS8700_PULSE @@ -588,7 +590,7 @@ static const struct fxos8700_config fxos8700_config = { static struct fxos8700_data fxos8700_data; -DEVICE_AND_API_INIT(fxos8700, DT_INST_0_NXP_FXOS8700_LABEL, fxos8700_init, +DEVICE_AND_API_INIT(fxos8700, DT_INST_LABEL(0), fxos8700_init, &fxos8700_data, &fxos8700_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &fxos8700_driver_api); diff --git a/drivers/serial/uart_nrfx_uart.c b/drivers/serial/uart_nrfx_uart.c index 043b184f31ab1..6868c348ff4e0 100644 --- a/drivers/serial/uart_nrfx_uart.c +++ b/drivers/serial/uart_nrfx_uart.c @@ -12,16 +12,37 @@ #include #include -#ifdef DT_NORDIC_NRF_UART_UART_0_RX_PIN -#define RX_PIN DT_NORDIC_NRF_UART_UART_0_RX_PIN +/* + * Extract information from devicetree. + * + * This driver only supports one instance of this IP block, so the + * instance number is always 0. + */ +#define DT_DRV_COMPAT nordic_nrf_uart + +#define PROP(prop) DT_INST_PROP(0, prop) +#define HAS_PROP(prop) DT_INST_NODE_HAS_PROP(0, prop) + +#define BAUDRATE PROP(current_speed) +#define TX_PIN PROP(tx_pin) + +#define RX_PIN_USED HAS_PROP(rx_pin) +#if RX_PIN_USED +#define RX_PIN PROP(rx_pin) #else -#define RX_PIN NRF_UART_PSEL_DISCONNECTED +#define RX_PIN NRF_UART_PSEL_DISCONNECTED +#endif + +#define HW_FLOW_CONTROL (HAS_PROP(rts_pin) && HAS_PROP(cts_pin)) +#if HW_FLOW_CONTROL +#define RTS_PIN PROP(rts_pin) +#define CTS_PIN PROP(cts_pin) #endif -#define RX_PIN_USED() (RX_PIN != NRF_UART_PSEL_DISCONNECTED) +#define IRQN DT_INST_IRQN(0) +#define IRQ_PRIO DT_INST_IRQ(0, priority) -static NRF_UART_Type *const uart0_addr = - (NRF_UART_Type *)DT_NORDIC_NRF_UART_UART_0_BASE_ADDRESS; +static NRF_UART_Type *const uart0_addr = (NRF_UART_Type *)DT_INST_REG_ADDR(0); /* Device data structure */ struct uart_nrfx_data { @@ -64,8 +85,7 @@ static struct { const u8_t *volatile tx_buffer; size_t tx_buffer_length; volatile size_t tx_counter; -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) +#if HW_FLOW_CONTROL s32_t tx_timeout; struct k_delayed_work tx_timeout_work; #endif @@ -403,8 +423,7 @@ static int uart_nrfx_tx(struct device *dev, const u8_t *buf, size_t len, } uart0_cb.tx_buffer = buf; -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) +#if HW_FLOW_CONTROL uart0_cb.tx_timeout = timeout; #endif nrf_uart_event_clear(uart0_addr, NRF_UART_EVENT_TXDRDY); @@ -423,8 +442,7 @@ static int uart_nrfx_tx_abort(struct device *dev) if (uart0_cb.tx_buffer_length == 0) { return -EINVAL; } -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) +#if HW_FLOW_CONTROL if (uart0_cb.tx_timeout != K_FOREVER) { k_delayed_work_cancel(&uart0_cb.tx_timeout_work); } @@ -448,7 +466,7 @@ static int uart_nrfx_tx_abort(struct device *dev) static int uart_nrfx_rx_enable(struct device *dev, u8_t *buf, size_t len, s32_t timeout) { - if (!RX_PIN_USED()) { + if (!RX_PIN_USED) { __ASSERT(false, "TX only UART instance"); return -ENOTSUP; } @@ -603,8 +621,7 @@ static void tx_isr(void) uart0_cb.tx_counter++; if (uart0_cb.tx_counter < uart0_cb.tx_buffer_length && !uart0_cb.tx_abort) { -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) +#if HW_FLOW_CONTROL if (uart0_cb.tx_timeout != K_FOREVER) { k_delayed_work_submit(&uart0_cb.tx_timeout_work, uart0_cb.tx_timeout); @@ -616,8 +633,7 @@ static void tx_isr(void) nrf_uart_txd_set(uart0_addr, txd); } else { -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) +#if HW_FLOW_CONTROL if (uart0_cb.tx_timeout != K_FOREVER) { k_delayed_work_cancel(&uart0_cb.tx_timeout_work); @@ -720,8 +736,7 @@ static void rx_timeout(struct k_work *work) rx_rdy_evt(); } -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) +#if HW_FLOW_CONTROL static void tx_timeout(struct k_work *work) { struct uart_event evt; @@ -808,7 +823,7 @@ static void uart_nrfx_irq_tx_enable(struct device *dev) /* Due to HW limitation first TXDRDY interrupt shall be * triggered by the software. */ - NVIC_SetPendingIRQ(DT_NORDIC_NRF_UART_UART_0_IRQ_0); + NVIC_SetPendingIRQ(IRQN); } irq_unlock(key); } @@ -935,29 +950,24 @@ static int uart_nrfx_init(struct device *dev) /* Setting default height state of the TX PIN to avoid glitches * on the line during peripheral activation/deactivation. */ - nrf_gpio_pin_write(DT_NORDIC_NRF_UART_UART_0_TX_PIN, 1); - nrf_gpio_cfg_output(DT_NORDIC_NRF_UART_UART_0_TX_PIN); + nrf_gpio_pin_write(TX_PIN, 1); + nrf_gpio_cfg_output(TX_PIN); - if (RX_PIN_USED()) { + if (RX_PIN_USED) { nrf_gpio_cfg_input(RX_PIN, NRF_GPIO_PIN_NOPULL); } - nrf_uart_txrx_pins_set(uart0_addr, - DT_NORDIC_NRF_UART_UART_0_TX_PIN, RX_PIN); -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) + nrf_uart_txrx_pins_set(uart0_addr, TX_PIN, RX_PIN); +#if HW_FLOW_CONTROL /* Setting default height state of the RTS PIN to avoid glitches * on the line during peripheral activation/deactivation. */ - nrf_gpio_pin_write(DT_NORDIC_NRF_UART_UART_0_RTS_PIN, 1); - nrf_gpio_cfg_output(DT_NORDIC_NRF_UART_UART_0_RTS_PIN); + nrf_gpio_pin_write(RTS_PIN, 1); + nrf_gpio_cfg_output(RTS_PIN); - nrf_gpio_cfg_input(DT_NORDIC_NRF_UART_UART_0_CTS_PIN, - NRF_GPIO_PIN_NOPULL); + nrf_gpio_cfg_input(CTS_PIN, NRF_GPIO_PIN_NOPULL); - nrf_uart_hwfc_pins_set(uart0_addr, - DT_NORDIC_NRF_UART_UART_0_RTS_PIN, - DT_NORDIC_NRF_UART_UART_0_CTS_PIN); + nrf_uart_hwfc_pins_set(uart0_addr, RTS_PIN, CTS_PIN); #endif /* Set initial configuration */ @@ -972,7 +982,7 @@ static int uart_nrfx_init(struct device *dev) */ nrf_uart_enable(uart0_addr); - if (RX_PIN_USED()) { + if (RX_PIN_USED) { nrf_uart_event_clear(uart0_addr, NRF_UART_EVENT_RXDRDY); nrf_uart_task_trigger(uart0_addr, NRF_UART_TASK_STARTRX); @@ -987,18 +997,17 @@ static int uart_nrfx_init(struct device *dev) #if defined(CONFIG_UART_0_ASYNC) || defined(CONFIG_UART_0_INTERRUPT_DRIVEN) - IRQ_CONNECT(DT_NORDIC_NRF_UART_UART_0_IRQ_0, - DT_NORDIC_NRF_UART_UART_0_IRQ_0_PRIORITY, + IRQ_CONNECT(IRQN, + IRQ_PRIO, uart_nrfx_isr, DEVICE_GET(uart_nrfx_uart0), 0); - irq_enable(DT_NORDIC_NRF_UART_UART_0_IRQ_0); + irq_enable(IRQN); #endif #ifdef CONFIG_UART_0_ASYNC k_delayed_work_init(&uart0_cb.rx_timeout_work, rx_timeout); -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) +#if HW_FLOW_CONTROL k_delayed_work_init(&uart0_cb.tx_timeout_work, tx_timeout); #endif #endif @@ -1056,7 +1065,7 @@ static void uart_nrfx_pins_enable(struct device *dev, bool enable) if (enable) { nrf_gpio_pin_write(tx_pin, 1); nrf_gpio_cfg_output(tx_pin); - if (RX_PIN_USED()) { + if (RX_PIN_USED) { nrf_gpio_cfg_input(rx_pin, NRF_GPIO_PIN_NOPULL); } @@ -1068,7 +1077,7 @@ static void uart_nrfx_pins_enable(struct device *dev, bool enable) } } else { nrf_gpio_cfg_default(tx_pin); - if (RX_PIN_USED()) { + if (RX_PIN_USED) { nrf_gpio_cfg_default(rx_pin); } if (get_dev_config(dev)->rts_cts_pins_set) { @@ -1083,7 +1092,7 @@ static void uart_nrfx_set_power_state(struct device *dev, u32_t new_state) if (new_state == DEVICE_PM_ACTIVE_STATE) { uart_nrfx_pins_enable(dev, true); nrf_uart_enable(uart0_addr); - if (RX_PIN_USED()) { + if (RX_PIN_USED) { nrf_uart_task_trigger(uart0_addr, NRF_UART_TASK_STARTRX); } @@ -1125,7 +1134,7 @@ static struct uart_nrfx_data uart_nrfx_uart0_data = { .uart_config = { .stop_bits = UART_CFG_STOP_BITS_1, .data_bits = UART_CFG_DATA_BITS_8, - .baudrate = DT_NORDIC_NRF_UART_UART_0_CURRENT_SPEED, + .baudrate = BAUDRATE, #ifdef CONFIG_UART_0_NRF_PARITY_BIT .parity = UART_CFG_PARITY_EVEN, #else @@ -1140,16 +1149,11 @@ static struct uart_nrfx_data uart_nrfx_uart0_data = { }; static const struct uart_nrfx_config uart_nrfx_uart0_config = { -#if defined(DT_NORDIC_NRF_UART_UART_0_RTS_PIN) && \ - defined(DT_NORDIC_NRF_UART_UART_0_CTS_PIN) - .rts_cts_pins_set = true, -#else - .rts_cts_pins_set = false, -#endif + .rts_cts_pins_set = HW_FLOW_CONTROL, }; DEVICE_DEFINE(uart_nrfx_uart0, - DT_NORDIC_NRF_UART_UART_0_LABEL, + DT_INST_LABEL(0), uart_nrfx_init, uart_nrfx_pm_control, &uart_nrfx_uart0_data, diff --git a/drivers/serial/uart_sifive.c b/drivers/serial/uart_sifive.c index b94f56ef97175..0b22f0118ae3e 100644 --- a/drivers/serial/uart_sifive.c +++ b/drivers/serial/uart_sifive.c @@ -8,6 +8,8 @@ * @brief UART driver for the SiFive Freedom Processor */ +#define DT_DRV_COMPAT sifive_uart0 + #include #include #include @@ -381,9 +383,9 @@ static void uart_sifive_irq_cfg_func_0(void); #endif static const struct uart_sifive_device_config uart_sifive_dev_cfg_0 = { - .port = DT_INST_0_SIFIVE_UART0_BASE_ADDRESS, - .sys_clk_freq = DT_INST_0_SIFIVE_UART0_CLOCK_FREQUENCY, - .baud_rate = DT_INST_0_SIFIVE_UART0_CURRENT_SPEED, + .port = DT_INST_REG_ADDR(0), + .sys_clk_freq = DT_INST_PROP(0, clock_frequency), + .baud_rate = DT_INST_PROP(0, current_speed), .rxcnt_irq = CONFIG_UART_SIFIVE_PORT_0_RXCNT_IRQ, .txcnt_irq = CONFIG_UART_SIFIVE_PORT_0_TXCNT_IRQ, #ifdef CONFIG_UART_INTERRUPT_DRIVEN @@ -391,7 +393,7 @@ static const struct uart_sifive_device_config uart_sifive_dev_cfg_0 = { #endif }; -DEVICE_AND_API_INIT(uart_sifive_0, DT_INST_0_SIFIVE_UART0_LABEL, +DEVICE_AND_API_INIT(uart_sifive_0, DT_INST_PROP(0, label), uart_sifive_init, &uart_sifive_data_0, &uart_sifive_dev_cfg_0, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, @@ -400,12 +402,12 @@ DEVICE_AND_API_INIT(uart_sifive_0, DT_INST_0_SIFIVE_UART0_LABEL, #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void uart_sifive_irq_cfg_func_0(void) { - IRQ_CONNECT(DT_INST_0_SIFIVE_UART0_IRQ_0, + IRQ_CONNECT(DT_INST_IRQN(0), CONFIG_UART_SIFIVE_PORT_0_IRQ_PRIORITY, uart_sifive_irq_handler, DEVICE_GET(uart_sifive_0), 0); - irq_enable(DT_INST_0_SIFIVE_UART0_IRQ_0); + irq_enable(DT_INST_IRQN(0)); } #endif @@ -420,9 +422,9 @@ static void uart_sifive_irq_cfg_func_1(void); #endif static const struct uart_sifive_device_config uart_sifive_dev_cfg_1 = { - .port = DT_INST_1_SIFIVE_UART0_BASE_ADDRESS, - .sys_clk_freq = DT_INST_1_SIFIVE_UART0_CLOCK_FREQUENCY, - .baud_rate = DT_INST_1_SIFIVE_UART0_CURRENT_SPEED, + .port = DT_INST_REG_ADDR(1), + .sys_clk_freq = DT_INST_PROP(1, clock_frequency), + .baud_rate = DT_INST_PROP(1, current_speed), .rxcnt_irq = CONFIG_UART_SIFIVE_PORT_1_RXCNT_IRQ, .txcnt_irq = CONFIG_UART_SIFIVE_PORT_1_TXCNT_IRQ, #ifdef CONFIG_UART_INTERRUPT_DRIVEN @@ -430,7 +432,7 @@ static const struct uart_sifive_device_config uart_sifive_dev_cfg_1 = { #endif }; -DEVICE_AND_API_INIT(uart_sifive_1, DT_INST_1_SIFIVE_UART0_LABEL, +DEVICE_AND_API_INIT(uart_sifive_1, DT_INST_PROP(1, label), uart_sifive_init, &uart_sifive_data_1, &uart_sifive_dev_cfg_1, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, @@ -439,12 +441,12 @@ DEVICE_AND_API_INIT(uart_sifive_1, DT_INST_1_SIFIVE_UART0_LABEL, #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void uart_sifive_irq_cfg_func_1(void) { - IRQ_CONNECT(DT_INST_1_SIFIVE_UART0_IRQ_0, + IRQ_CONNECT(DT_INST_IRQN(1), CONFIG_UART_SIFIVE_PORT_1_IRQ_PRIORITY, uart_sifive_irq_handler, DEVICE_GET(uart_sifive_1), 0); - irq_enable(DT_INST_1_SIFIVE_UART0_IRQ_0); + irq_enable(DT_INST_IRQN(1)); } #endif diff --git a/drivers/spi/spi_nrfx_spim.c b/drivers/spi/spi_nrfx_spim.c index 1a2e1dbf49779..7182a295359c4 100644 --- a/drivers/spi/spi_nrfx_spim.c +++ b/drivers/spi/spi_nrfx_spim.c @@ -366,11 +366,17 @@ static int spim_nrfx_pm_control(struct device *dev, u32_t ctrl_command, } #endif /* CONFIG_DEVICE_POWER_MANAGEMENT */ -#define SPIM_NRFX_MISO_PULL_DOWN(idx) \ - IS_ENABLED(DT_NORDIC_NRF_SPIM_SPI_##idx##_MISO_PULL_DOWN) +/* + * We use NODELABEL here because the nrfx API requires us to call + * functions which are named according to SoC peripheral instance + * being operated on. Since DT_INST() makes no guarantees about that, + * it won't work. + */ +#define SPIM(idx) DT_NODELABEL(spi##idx) +#define SPIM_PROP(idx, prop) DT_PROP(SPIM(idx), prop) -#define SPIM_NRFX_MISO_PULL_UP(idx) \ - IS_ENABLED(DT_NORDIC_NRF_SPIM_SPI_##idx##_MISO_PULL_UP) +#define SPIM_NRFX_MISO_PULL_DOWN(idx) DT_PROP(SPIM(idx), miso_pull_down) +#define SPIM_NRFX_MISO_PULL_UP(idx) DT_PROP(SPIM(idx), miso_pull_up) #define SPIM_NRFX_MISO_PULL(idx) \ (SPIM_NRFX_MISO_PULL_UP(idx) \ @@ -396,7 +402,7 @@ static int spim_nrfx_pm_control(struct device *dev, u32_t ctrl_command, static int spi_##idx##_init(struct device *dev) \ { \ IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_SPIM##idx), \ - DT_NORDIC_NRF_SPIM_SPI_##idx##_IRQ_0_PRIORITY, \ + DT_IRQ(SPIM(idx), priority), \ nrfx_isr, nrfx_spim_##idx##_irq_handler, 0); \ return init_spim(dev); \ } \ @@ -409,9 +415,9 @@ static int spim_nrfx_pm_control(struct device *dev, u32_t ctrl_command, .spim = NRFX_SPIM_INSTANCE(idx), \ .max_chunk_len = (1 << SPIM##idx##_EASYDMA_MAXCNT_SIZE) - 1, \ .config = { \ - .sck_pin = DT_NORDIC_NRF_SPIM_SPI_##idx##_SCK_PIN, \ - .mosi_pin = DT_NORDIC_NRF_SPIM_SPI_##idx##_MOSI_PIN, \ - .miso_pin = DT_NORDIC_NRF_SPIM_SPI_##idx##_MISO_PIN, \ + .sck_pin = SPIM_PROP(idx, sck_pin), \ + .mosi_pin = SPIM_PROP(idx, mosi_pin), \ + .miso_pin = SPIM_PROP(idx, miso_pin), \ .ss_pin = NRFX_SPIM_PIN_NOT_USED, \ .orc = CONFIG_SPI_##idx##_NRF_ORC, \ .frequency = NRF_SPIM_FREQ_4M, \ @@ -422,7 +428,7 @@ static int spim_nrfx_pm_control(struct device *dev, u32_t ctrl_command, } \ }; \ DEVICE_DEFINE(spi_##idx, \ - DT_NORDIC_NRF_SPIM_SPI_##idx##_LABEL, \ + SPIM_PROP(idx, label), \ spi_##idx##_init, \ spim_nrfx_pm_control, \ &spi_##idx##_data, \ diff --git a/dts/bindings/test/vnd,adc.yaml b/dts/bindings/test/vnd,adc.yaml new file mode 100644 index 0000000000000..f7b274ea3b95c --- /dev/null +++ b/dts/bindings/test/vnd,adc.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2020, Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test ADC + +compatible: "vnd,adc" + +include: adc-controller.yaml + +properties: + "#io-channel-cells": + const: 1 + +io-channel-cells: + - input diff --git a/dts/bindings/test/vnd,array-holder.yaml b/dts/bindings/test/vnd,array-holder.yaml new file mode 100644 index 0000000000000..66637d3079451 --- /dev/null +++ b/dts/bindings/test/vnd,array-holder.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test array container + +compatible: "vnd,array-holder" + +include: [base.yaml] + +properties: + a: {type: "array"} + b: {type: "uint8-array"} + c: {type: "string-array"} + +gpio-cells: + - pin + - flags diff --git a/dts/bindings/test/vnd,clock.yaml b/dts/bindings/test/vnd,clock.yaml new file mode 100644 index 0000000000000..325c0a2b08c9c --- /dev/null +++ b/dts/bindings/test/vnd,clock.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test Clock Controller + +compatible: "vnd,clock" + +include: [clock-controller.yaml, base.yaml] + +properties: + "#clock-cells": + const: 2 + +clock-cells: + - bus + - bits diff --git a/dts/bindings/test/vnd,disabled-compat.yaml b/dts/bindings/test/vnd,disabled-compat.yaml new file mode 100644 index 0000000000000..5d074e0fa81f2 --- /dev/null +++ b/dts/bindings/test/vnd,disabled-compat.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test disabled compatible + +compatible: "vnd,disabled-compat" + +include: base.yaml diff --git a/dts/bindings/test/vnd,enum-holder.yaml b/dts/bindings/test/vnd,enum-holder.yaml new file mode 100644 index 0000000000000..e4ff7a2cd0217 --- /dev/null +++ b/dts/bindings/test/vnd,enum-holder.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test enum property container + +compatible: "vnd,enum-holder" + +include: [base.yaml] + +properties: + val: + type: string + required: true + enum: + - "zero" + - "one" + - "two" diff --git a/dts/bindings/test/vnd,gpio-one-cell.yaml b/dts/bindings/test/vnd,gpio-one-cell.yaml new file mode 100644 index 0000000000000..e8b5fa1ff3416 --- /dev/null +++ b/dts/bindings/test/vnd,gpio-one-cell.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test GPIO node with one cell + +compatible: "vnd,gpio-one-cell" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + label: + required: true + + "#gpio-cells": + const: 1 + +gpio-cells: + - pin diff --git a/dts/bindings/test/vnd,gpio.yaml b/dts/bindings/test/vnd,gpio.yaml new file mode 100644 index 0000000000000..e09f22eaf4889 --- /dev/null +++ b/dts/bindings/test/vnd,gpio.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test GPIO node + +compatible: "vnd,gpio" + +include: [gpio-controller.yaml, base.yaml] + +properties: + reg: + required: true + + label: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags + +foo-cells: + - foocell diff --git a/dts/bindings/test/vnd,i2c-device.yaml b/dts/bindings/test/vnd,i2c-device.yaml new file mode 100644 index 0000000000000..a22ec3b178e00 --- /dev/null +++ b/dts/bindings/test/vnd,i2c-device.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test I2C device + +compatible: "vnd,i2c-device" + +include: i2c-device.yaml diff --git a/dts/bindings/test/vnd,i2c.yaml b/dts/bindings/test/vnd,i2c.yaml new file mode 100644 index 0000000000000..39f5b5b8fe321 --- /dev/null +++ b/dts/bindings/test/vnd,i2c.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test I2C node + +compatible: "vnd,i2c" + +include: [i2c-controller.yaml, base.yaml] diff --git a/dts/bindings/test/vnd,intc.yaml b/dts/bindings/test/vnd,intc.yaml new file mode 100644 index 0000000000000..ace8f796cbac6 --- /dev/null +++ b/dts/bindings/test/vnd,intc.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test Interrupt Controller + +compatible: "vnd,intc" + +include: [interrupt-controller.yaml, base.yaml] + +properties: + reg: + required: true + + "#interrupt-cells": + const: 2 + +interrupt-cells: + - irq + - priority diff --git a/dts/bindings/test/vnd,interrupt-holder.yaml b/dts/bindings/test/vnd,interrupt-holder.yaml new file mode 100644 index 0000000000000..38d611ad4d35d --- /dev/null +++ b/dts/bindings/test/vnd,interrupt-holder.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test Interrupt Controller + +compatible: "vnd,interrupt-holder" + +include: [base.yaml] + +properties: + interrupts: + required: true + + interrupt-names: + required: true diff --git a/dts/bindings/test/vnd,phandle-holder.yaml b/dts/bindings/test/vnd,phandle-holder.yaml new file mode 100644 index 0000000000000..b45719e1f951d --- /dev/null +++ b/dts/bindings/test/vnd,phandle-holder.yaml @@ -0,0 +1,16 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test phandle property container + +compatible: "vnd,phandle-holder" + +include: [base.yaml] + +properties: + ph: {type: "phandle"} + phs: {type: "phandles"} + pha-gpios: {type: "phandle-array"} + gpios: {type: "phandle-array"} + foos: {type: "phandle-array"} + foo-names: {type: "string-array"} diff --git a/dts/bindings/test/vnd,reg-holder.yaml b/dts/bindings/test/vnd,reg-holder.yaml new file mode 100644 index 0000000000000..c2685a49e3eb1 --- /dev/null +++ b/dts/bindings/test/vnd,reg-holder.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2020 Linaro Ltd. +# SPDX-License-Identifier: Apache-2.0 + +description: Test register property container + +compatible: "vnd,reg-holder" + +include: [base.yaml] + +properties: + reg: + required: true + + reg-names: + required: true + + misc-prop: + type: int diff --git a/dts/bindings/test/vnd,spi-device-2.yaml b/dts/bindings/test/vnd,spi-device-2.yaml new file mode 100644 index 0000000000000..79983f1641b89 --- /dev/null +++ b/dts/bindings/test/vnd,spi-device-2.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test SPI device 2 + +compatible: "vnd,spi-device-2" + +include: spi-device.yaml diff --git a/dts/bindings/test/vnd,spi-device.yaml b/dts/bindings/test/vnd,spi-device.yaml new file mode 100644 index 0000000000000..7bf2eeccc799a --- /dev/null +++ b/dts/bindings/test/vnd,spi-device.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test SPI device + +compatible: "vnd,spi-device" + +include: spi-device.yaml diff --git a/dts/bindings/test/vnd,spi.yaml b/dts/bindings/test/vnd,spi.yaml new file mode 100644 index 0000000000000..edd12ad2026ee --- /dev/null +++ b/dts/bindings/test/vnd,spi.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test SPI node + +compatible: "vnd,spi" + +include: [spi-controller.yaml, base.yaml] diff --git a/dts/bindings/test/vnd,temperature-sensor.yaml b/dts/bindings/test/vnd,temperature-sensor.yaml new file mode 100644 index 0000000000000..8370a21754a7d --- /dev/null +++ b/dts/bindings/test/vnd,temperature-sensor.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2018, Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Test ADC-based temperature sensor + +compatible: "vnd,adc-temp-sensor" + +include: base.yaml + +properties: + label: + required: true + + io-channels: + type: phandle-array + required: true + description: ADC conversion channels + + io-channel-names: + type: string-array + required: true + description: conversion channel names + + clocks: + required: true diff --git a/dts/bindings/vendor-prefixes.txt b/dts/bindings/vendor-prefixes.txt index 75a202e1d6a4a..8a4cd3b9042fd 100644 --- a/dts/bindings/vendor-prefixes.txt +++ b/dts/bindings/vendor-prefixes.txt @@ -440,6 +440,7 @@ virtio Virtual I/O Device Specification, developed by the OASIS consortium vishay Vishay Intertechnology, Inc vitesse Vitesse Semiconductor Corporation vivante Vivante Corporation +vnd A stand-in for a real vendor which can be used in examples and tests vocore VoCore Studio voipac Voipac Technologies s.r.o. vot Vision Optical Technology Co., Ltd. diff --git a/include/devicetree.h b/include/devicetree.h index 4c7ee3450abba..a13c59655a8c7 100644 --- a/include/devicetree.h +++ b/include/devicetree.h @@ -1,14 +1,1385 @@ /* * SPDX-License-Identifier: Apache-2.0 * Copyright (c) 2020 Nordic Semiconductor + * Copyright (c) 2020, Linaro Ltd. * * Not a generated file. Feel free to modify. */ +/** + * @file + * @brief Devicetree main header + * + * API for accessing the current application's devicetree macros. + */ + #ifndef DEVICETREE_H #define DEVICETREE_H #include +#include #include +#include + +/** + * @brief devicetree.h API + */ + +/* + * Property suffixes + * ----------------- + * + * These are the optional parts that come after the _P_ + * part in DT_N__P_ macros, or the "prop-suf" + * nonterminal in the DT guide's macros.bnf file. + * + * Before adding new ones, check this list to avoid conflicts. If any + * are missing from this list, please add them. It should be complete. + * + * _ENUM_IDX: property's value as an index into bindings enum + * _EXISTS: property is defined + * _IDX_: logical index into property + * _IDX__PH: phandle array's phandle by index (or phandle, phandles) + * _IDX__VAL_: phandle array's specifier value by index + * _IDX__VAL__EXISTS: cell value exists, by index + * _LEN: property logical length + * _NAME__PH: phandle array's phandle by name + * _NAME__VAL_: phandle array's property specifier by name + * _NAME__VAL__EXISTS: cell value exists, by name + */ + +/** + * @defgroup devicetree-generic-id Node identifiers + * @{ + */ + +/** + * @brief Node identifier for the root node in the devicetree + */ +#define DT_ROOT DT_N + +/** + * @brief Get a node identifier for a devicetree path + * + * Example devicetree fragment: + * + * / { + * soc { + * my-serial: serial@40002000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * }; + * }; + * + * Example usage with @ref DT_PROP() to get current-speed: + * + * DT_PROP(DT_PATH(soc, serial_40002000), current_speed) // 115200 + * + * The arguments to this macro are the names of non-root nodes in the + * tree required to reach the desired node, starting from the root. + * Non-alphanumeric characters in each name must be converted to + * underscores to form valid C tokens, and letters must be lowercased. + * + * That is: + * + * - a first argument corresponds to a child node of the root ("soc" above) + * - a second argument corresponds to a child of the first argument + * ("serial_40002000" above, from the node name "serial@40002000" + * after changing "@" to "_") + * - and so on for deeper nodes until the desired path is given + * + * @param ... lowercase-and-underscores node names along the node's path, + * with each name given as a separate argument + * @return node identifier for the node with that path + */ +#define DT_PATH(...) DT_PATH_INTERNAL(__VA_ARGS__) + +/** + * @brief Get a node identifier for a node label + * + * Example devicetree fragment: + * + * my-serial: serial@40002000 { + * label = "UART_0"; + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * + * The only node label in this example is "my-serial". + * The string "UART_0" is *not* a node label; it's the value of a + * property named "label". + * + * Example usage to get current-speed: + * + * DT_PROP(DT_NODELABEL(my_serial), current_speed) // 115200 + * + * Convert non-alphanumeric characters in the label to underscores as + * shown, and lowercase all letters. + * + * Another example devicetree fragment: + * + * cpu@0 { + * L2_0: l2-cache { + * cache-level = <2>; + * ... + * }; + * }; + * + * Example usage to get cache-level: + * + * DT_PROP(DT_NODELABEL(l2_0), cache_level) // 2 + * + * Notice how "L2_0" in the devicetree is lowercased to "l2_0" + * for this macro's argument. + * + * @param label lowercase-and-underscores node label name + * @return node identifier for the node with that label + */ +#define DT_NODELABEL(label) DT_CAT(DT_N_NODELABEL_, label) + +/** + * @brief Get a node identifier for an alias + * + * Example devicetree fragment: + * + * aliases { + * my-serial = &serial0; + * }; + * + * serial0: serial@40002000 { + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * + * Example usage to get current-speed: + * + * DT_PROP(DT_ALIAS(my_serial), current_speed) // 115200 + * + * Convert non-alphanumeric characters in the alias to underscores as + * shown, and lowercase all letters. + * + * @param alias lowercase-and-underscores alias name. + * @return node identifier for the node with that alias + */ +#define DT_ALIAS(alias) DT_CAT(DT_N_ALIAS_, alias) + +/** + * @brief Get a node identifier for an instance of a compatible + * + * Instance numbers are just indexes among enabled nodes with the same + * compatible. This complicates their use outside of device drivers. + * The **only guarantees** are: + * + * - instance numbers start at 0, + * - are contiguous, and + * - exactly one is assigned for each enabled node with a matching + * compatible + * + * Instance numbers **in no way reflect** any numbering scheme that + * might exist in SoC documentation, node labels or unit addresses, or + * properties of the /aliases node. There **is no guarantee** that the + * same node will have the same instance number between builds, even + * if you are building the same application again in the same build + * directory. + * + * Example devicetree fragment: + * + * serial@40002000 { + * compatible = "vnd,soc-serial"; + * status = "okay"; + * current-speed = <115200>; + * ... + * }; + * + * Example usage to get current-speed, **assuming that** this node is + * instance number zero of the compatible "vnd,soc-serial": + * + * DT_PROP(DT_INST(0, vnd_soc_serial), current_speed) // 115200 + * + * @param inst instance number + * @param compat lowercase-and-underscores compatible, without quotes + * @return node identifier for the node with that instance number and + * compatible + */ +#define DT_INST(inst, compat) UTIL_CAT(DT_N_INST, DT_DASH(inst, compat)) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-prop Property accessors + * @{ + */ + +/** + * @brief Get a devicetree property value + * + * For properties whose bindings have the following types, this macro + * expands to: + * + * - string: a string literal + * - boolean: 0 if the property is false, or 1 if it is true + * - int: the property's value as an integer literal + * - array, uint8-array, string-array: an initializer expression in braces, + * whose elements are integer or string literals (like {0, 1, 2}, + * {"hello", "world"}, etc.) + * - phandle: a node identifier + * + * For other properties, behavior is undefined. + * + * For examples, see @ref DT_PATH(), @ref DT_ALIAS(), @ref DT_NODELABEL(), + * and @ref DT_INST(). + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @return a representation of the property's value + */ +#define DT_PROP(node_id, prop) DT_CAT(node_id, _P_##prop) + +/** + * @brief Get a property's logical length + * + * Here, "length" is a number of elements, which may not + * be a size in bytes. + * + * For properties whose binding has type array, string-array, or + * uint8-array, this expands to the number of elements in the array. + * + * For properties of type phandles or phandle-array, it expands to the + * number of phandles or phandle+specifiers respectively. + * + * These properties are handled as special cases: + * + * - reg property: use DT_NUM_REGS(node_id) instead + * - interrupts property: use DT_NUM_IRQS(node_id) instead + * + * It is an error to use this macro with the above properties. + * + * For other properties, behavior is undefined. + * + * @param node_id node identifier + * @param prop a lowercase-and-underscores property with a logical length + * @return the property's length + */ +#define DT_PROP_LEN(node_id, prop) DT_PROP(node_id, prop##_LEN) + +/** + * @brief Is index "idx" valid for an array type property? + * + * If this returns 1, then DT_PROP_BY_IDX(node_id, prop, idx) or + * DT_PHA_BY_IDX(node_id, pha, idx, cell) are valid at index "idx". + * If it returns 0, it is an error to use those macros with that index. + * + * These properties are handled as special cases: + * + * - reg property: use DT_REG_HAS_IDX(node_id, idx) instead + * - interrupts property: use DT_IRQ_HAS_IDX(node_id, idx) instead + * + * It is an error to use this macro with the above properties. + * + * @param node_id node identifier + * @param prop a lowercase-and-underscores property with a logical length + * @param idx index to check + * @return 1 if "idx" is a valid index into the given property, + * 0 otherwise. + */ +#define DT_PROP_HAS_IDX(node_id, prop, idx) \ + ((idx) < DT_PROP_LEN(node_id, prop)) + +/** + * @brief Get the value at index "idx" in an array type property + * + * It might help to read the argument order as being similar to + * "node->property[index]". + * + * When the property's binding has type array, string-array, + * uint8-array, or phandles, this expands to the idx-th array element + * as an integer, string literal, or node identifier respectively. + * + * These properties are handled as special cases: + * + * - reg property: use DT_REG_ADDR_BY_IDX() or DT_REG_SIZE_BY_IDX() instead + * - interrupts property: use DT_IRQ_BY_IDX() instead + * + * For non-array properties, behavior is undefined. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @param idx the index to get + * @return a representation of the idx-th element of the property + */ +#define DT_PROP_BY_IDX(node_id, prop, idx) DT_PROP(node_id, prop##_IDX_##idx) + +/** + * @brief Equivalent to DT_PROP(node_id, label) + * + * This is a convenience for the Zephyr device API, which uses label + * properties as device_get_binding() arguments. + * @param node_id node identifier + * @return node's label property value + */ +#define DT_LABEL(node_id) DT_PROP(node_id, label) + +/** + * @brief Get a property value's index into its enumeration values + * + * The return values start at zero. + * + * Example devicetree fragment: + * + * usb1: usb@12340000 { + * maximum-speed = "full-speed"; + * }; + * usb2: usb@12341000 { + * maximum-speed = "super-speed"; + * }; + * + * Example bindings fragment: + * + * properties: + * maximum-speed: + * type: string + * enum: + * - "low-speed" + * - "full-speed" + * - "high-speed" + * - "super-speed" + * + * Example usage: + * + * DT_ENUM_IDX(DT_NODELABEL(usb1), maximum_speed) // 1 + * DT_ENUM_IDX(DT_NODELABEL(usb2), maximum_speed) // 3 + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @return zero-based index of the property's value in its enum: list + */ +#define DT_ENUM_IDX(node_id, prop) DT_PROP(node_id, prop##_ENUM_IDX) + +/* + * phandle properties + * + * These are special-cased to manage the impedance mismatch between + * phandles, which are just u32_t node properties that only make sense + * within the tree itself, and C values. + */ + +/** + * @brief Get a property value from a phandle's node + * + * This is a shorthand for DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop). + * It helps readability when "ph" has type "phandle". + * + * @param node_id node identifier + * @param ph lowercase-and-underscores property of "node_id" + * with type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the value of "prop" as described in the DT_PROP() documentation + */ +#define DT_PROP_BY_PHANDLE(node_id, ph, prop) \ + DT_PROP_BY_PHANDLE_IDX(node_id, ph, 0, prop) + +/** + * @brief Get a property value from a phandle in a property. + * + * This is a shorthand for: + * + * DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop) + * + * That is, "prop" is a property of the phandle's node, not a + * property of "node_id". + * + * Example devicetree fragment: + * + * n1: node-1 { + * foo = <&n2 &n3>; + * }; + * + * n2: node-2 { + * bar = <42>; + * }; + * + * n3: node-3 { + * baz = <43>; + * }; + * + * Example usage: + * + * #define N1 DT_NODELABEL(n1) + * + * DT_PROP_BY_PHANDLE_IDX(N1, foo, 0, bar) // 42 + * DT_PROP_BY_PHANDLE_IDX(N1, foo, 1, baz) // 43 + * + * @param node_id node identifier + * @param phs lowercase-and-underscores property with type "phandle", + * "phandles", or "phandle-array" + * @param idx logical index into "phs", which must be zero if "phs" + * has type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the value of "prop" as described in the DT_PROP() documentation + */ +#define DT_PROP_BY_PHANDLE_IDX(node_id, phs, idx, prop) \ + DT_PROP(DT_PHANDLE_BY_IDX(node_id, phs, idx), prop) + +/** + * @brief Get a phandle-array specifier value at an index + * + * It might help to read the argument order as being similar to + * "node->phandle[index].cell". That is, the cell value is in the + * "pha" property of "node_id". + * + * Example devicetree fragment: + * + * gpio0: gpio@12340000 { + * #gpio-cells = < 2 >; + * }; + * + * led: led_0 { + * gpios = < &gpio0 17 0x1 >; + * }; + * + * Bindings fragment for the gpio0 node: + * + * gpio-cells: + * - pin + * - flags + * + * Example usage: + * + * #define LED DT_NODELABEL(led) + * + * DT_PHA_BY_IDX(LED, gpios, pin, 0) // 17 + * DT_PHA_BY_IDX(LED, gpios, flags, 0) // 0x1 + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx logical index into the property "pha" + * @param cell binding's cell name within the specifier at index "idx" + * @return the value of the cell inside the specifier at index "idx" + */ +#define DT_PHA_BY_IDX(node_id, pha, idx, cell) \ + DT_PROP(node_id, pha##_IDX_##idx##_VAL_##cell) + +/** + * @brief Equivalent to DT_PHA_BY_IDX(node_id, pha, 0, cell) + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell binding's cell name for the specifier at "pha" index 0 + * @return the cell value + */ +#define DT_PHA(node_id, pha, cell) DT_PHA_BY_IDX(node_id, pha, 0, cell) + +/** + * @brief Get a value within a phandle-array specifier by name + * + * This is like DT_PHA_BY_IDX(), except it treats "pha" as a structure + * where each specifier has a name. + * + * It might help to read the argument order as being similar to + * "node->phandle_struct.name.cell". That is, the cell value is in the + * "pha" property of "node_id", treated as a data structure with named + * components. + * + * Example devicetree fragment: + * + * n: node { + * io-channels = <&adc1 10>, <&adc2 20>; + * io-channel-names = "SENSOR", "BANDGAP"; + * }; + * + * Bindings fragment for the "adc1" and "adc2" nodes: + * + * io-channel-cells: + * - input + * + * Example usage: + * + * DT_PHA_BY_NAME(DT_NODELABEL(n), io_channels, sensor, input) // 10 + * DT_PHA_BY_NAME(DT_NODELABEL(n), io_channels, bandgap, input) // 20 + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of a specifier in "pha" + * @param cell binding's cell name for the named specifier + * @return the cell value + */ +#define DT_PHA_BY_NAME(node_id, pha, name, cell) \ + DT_PROP(node_id, pha##_NAME_##name##_VAL_##cell) + +/** + * @brief Get a phandle's node identifier from a phandle array by name + * + * It might help to read the argument order as being similar to + * "node->phandle_struct.name.phandle". That is, the phandle array is + * treated as a structure with named components. The return value is + * the node identifier for a phandle inside the structure. + * + * Example devicetree fragment: + * + * adc1: adc@... { + * label = "ADC_1"; + * }; + * + * adc2: adc@... { + * label = "ADC_2"; + * }; + * + * n: node { + * io-channels = <&adc1 10>, <&adc2 20>; + * io-channel-names = "SENSOR", "BANDGAP"; + * }; + * + * Example usage: + * + * #define NODE DT_NODELABEL(n) + * + * DT_LABEL(DT_PHANDLE_BY_NAME(NODE, io_channels, sensor)) // "ADC_1" + * DT_LABEL(DT_PHANDLE_BY_NAME(NODE, io_channels, bandgap)) // "ADC_2" + * + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of an element in "pha" + * @return node identifier for the phandle at the element named "name" + */ +#define DT_PHANDLE_BY_NAME(node_id, pha, name) \ + DT_PROP(node_id, pha##_NAME_##name##_PH) + +/** + * @brief Get a node identifier for a phandle in a property. + * + * When a node's value at a logical index contains a phandle, this + * macro returns a node identifier for the node with that phandle. + * + * Therefore, if "prop" has type "phandle", "idx" must be zero. (A + * "phandle" type is treated as a "phandles" with a fixed length of + * 1). + * + * Example devicetree fragment: + * + * n1: node-1 { + * foo = <&n2 &n3>; + * }; + * + * n2: node-2 { ... }; + * n3: node-3 { ... }; + * + * Example usage: + * + * #define N1 DT_NODELABEL(n1) + * + * DT_PHANDLE_BY_IDX(N1, foo, 0) // node identifier for node-2 + * DT_PHANDLE_BY_IDX(N1, foo, 1) // node identifier for node-3 + * + * Behavior is analogous for phandle-arrays. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name in "node_id" + * with type "phandle", "phandles" or "phandle-array" + * @param idx index into "prop" + * @return a node identifier for the phandle at index "idx" in "prop" + */ +#define DT_PHANDLE_BY_IDX(node_id, prop, idx) \ + DT_PROP(node_id, prop##_IDX_##idx##_PH) + +/** + * @brief Get a node identifier for a phandle property's value + * + * This is equivalent to DT_PHANDLE_BY_IDX(node_id, prop, 0). Its primary + * benefit is readability when "prop" has type "phandle". + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property of "node_id" + * with type "phandle" + * @return a node identifier for the node pointed to by "ph" + */ +#define DT_PHANDLE(node_id, prop) DT_PHANDLE_BY_IDX(node_id, prop, 0) + +/* + * reg property + */ + +/** + * @brief Get the number of register blocks in the reg property + * + * Use this instead of DT_PROP_LEN(node_id, reg). + * @param node_id node identifier + * @return Number of register blocks in the node's "reg" property. + */ +#define DT_NUM_REGS(node_id) DT_CAT(node_id, _REG_NUM) + +/** + * @brief Is "idx" a valid register block index? + * + * If this returns 1, then DT_REG_ADDR_BY_IDX(node_id, idx) or + * DT_REG_SIZE_BY_IDX(node_id, idx) are valid. + * If it returns 0, it is an error to use those macros with index "idx". + * @param node_id node identifier + * @param idx index to check + * @return 1 if "idx" is a valid register block index, + * 0 otherwise. + */ +#define DT_REG_HAS_IDX(node_id, idx) ((idx) < DT_NUM_REGS(node_id)) + +/** + * @brief Get the base address of the register block at index "idx" + * @param node_id node identifier + * @param idx index of the register whose address to return + * @return address of the idx-th register block + */ +#define DT_REG_ADDR_BY_IDX(node_id, idx) \ + DT_CAT(node_id, _REG_IDX_##idx##_VAL_ADDRESS) + +/** + * @brief Get the size of the register block at index "idx" + * + * This is the size of an individual register block, not the total + * number of register blocks in the property; use DT_NUM_REGS() for + * that. + * + * @param node_id node identifier + * @param idx index of the register whose size to return + * @return size of the idx-th register block + */ +#define DT_REG_SIZE_BY_IDX(node_id, idx) \ + DT_CAT(node_id, _REG_IDX_##idx##_VAL_SIZE) + +/** + * @brief Get a node's (only) register block address + * + * Equivalent to DT_REG_ADDR_BY_IDX(node_id, 0). + * @param node_id node identifier + * @return node's register block address + */ +#define DT_REG_ADDR(node_id) DT_REG_ADDR_BY_IDX(node_id, 0) + +/** + * @brief Get a node's (only) register block size + * + * Equivalent to DT_REG_SIZE_BY_IDX(node_id, 0). + * @param node_id node identifier + * @return node's only register block's size + */ +#define DT_REG_SIZE(node_id) DT_REG_SIZE_BY_IDX(node_id, 0) + +/** + * @brief Get a register block's base address by name + * @param node_id node identifier + * @param name lowercase-and-underscores register specifier name + * @return address of the register block specified by name + */ +#define DT_REG_ADDR_BY_NAME(node_id, name) \ + DT_CAT(node_id, _REG_NAME_##name##_VAL_ADDRESS) + +/** + * @brief Get a register block's size by name + * @param node_id node identifier + * @param name lowercase-and-underscores register specifier name + * @return size of the register block specified by name + */ +#define DT_REG_SIZE_BY_NAME(node_id, name) \ + DT_CAT(node_id, _REG_NAME_##name##_VAL_SIZE) + +/* + * interrupts property + */ + +/** + * @brief Get the number of interrupt sources for the node + * + * Use this instead of DT_PROP_LEN(node_id, interrupts). + * + * @param node_id node identifier + * @return Number of interrupt specifiers in the node's "interrupts" property. + */ +#define DT_NUM_IRQS(node_id) DT_CAT(node_id, _IRQ_NUM) + +/** + * @brief Is "idx" a valid interrupt index? + * + * If this returns 1, then DT_IRQ_BY_IDX(node_id, idx) is valid. + * If it returns 0, it is an error to use that macro with this index. + * @param node_id node identifier + * @param idx index to check + * @return 1 if the idx is valid for the interrupt property + * 0 otherwise. + */ +#define DT_IRQ_HAS_IDX(node_id, idx) ((idx) < DT_NUM_IRQS(node_id)) + +/** + * @brief Get a value within an interrupt specifier at an index + * + * It might help to read the argument order as being similar to + * "node->interrupts[index].cell". + * + * This can be used to get information about an individual interrupt + * when a device generates more than one. + * + * Example devicetree fragment: + * + * my-serial: serial@... { + * interrupts = < 33 0 >, < 34 1 >; + * }; + * + * Assuming the node's interrupt domain has "#interrupt-cells = <2>;" and + * the individual cells in each interrupt specifier are named "irq" and + * "priority" by the node's binding, here are some examples: + * + * #define SERIAL DT_NODELABEL(my_serial) + * + * Example usage Value + * ------------- ----- + * DT_IRQ_BY_IDX(SERIAL, 0, irq) 33 + * DT_IRQ_BY_IDX(SERIAL, 0, priority) 0 + * DT_IRQ_BY_IDX(SERIAL, 1, irq, 34 + * DT_IRQ_BY_IDX(SERIAL, 1, priority) 1 + * + * @param node_id node identifier + * @param idx logical index into the interrupt specifier array + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_IRQ_BY_IDX(node_id, idx, cell) \ + DT_CAT(node_id, _IRQ_IDX_##idx##_VAL_##cell) + +/** + * @brief Get a value within an interrupt specifier by name + * + * It might help to read the argument order as being similar to + * "node->interrupts.name.cell". + * + * This can be used to get information about an individual interrupt + * when a device generates more than one, if the bindings give each + * interrupt specifier a name. + * + * @param node_id node identifier + * @param name lowercase-and-underscores interrupt specifier name + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_IRQ_BY_NAME(node_id, name, cell) \ + DT_CAT(node_id, _IRQ_NAME_##name##_VAL_##cell) + +/** + * @brief Get an interrupt specifier's value + * Equivalent to DT_IRQ_BY_IDX(node_id, 0, cell). + * @param node_id node identifier + * @param cell cell name specifier + * @return the named value at that index + */ +#define DT_IRQ(node_id, cell) DT_IRQ_BY_IDX(node_id, 0, cell) + +/** + * @brief Get a node's (only) irq number + * + * Equivalent to DT_IRQ(node_id, irq). This is provided as a convenience + * for the common case where a node generates exactly one interrupt, + * and the IRQ number is in a cell named "irq". + * + * @param node_id node identifier + * @return the interrupt number for the node's only interrupt + */ +#define DT_IRQN(node_id) DT_IRQ(node_id, irq) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-chosen Chosen nodes + * @{ + */ + +/** + * @brief Get a node identifier for a /chosen node property + * This is only valid to call if DT_HAS_CHOSEN_NODE(prop) is 1. + * @param prop lowercase-and-underscores property name for + * the /chosen node + * @return a node identifier for the chosen node property + */ +#define DT_CHOSEN(prop) DT_CAT(DT_CHOSEN_, prop) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-exist Existence checks + * @{ + */ + +/** + * @brief Does a node identifier refer to a usable node? + * + * Example uses: + * + * DT_HAS_NODE(DT_PATH(soc, i2c@12340000)) + * DT_HAS_NODE(DT_ALIAS(an_alias_name)) + * + * Tests whether a node identifier refers to a node which: + * + * - exists in the devicetree, and + * - is enabled (has status property "okay"), and + * - has a matching binding + * + * @param node_id a node identifier + * @return 1 if the node identifier refers to a usable node, + * 0 otherwise. + */ +#define DT_HAS_NODE(node_id) IS_ENABLED(DT_CAT(node_id, _EXISTS)) + +/** + * @brief Does the devicetree have any usable nodes with a compatible? + * + * Test for whether the devicetree has any usable nodes (as determined by + * @ref DT_HAS_NODE()) with a given compatible, i.e. if there is at least one + * "x" for which "DT_HAS_NODE(DT_INST(x, compat))" is 1. + * + * @param compat lowercase-and-underscores version of a compatible + * @return 0 if no nodes of the compatible are available for use, + * 1 if at least one is enabled and has a matching binding + */ +#define DT_HAS_COMPAT(compat) DT_HAS_NODE(DT_INST(0, compat)) + +/** + * @brief Get the number of enabled instances for a given compatible + * @param compat lowercase-and-underscores version of a compatible + * @return Number of enabled instances + */ +#define DT_NUM_INST(compat) UTIL_CAT(DT_N_INST, DT_DASH(compat, NUM)) + +/** + * @brief Test if the devicetree has a /chosen node + * @param prop lowercase-and-underscores devicetree property + * @return 1 if the chosen property exists and refers to a node, + * 0 otherwise + */ +#define DT_HAS_CHOSEN(prop) IS_ENABLED(DT_CHOSEN_##prop##_EXISTS) + +/** + * @brief Does a devicetree node match a compatible? + * + * Example devicetree fragment: + * + * n: node { + * compatible = "vnd,specific-device", "generic-device"; + * } + * + * Example usages which evaluate to 1: + * + * DT_NODE_HAS_COMPAT(DT_NODELABEL(n), vnd_specific_device) + * DT_NODE_HAS_COMPAT(DT_NODELABEL(n), generic_device) + * + * This macro only uses the value of the compatible property. Whether + * or not a particular compatible has a matching binding has no effect + * on its value. + * + * @param node_id node identifier + * @param compat lowercase-and-underscorse compatible value + * @return 1 if the node's compatible property contains compat, + * 0 otherwise. + */ +#define DT_NODE_HAS_COMPAT(node_id, compat) \ + IS_ENABLED(DT_CAT(node_id, _COMPAT_MATCHES_##compat)) + +/** + * @brief Does a devicetree node have a property? + * + * Tests whether a devicetree node has a property defined. This + * tests whether the property is part of the node at all, not whether + * a boolean property is true or not. + * + * @param node_id node identifier + * @param prop lowercase-and-underscores property name + * @return 1 if the node has the property, 0 otherwise. + */ +#define DT_NODE_HAS_PROP(node_id, prop) \ + IS_ENABLED(DT_CAT(node_id, _P_##prop##_EXISTS)) + + +/** + * @brief Does a phandle array have a named cell specifier at an index? + * If this returns 1, then the cell argument to + * DT_PHA_BY_IDX(node_id, pha, idx, cell) is valid. + * If it returns 0, it is an error. + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx index to check + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the specifier at index idx, + * 0 otherwise. + */ +#define DT_PHA_HAS_CELL_AT_IDX(node_id, pha, idx, cell) \ + IS_ENABLED(DT_PROP(node_id, \ + pha##_IDX_##idx##_VAL_##cell##_EXISTS)) + +/** + * @brief Equivalent to DT_PHA_HAS_CELL_AT_IDX(node_id, pha, 0, cell) + * @param node_id node identifier + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell named cell value whose existence to check + * @return 1 if the named ceell exists in the specifier at index 0, + * 0 otherwise. + */ +#define DT_PHA_HAS_CELL(node_id, pha, cell) \ + DT_PHA_HAS_CELL_AT_IDX(node_id, pha, 0, cell) + +/** + * @brief Does an interrupts property have a named cell specifier at an index? + * If this returns 1, then DT_IRQ_BY_IDX(node_id, idx, cell) is valid. + * If it returns 0, it is an error to use that macro. + * @param node_id node identifier + * @param idx index to check + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index idx + * 0 otherwise. + */ +#define DT_IRQ_HAS_CELL_AT_IDX(node_id, idx, cell) \ + IS_ENABLED(DT_CAT(node_id, _IRQ_IDX_##idx##_VAL_##cell##_EXISTS)) + +/** + * @brief Equivalent to DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell) + * @param node_id node identifier + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index 0 + * 0 otherwise. + */ +#define DT_IRQ_HAS_CELL(node_id, cell) DT_IRQ_HAS_CELL_AT_IDX(node_id, 0, cell) + +/** + * @brief Does an interrupts property have a named specifier value at an index? + * If this returns 1, then DT_IRQ_BY_NAME(node_id, name, cell) is valid. + * If it returns 0, it is an error to use that macro. + * @param node_id node identifier + * @param name lowercase-and-underscores interrupt specifier name + * @return 1 if "name" is a valid named specifier + * 0 otherwise. + */ +#define DT_IRQ_HAS_NAME(node_id, name) \ + IS_ENABLED(DT_CAT(node_id, _IRQ_NAME_##name##_VAL_irq_EXISTS)) + +/** + * @} + */ + +/** + * @defgroup devicetree-generic-bus Bus helpers + * @{ + */ + +/** + * @brief Node's bus controller + * + * Get the node identifier of the node's bus controller. This can be + * used with @ref DT_PROP() to get properties of the bus controller. + * + * It is an error to use this with nodes which do not have bus + * controllers. + * + * Example devicetree fragment: + * + * i2c@deadbeef { + * label = "I2C_CTLR"; + * status = "okay"; + * clock-frequency = < 100000 >; + * + * i2c_device: accelerometer@12 { + * ... + * }; + * }; + * + * Example usage: + * + * DT_PROP(DT_BUS(DT_NODELABEL(i2c_device)), clock_frequency) // 100000 + * + * @param node_id node identifier + * @return a node identifier for the node's bus controller + */ +#define DT_BUS(node_id) DT_CAT(node_id, _BUS) + +/** + * @brief Node's bus controller's label property + * @param node_id node identifier + * @return the label property of the node's bus controller DT_BUS(node) + */ +#define DT_BUS_LABEL(node_id) DT_PROP(DT_BUS(node_id), label) + +/** + * @brief Test if a node's bus type is a given type + * + * Example devicetree overlay: + * + * &i2c0 { + * temp: temperature-sensor@76 { + * compatible = "vnd,some-sensor"; + * reg = <0x76>; + * }; + * }; + * + * Example usage, assuming "i2c0" is an I2C bus controller node, and + * therefore "temp" is on an I2C bus: + * + * DT_ON_BUS(DT_NODELABEL(temp), i2c) // 1 + * DT_ON_BUS(DT_NODELABEL(temp), spi) // 0 + * + * @param node_id node identifier + * @param bus a binding's bus type as a C token, lowercased and without quotes + * @return 1 if the node is on a bus of the given type, + * 0 otherwise + */ +#define DT_ON_BUS(node_id, bus) IS_ENABLED(DT_CAT(node_id, _BUS_##bus)) + +/** + * @} + */ + +/** + * @defgroup devicetree-inst Instance-based devicetree APIs + * @{ + */ + +/** + * @brief Node identifier for an instance of a DT_DRV_COMPAT compatible + * @param inst instance number + * @return a node identifier for the node with DT_DRV_COMPAT compatible and + * instance number "inst" + */ +#define DT_DRV_INST(inst) DT_INST(inst, DT_DRV_COMPAT) + +/** + * @brief Get a DT_DRV_COMPAT instance property + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @return a representation of the property's value + */ +#define DT_INST_PROP(inst, prop) DT_PROP(DT_DRV_INST(inst), prop) + +/** + * @brief Get a DT_DRV_COMPAT element value in an array property + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @param idx the index to get + * @return a representation of the idx-th element of the property + */ +#define DT_INST_PROP_BY_IDX(inst, prop, idx) \ + DT_PROP_BY_IDX(DT_DRV_INST(inst), prop, idx) + +/** + * @brief Get a DT_DRV_COMPAT property length + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @return logical length of the property + */ +#define DT_INST_PROP_LEN(inst, prop) DT_PROP_LEN(DT_DRV_INST(inst), prop) + +/** + * @brief Get a DT_DRV_COMPAT instance's "label" property + * @param inst instance number + * @return instance's label property value + */ +#define DT_INST_LABEL(inst) DT_INST_PROP(inst, label) + +/** + * @brief Get a DT_DRV_COMPAT instance's property value from a phandle's node + * @param inst instance number + * @param ph lowercase-and-underscores property of "inst" + * with type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the value of "prop" as described in the DT_PROP() documentation + */ +#define DT_INST_PROP_BY_PHANDLE(inst, ph, prop) \ + DT_INST_PROP_BY_PHANDLE_IDX(inst, ph, 0, prop) + +/** + * @brief Get a DT_DRV_COMPAT instance's property value from a phandle in a + * property. + * @param inst instance number + * @param phs lowercase-and-underscores property with type "phandle", + * "phandles", or "phandle-array" + * @param idx logical index into "phs", which must be zero if "phs" + * has type "phandle" + * @param prop lowercase-and-underscores property of the phandle's node + * @return the value of "prop" as described in the DT_PROP() documentation + */ +#define DT_INST_PROP_BY_PHANDLE_IDX(inst, phs, idx, prop) \ + DT_PROP_BY_PHANDLE_IDX(DT_DRV_INST(inst), phs, idx, prop) + +/** + * @brief Get a DT_DRV_COMPAT instance's phandle-array specifier value at an index + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param idx logical index into the property "pha" + * @param cell binding's cell name within the specifier at index "idx" + * @return the value of the cell inside the specifier at index "idx" + */ +#define DT_INST_PHA_BY_IDX(inst, pha, idx, cell) \ + DT_PHA_BY_IDX(DT_DRV_INST(inst), pha, idx, cell) + +/** + * @brief Get a DT_DRV_COMPAT instance's phandle-array specifier value + * Equivalent to DT_INST_PHA_BY_IDX(inst, pha, 0, cell) + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param cell binding's cell name for the specifier at "pha" index 0 + * @return the cell value + */ +#define DT_INST_PHA(inst, pha, cell) DT_INST_PHA_BY_IDX(inst, pha, 0, cell) + +/** + * @brief Get a DT_DRV_COMPAT instance's value within a phandle-array + * specifier by name + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of a specifier in "pha" + * @param cell binding's cell name for the named specifier + * @return the cell value + */ +#define DT_INST_PHA_BY_NAME(inst, pha, name, cell) \ + DT_PHA_BY_NAME(DT_DRV_INST(inst), pha, name, cell) + +/** + * @brief Get a DT_DRV_COMPAT instance's phandle node identifier from a + * phandle array by name + * @param inst instance number + * @param pha lowercase-and-underscores property with type "phandle-array" + * @param name lowercase-and-underscores name of an element in "pha" + * @return node identifier for the phandle at the element named "name" + */ +#define DT_INST_PHANDLE_BY_NAME(inst, pha, name) \ + DT_PHANDLE_BY_NAME(DT_DRV_INST(inst), pha, name) \ + +/** + * @brief Get a DT_DRV_COMPAT instance's node identifier for a phandle in + * a property. + * @param inst instance number + * @param prop lowercase-and-underscores property name in "inst" + * with type "phandle", "phandles" or "phandle-array" + * @param idx index into "prop" + * @return a node identifier for the phandle at index "idx" in "prop" + */ +#define DT_INST_PHANDLE_BY_IDX(inst, prop, idx) \ + DT_PHANDLE_BY_IDX(DT_DRV_INST(inst), prop, idx) + +/** + * @brief Get a DT_DRV_COMPAT instance's node identifier for a phandle + * property's value + * @param inst instance number + * @param prop lowercase-and-underscores property of "inst" + * with type "phandle" + * @return a node identifier for the node pointed to by "ph" + */ +#define DT_INST_PHANDLE(inst, prop) DT_INST_PHANDLE_BY_IDX(inst, prop, 0) + +/** + * @brief is "idx" a valid register block index on a DT_DRV_COMPAT instance? + * @param inst instance number + * @param idx index to check + * @return 1 if "idx" is a valid register block index, + * 0 otherwise. + */ +#define DT_INST_REG_HAS_IDX(inst, idx) DT_REG_HAS_IDX(DT_DRV_INST(inst), idx) +/** + * @brief Get a DT_DRV_COMPAT instance's idx-th register block's address + * @param inst instance number + * @param idx index of the register whose address to return + * @return address of the instance's idx-th register block + */ +#define DT_INST_REG_ADDR_BY_IDX(inst, idx) DT_REG_ADDR_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get a DT_DRV_COMPAT instance's idx-th register block's size + * @param inst instance number + * @param idx index of the register whose size to return + * @return size of the instance's idx-th register block + */ +#define DT_INST_REG_SIZE_BY_IDX(inst, idx) \ + DT_REG_SIZE_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get a DT_DRV_COMPAT's register block address by name + * @param inst instance number + * @param name lowercase-and-underscores register specifier name + * @return address of the register block with the given name + */ +#define DT_INST_REG_ADDR_BY_NAME(inst, name) \ + DT_REG_ADDR_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Get a DT_DRV_COMPAT's register block size by name + * @param inst instance number + * @param name lowercase-and-underscores register specifier name + * @return size of the register block with the given name + */ +#define DT_INST_REG_SIZE_BY_NAME(inst, name) \ + DT_REG_SIZE_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Get a DT_DRV_COMPAT's (only) register block address + * Equivalent to DT_INST_REG_ADDR_BY_IDX(inst, 0). + * @param inst instance number + * @return instance's register block address + */ +#define DT_INST_REG_ADDR(inst) DT_INST_REG_ADDR_BY_IDX(inst, 0) + +/** + * @brief Get a DT_DRV_COMPAT's (only) register block size + * Equivalent to DT_INST_REG_SIZE_BY_IDX(inst, 0). + * @param inst instance number + * @return instance's register block size + */ +#define DT_INST_REG_SIZE(inst) DT_INST_REG_SIZE_BY_IDX(inst, 0) + +/** + * @brief Get a DT_DRV_COMPAT interrupt specifier value at an index + * (see @ref DT_IRQ_BY_IDX) + * @param inst instance number + * @param idx logical index into the interrupt specifier array + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_INST_IRQ_BY_IDX(inst, idx, cell) \ + DT_IRQ_BY_IDX(DT_DRV_INST(inst), idx, cell) + +/** + * @brief Get a DT_DRV_COMPAT interrupt specifier value by name + * (see @ref DT_IRQ_BY_NAME) + * @param inst instance number + * @param name lowercase-and-underscores interrupt specifier name + * @param cell cell name specifier + * @return the named value at the specifier given by the index + */ +#define DT_INST_IRQ_BY_NAME(inst, name, cell) \ + DT_IRQ_BY_NAME(DT_DRV_INST(inst), name, cell) + +/** + * @brief Get a DT_DRV_COMAPT interrupt specifier's value + * Equivalent to DT_INST_IRQ_BY_IDX(inst, 0, cell). + * @param inst instance number + * @param cell cell name specifier + * @return the named value at that index + */ +#define DT_INST_IRQ(inst, cell) DT_INST_IRQ_BY_IDX(inst, 0, cell) + +/** + * @brief Get a DT_DRV_COMPAT's (only) irq number + * Equivalent to DT_INST_IRQ(inst, irq). + * @param inst instance number + * @return the interrupt number for the node's only interrupt + */ +#define DT_INST_IRQN(inst) DT_INST_IRQ(inst, irq) + +/** + * @brief Get a DT_DRV_COMPAT's bus node's label property + * Equivalent to DT_BUS_LABEL(DT_DRV_INST(inst)) + * @param inst instance number + * @return the label property of the instance's bus controller + */ +#define DT_INST_BUS_LABEL(inst) DT_BUS_LABEL(DT_DRV_INST(inst)) + +/** + * @brief Test if a DT_DRV_COMPAT's bus type is a given type + * This is equivalent to DT_ON_BUS(DT_DRV_INST(inst), bus). + * @param inst instance number + * @param bus a binding's bus type as a C token, lowercased and without quotes + * @return 1 if the given instance is on a bus of the given type, + * 0 otherwise + */ +#define DT_INST_ON_BUS(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus) + +/** + * @brief Test if any node with compatible DT_DRV_COMPAT is on a bus + * + * This is the same as logically ORing together DT_ON_BUS(node, bus) + * for every enabled node which matches compatible DT_DRV_COMPAT. + * + * It can be useful, for instance, when writing device drivers for + * hardware that supports multiple possible bus connections to the + * SoC. + * + * @param bus a binding's bus type as a C token, lowercased and without quotes + */ +#define DT_ANY_INST_ON_BUS(bus) \ + (UTIL_LISTIFY(DT_NUM_INST(DT_DRV_COMPAT), DT_INST_ON_BUS_OR, bus) 0) + +/** + * @brief Does a DT_DRV_COMPAT instance have a property? + * Equivalent to DT_NODE_HAS_PROP(DT_DRV_INST(inst), prop) + * @param inst instance number + * @param prop lowercase-and-underscores property name + * @return 1 if the instance has the property, 0 otherwise. + */ +#define DT_INST_NODE_HAS_PROP(inst, prop) \ + DT_NODE_HAS_PROP(DT_DRV_INST(inst), prop) + +/** + * @brief is index valid for interrupt property on a DT_DRV_COMPAT instance? + * Equivalent to DT_IRQ_HAS_IDX(DT_DRV_INST(inst), idx). + * @param inst instance number + * @param idx logical index into the interrupt specifier array + * @return 1 if the idx is valid for the interrupt property + * 0 otherwise. + */ +#define DT_INST_IRQ_HAS_IDX(inst, idx) DT_IRQ_HAS_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Does a DT_DRV_COMPAT instance have an interrupt named cell specifier? + * Equivalent to DT_IRQ_HAS_CELL_AT_IDX(DT_DRV_INST(inst), idx, cell). + * @param inst instance number + * @param idx index to check + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index idx + * 0 otherwise. + */ +#define DT_INST_IRQ_HAS_CELL_AT_IDX(inst, idx, cell) \ + DT_IRQ_HAS_CELL_AT_IDX(DT_DRV_INST(inst), idx, cell) + +/** + * @brief Does a DT_DRV_COMPAT instance have an interrupt value? + * Equivalent to DT_INST_IRQ_HAS_IDX(DT_DRV_INST(inst), 0, cell). + * @param inst instance number + * @param cell named cell value whose existence to check + * @return 1 if the named cell exists in the interrupt specifier at index 0 + * 0 otherwise. + */ +#define DT_INST_IRQ_HAS_CELL(inst, cell) \ + DT_INST_IRQ_HAS_CELL_AT_IDX(inst, 0, cell) + +/** + * @brief Does a DT_DRV_COMPAT instance have an interrupt value? + * Equivalent to DT_INST_IRQ_HAS_NAME(DT_DRV_INST(inst), name). + * @param inst instance number + * @param name lowercase-and-underscores interrupt specifier name + * @return 1 if "name" is a valid named specifier + */ +#define DT_INST_IRQ_HAS_NAME(inst, name) \ + DT_IRQ_HAS_NAME(DT_DRV_INST(inst), name) + +/** + * @} + */ + +#include +#include +#include +#include + +/** @internal pay no attention to the man behind the curtain! */ +#define DT_PATH_INTERNAL(...) \ + UTIL_CAT(DT_ROOT, MACRO_MAP_CAT(DT_S_PREFIX, __VA_ARGS__)) +/** @internal helper for DT_PATH(): prepends _S_ to a node name */ +#define DT_S_PREFIX(name) _S_##name +/** @internal concatenation helper, sometimes used to force expansion */ +#define DT_CAT(node_id, prop_suffix) node_id##prop_suffix +/** @internal helper for node identifier macros to expand args */ +#define DT_DASH(...) MACRO_MAP_CAT(DT_DASH_PREFIX, __VA_ARGS__) +/** @internal helper for DT_DASH(): prepends _ to a name */ +#define DT_DASH_PREFIX(name) _##name +/** @internal DT_ANY_INST_ON_BUS helper */ +#define DT_INST_ON_BUS_OR(inst, bus) DT_ON_BUS(DT_DRV_INST(inst), bus) || + #endif /* DEVICETREE_H */ diff --git a/include/devicetree/adc.h b/include/devicetree/adc.h new file mode 100644 index 0000000000000..6ed4d8764a358 --- /dev/null +++ b/include/devicetree/adc.h @@ -0,0 +1,198 @@ +/** + * @file + * @brief ADC Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020, Linaro Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_ADC_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_ADC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-adc Devicetree ADC API + * @{ + */ + +/** + * @brief Get IO channels controller "name" (label property) at an index + * + * It's an error if the IO channels controller referenced by the phandle + * in property "io_channels" at index "idx" has no label property. + * + * Example devicetree fragment: + * + * adc1: adc@... { + * label = "ADC_1"; + * }; + * + * adc2: adc@... { + * label = "ADC_2"; + * }; + * + * n: node { + * io-channels = <&adc1 10>, <&adc2 20>; + * }; + * + * Example usage: + * + * DT_IO_CHANNELS_LABEL_BY_IDX(DT_NODELABEL(n), 1) // "ADC_2" + * + * @param node_id node identifier + * @param idx logical index into the property + * @return the label property for the referenced node at index idx + * @see DT_PHANDLE_BY_IDX() + */ +#define DT_IO_CHANNELS_LABEL_BY_IDX(node_id, idx) \ + DT_PROP_BY_PHANDLE_IDX(node_id, io_channels, idx, label) + +/** + * @brief Get IO channels controller "name" (label property) by name + * + * It's an error if the IO channels controller referenced by the phandle + * in property "io_channels" at index "idx" has no label property. + * + * Example devicetree fragment: + * + * adc1: adc@... { + * label = "ADC_1"; + * }; + * + * adc2: adc@... { + * label = "ADC_2"; + * }; + * + * n: node { + * io-channels = <&adc1 10>, <&adc2 20>; + * io-channel-names = "SENSOR", "BANDGAP"; + * }; + * + * Example usage: + * + * DT_IO_CHANNELS_LABEL_BY_NAME(DT_NODELABEL(n), bandgap) // "ADC_2" + * + * @param node_id node identifier + * @param name lowercase-and-underscores "io_channel" name + * @return the label property for the referenced node by name + * @see DT_PHANDLE_BY_NAME() + */ +#define DT_IO_CHANNELS_LABEL_BY_NAME(node_id, name) \ + DT_PROP(DT_PHANDLE_BY_NAME(node_id, io_channels, name), label) + +/** + * @brief Equivalent to DT_IO_CHANNELS_LABEL_BY_IDX(node_id, 0) + * @param node_id node identifier + * @return the label property for the named specifier at index 0 + * @see DT_IO_CHANNELS_LABEL_BY_IDX() + */ +#define DT_IO_CHANNELS_LABEL(node_id) DT_IO_CHANNELS_LABEL_BY_IDX(node_id, 0) + +/** + * @brief Get IO channel's controller "name" at an index + * (see @ref DT_IO_CHANNELS_LABEL_BY_IDX) + * @param inst instance number + * @param idx logical index into the property + * @return the label property for the named specifier at index idx + */ +#define DT_INST_IO_CHANNELS_LABEL_BY_IDX(inst, idx) \ + DT_IO_CHANNELS_LABEL_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get IO channel's controller "name" by name + * (see @ref DT_IO_CHANNELS_LABEL_BY_NAME) + * @param inst instance number + * @param name lowercase-and-underscores "io_channel" name + * @return the label property for the named specifier by name + */ +#define DT_INST_IO_CHANNELS_LABEL_BY_NAME(inst, name) \ + DT_IO_CHANNELS_LABEL_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Equivalent to DT_INST_IO_CHANNELS_LABEL_BY_IDX(inst, 0) + * @param inst instance number + * @return the label property for the named specifier at index 0 + */ +#define DT_INST_IO_CHANNELS_LABEL(inst) DT_INST_IO_CHANNELS_LABEL_BY_IDX(inst, 0) + +/** + * @brief Get IO channels controller 'input' at an index + * + * This macro only works for IO channels controllers that specify a 'input' + * field in the phandle-array specifier. Refer to the specific IO channels + * controller binding if needed. + * + * @param node_id node identifier + * @param idx logical index into the property + * @return the input value for the named specifier at index idx + * @see DT_PHA_BY_IDX() + * @see DT_PHA() + */ +#define DT_IO_CHANNELS_INPUT_BY_IDX(node_id, idx) \ + DT_PHA_BY_IDX(node_id, io_channels, idx, input) + +/** + * @brief Get IO channels controller 'input' by name + * + * This macro only works for IO channels controllers that specify a 'input' + * field in the phandle-array specifier. Refer to the specific IO channels + * controller binding if needed. + * + * @param node_id node identifier + * @param name lowercase-and-underscores "io_channel" name + * @return the input value for the named specifier by name + * @see DT_PHA_BY_NAME() + */ +#define DT_IO_CHANNELS_INPUT_BY_NAME(node_id, name) \ + DT_PHA_BY_NAME(node_id, io_channels, name, input) +/** + * @brief Equivalent to DT_IO_CHANNELS_INPUT_BY_IDX(node_id, 0) + * @param node_id node identifier + * @return the label property for the named specifier at index 0 + * @see DT_IO_CHANNELS_INPUT_BY_IDX() + */ +#define DT_IO_CHANNELS_INPUT(node_id) DT_IO_CHANNELS_INPUT_BY_IDX(node_id, 0) + +/** + * @brief Get IO channel's controller "input" at an index + * (see @ref DT_IO_CHANNELS_INPUT_BY_IDX) + * @param inst instance number + * @param idx logical index into the property + * @return the input value for the named specifier at index idx + */ +#define DT_INST_IO_CHANNELS_INPUT_BY_IDX(inst, idx) \ + DT_IO_CHANNELS_INPUT_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get IO channel's controller "input" by name + * (see @ref DT_IO_CHANNELS_INPUT_BY_NAME) + * @param inst instance number + * @param name lowercase-and-underscores "io_channel" name + * @return the input value for the named specifier at index idx + */ +#define DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, name) \ + DT_IO_CHANNELS_INPUT_BY_NAME(DT_DRV_INST(inst), name) + +/** + * @brief Equivalent to DT_INST_IO_CHANNELS_INPUT(inst, 0) + * @param inst instance number + * @return the input property for the named specifier at index 0 + */ +#define DT_INST_IO_CHANNELS_INPUT(inst) DT_INST_IO_CHANNELS_INPUT_BY_IDX(inst, 0) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_ADC_H_ */ diff --git a/include/devicetree/clocks.h b/include/devicetree/clocks.h new file mode 100644 index 0000000000000..dedd09d6465c3 --- /dev/null +++ b/include/devicetree/clocks.h @@ -0,0 +1,153 @@ +/** + * @file + * @brief Clocks Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020, Linaro Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_CLOCKS_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_CLOCKS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-clocks Devicetree Clocks API + * @{ + */ + +/** + * @brief Get clock controller "name" (label property) at an index + * + * It's an error if the clock controller referenced by the phandle + * in property "clocks" at index "idx" has no label property. + * + * Example devicetree fragment: + * + * clk1: clkctrl@... { + * label = "CLK_1"; + * }; + * + * clk2: clkctrl@... { + * label = "CLK_2"; + * }; + * + * n: node { + * clocks = <&clk1 10 20>, <&clk2 30 40>; + * }; + * + * Example usage: + * + * DT_CLOCKS_LABEL_BY_IDX(DT_NODELABEL(n), 1) // "CLK_2" + * + * @param node_id node identifier + * @param idx logical index into the property + * @return the label property for the referenced node at index idx + * @see DT_PROP_BY_PHANDLE_IDX() + */ +#define DT_CLOCKS_LABEL_BY_IDX(node_id, idx) \ + DT_PROP_BY_PHANDLE_IDX(node_id, clocks, idx, label) + +/** + * @brief Equivalent to DT_CLOCKS_LABEL_BY_IDX(node_id, 0) + * @param node_id node identifier + * @return the label property for the named specifier at index 0 + * @see DT_CLOCKS_LABEL_BY_IDX() + */ +#define DT_CLOCKS_LABEL(node_id) DT_CLOCKS_LABEL_BY_IDX(node_id, 0) + +/** + * @brief Get Clock controller "cell" value at an index + * + * Example devicetree fragment: + * + * clk1: clkctrl@... { + * label = "CLK_1"; + * #clock-cells = < 2 >; + * }; + * + * n: node { + * clocks = < &clk1 10 20 >, < &clk1 30 40 >; + * }; + * + * Bindings fragment for the gpio0 node: + * + * clock-cells: + * - bus + * - bits + * + * Example usage: + * + * DT_CLOCKS_CELL_BY_IDX(DT_NODELABEL(n), bus, 0) // 10 + * DT_CLOCKS_CELL_BY_IDX(DT_NODELABEL(n), bits, 1) // 40 + * + * @param node_id node identifier + * @param cell binding's cell name within the specifier at index "idx" + * @param idx logical index into the property + * @return the value of the cell inside the specifier at index "idx" + * @see DT_PHA_PHANDLE_IDX() + */ +#define DT_CLOCKS_CELL_BY_IDX(node_id, cell, idx) \ + DT_PHA_BY_IDX(node_id, clocks, idx, cell) + +/** + * @brief Equivalent to DT_CLOCKS_CELL_BY_IDX(node_id, cell, 0) + * @param node_id node identifier + * @param cell binding's cell name within the specifier at index 0 + * @return the value of the cell inside the specifier at index 0 + * @see DT_CLOCKS_CELL_BY_IDX() + */ +#define DT_CLOCKS_CELL(node_id, cell) DT_CLOCKS_CELL_BY_IDX(node_id, cell, 0) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "name" at an index + * (see @ref DT_CLOCKS_LABEL_BY_IDX) + * @param inst instance number + * @param idx logical index into the property + * @return the label property for the named specifier at index idx + */ +#define DT_INST_CLOCKS_LABEL_BY_IDX(inst, idx) \ + DT_CLOCKS_LABEL_BY_IDX(DT_DRV_INST(inst), idx) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "name" + * (see @ref DT_CLOCKS_LABEL_BY_IDX) + * @param inst instance number + * @return the label property for the named specifier at index 0 + */ +#define DT_INST_CLOCKS_LABEL(inst) DT_INST_CLOCKS_LABEL_BY_IDX(inst, 0) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "cell" value at an index + * @param inst instance number + * @param cell binding's cell name within the specifier at index "idx" + * @param idx logical index into the property + * @return the value of the cell inside the specifier at index "idx" + */ +#define DT_INST_CLOCKS_CELL_BY_IDX(inst, cell, idx) \ + DT_CLOCKS_CELL_BY_IDX(DT_DRV_INST(inst), cell, idx) + +/** + * @brief Get a DT_DRV_COMPAT clock controller "cell" value at an index 0 + * @param inst instance number + * @param cell binding's cell name within the specifier at index 0 + * @return the value of the cell inside the specifier at index 0 + */ +#define DT_INST_CLOCKS_CELL(inst, cell) \ + DT_INST_CLOCKS_CELL_BY_IDX(inst, cell, 0) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_CLOCKS_H_ */ diff --git a/include/devicetree/gpio.h b/include/devicetree/gpio.h new file mode 100644 index 0000000000000..485c74074556d --- /dev/null +++ b/include/devicetree/gpio.h @@ -0,0 +1,202 @@ +/** + * @file + * @brief GPIO Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020, Linaro Ltd. + * Copyright (c) 2020 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_GPIO_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_GPIO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-gpio Devicetree GPIO API + * @{ + */ + +/** + * @brief Get gpio controller "name" (label property) at an index + * + * It's an error if the GPIO controller referenced by the phandle + * in property "gpio_pha" at index "idx" has no label property. + * + * Example devicetree fragment: + * + * gpio1: gpio@... { + * label = "GPIO_1"; + * }; + * + * gpio2: gpio@... { + * label = "GPIO_2"; + * }; + * + * n: node { + * gpios = <&gpio1 10 20>, <&gpio2 30 40>; + * }; + * + * Example usage: + * + * DT_GPIO_LABEL_BY_IDX(DT_NODELABEL(n), gpios, 1) // "GPIO_2" + * + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the label property for the referenced node at index idx + * @see DT_PHANDLE_BY_IDX() + */ +#define DT_GPIO_LABEL_BY_IDX(node_id, gpio_pha, idx) \ + DT_PROP_BY_PHANDLE_IDX(node_id, gpio_pha, idx, label) + +/** + * @brief Equivalent to DT_GPIO_LABEL_BY_IDX(node_id, gpio_pha, 0) + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the label property for the named specifier at index 0 + * @see DT_GPIO_LABEL_BY_IDX() + */ +#define DT_GPIO_LABEL(node_id, gpio_pha) \ + DT_GPIO_LABEL_BY_IDX(node_id, gpio_pha, 0) + +/** + * @brief Get gpio controller 'pin' at an index + * + * This macro only works for GPIO controllers that specify a 'pin' + * field in the phandle-array specifier. Refer to the specific GPIO + * controller binding if needed. + * + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the pin value for the named specifier at index idx + * @see DT_PHA_BY_IDX() + * @see DT_PHA() + */ +#define DT_GPIO_PIN_BY_IDX(node_id, gpio_pha, idx) \ + DT_PHA_BY_IDX(node_id, gpio_pha, idx, pin) + +/** + * @brief Equivalent to DT_GPIO_PIN_BY_IDX(node_id, gpio_pha, 0) + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the pin value for the named specifier at index idx + * @see DT_GPIO_PIN_BY_IDX() + */ +#define DT_GPIO_PIN(node_id, gpio_pha) \ + DT_GPIO_PIN_BY_IDX(node_id, gpio_pha, 0) + +/** + * @brief Get gpio controller 'flags' at an index + * + * This macro only works for GPIO controllers that specify a 'flags' + * field in the phandle-array specifier. Refer to the specific GPIO + * controller binding if needed. + * + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the flags value for the named specifier at index idx + * @see DT_PHA_BY_IDX() + * @see DT_PHA() + */ +#define DT_GPIO_FLAGS_BY_IDX(node_id, gpio_pha, idx) \ + DT_PHA_BY_IDX(node_id, gpio_pha, idx, flags) + +/** + * @brief Equivalent to DT_GPIO_FLAGS_BY_IDX(node_id, gpio_pha, 0) + * @param node_id node identifier + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the flags value for the named specifier at index idx + * @see DT_GPIO_FLAGS_BY_IDX() + */ +#define DT_GPIO_FLAGS(node_id, gpio_pha) \ + DT_GPIO_FLAGS_BY_IDX(node_id, gpio_pha, 0) + +/** + * @brief Get gpio controller "name" at an index (see @ref DT_GPIO_LABEL_BY_IDX) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the label property for the named specifier at index idx + */ +#define DT_INST_GPIO_LABEL_BY_IDX(inst, gpio_pha, idx) \ + DT_GPIO_LABEL_BY_IDX(DT_DRV_INST(inst), gpio_pha, idx) + +/** + * @brief Equivalent to DT_INST_GPIO_LABEL_BY_IDX(inst, gpio_pha, 0) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the label property for the named specifier at index 0 + */ +#define DT_INST_GPIO_LABEL(inst, gpio_pha) \ + DT_INST_GPIO_LABEL_BY_IDX(inst, gpio_pha, 0) + +/** + * @brief Get gpio controller "pin" at an index (see @ref DT_GPIO_PIN_BY_IDX) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return the pin value for the named specifier at index idx + */ +#define DT_INST_GPIO_PIN_BY_IDX(inst, gpio_pha, idx) \ + DT_GPIO_PIN_BY_IDX(DT_DRV_INST(inst), gpio_pha, idx) + +/** + * @brief Equivalent to DT_INST_GPIO_PIN_BY_IDX(inst, gpio_pha, 0) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the pin value for the named specifier at index 0 + * @see DT_INST_GPIO_PIN_BY_IDX() + */ +#define DT_INST_GPIO_PIN(inst, gpio_pha) \ + DT_INST_GPIO_PIN_BY_IDX(inst, gpio_pha, 0) + +/** + * @brief Get a devicetree property value (see @ref DT_GPIO_FLAGS_BY_IDX) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @param idx logical index into the property + * @return a representation of the property's value + */ +#define DT_INST_GPIO_FLAGS_BY_IDX(inst, gpio_pha, idx) \ + DT_GPIO_FLAGS_BY_IDX(DT_DRV_INST(inst), gpio_pha, idx) + +/** + * @brief Equivalent to DT_INST_GPIO_FLAGS_BY_IDX(inst, gpio_pha, 0) + * @param inst instance number + * @param gpio_pha lowercase-and-underscores GPIO property with + * type "phandle-array" + * @return the flags value for the named specifier at index 0 + * @see DT_INST_GPIO_FLAGS_BY_IDX() + */ +#define DT_INST_GPIO_FLAGS(inst, gpio_pha) \ + DT_INST_GPIO_FLAGS_BY_IDX(inst, gpio_pha, 0) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_GPIO_H_ */ diff --git a/include/devicetree/spi.h b/include/devicetree/spi.h new file mode 100644 index 0000000000000..9632f44dd32bc --- /dev/null +++ b/include/devicetree/spi.h @@ -0,0 +1,121 @@ +/** + * @file + * @brief SPI Devicetree macro public API header file. + */ + +/* + * Copyright (c) 2020 Nordic Semiconductor + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DEVICETREE_SPI_H_ +#define ZEPHYR_INCLUDE_DEVICETREE_SPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup devicetree-spi Devicetree SPI API + * @{ + */ + +/** + * @brief Does a SPI controller have chip select GPIOs configured? + * The commonly used "cs-gpios" property is used for this test. + * @param spi node identifier for a SPI bus controller + * @return 1 if it has a cs-gpios property, 0 otherwise + */ +#define DT_SPI_HAS_CS(spi) DT_NODE_HAS_PROP(spi, cs_gpios) + +/** + * @brief The number of chip select GPIOs in a SPI controller + * @param spi node identifier for a SPI bus controller + * @return The length of its cs-gpios, or 0 if it doesn't have one + */ +#define DT_SPI_NUM_CS(spi) \ + COND_CODE_1(DT_SPI_HAS_CS(spi), \ + (DT_PROP_LEN(spi, cs_gpios)), (0)) + +/** + * @brief Does a SPI device have a chip select line in DT? + * @param spi_dev node identifier for a SPI device + * @return 1 if the SPI device's bus DT_BUS(spi_dev) has a CS + * pin at index DT_REG_ADDR(spi_dev), 0 otherwise + */ +#define DT_SPI_DEV_HAS_CS(spi_dev) DT_SPI_HAS_CS(DT_BUS(spi_dev)) + +/** + * @brief Get GPIO controller name for a SPI device's chip select + * DT_SPI_DEV_HAS_CS(spi_dev) must expand to 1. + * @brief spi_dev a SPI device node identifier + * @return label property of spi_dev's chip select GPIO controller + */ +#define DT_SPI_DEV_CS_GPIO_LABEL(spi_dev) \ + DT_GPIO_LABEL_BY_IDX(DT_BUS(spi_dev), cs_gpios, DT_REG_ADDR(spi_dev)) + +/** + * @brief Get GPIO specifier 'pin' value for a SPI device's chip select + * It's an error if the GPIO specifier for spi_dev's entry in its + * bus node's cs-gpios property has no 'pin' value. + * @brief spi_dev a SPI device node identifier + * @return pin number of spi_dev's chip select GPIO + */ +#define DT_SPI_DEV_CS_GPIO_PIN(spi_dev) \ + DT_GPIO_PIN_BY_IDX(DT_BUS(spi_dev), cs_gpios, DT_REG_ADDR(spi_dev)) + +/** + * @brief Get GPIO specifier 'flags' value for a SPI device's chip select + * It's an error if the GPIO specifier for spi_dev's entry in its + * bus node's cs-gpios property has no 'flags' value. + * @brief spi_dev a SPI device node identifier + * @return flags value of spi_dev's chip select GPIO specifier + */ +#define DT_SPI_DEV_CS_GPIO_FLAGS(spi_dev) \ + DT_GPIO_FLAGS_BY_IDX(DT_BUS(spi_dev), cs_gpios, DT_REG_ADDR(spi_dev)) + +/** + * @brief Equivalent to DT_SPI_DEV_HAS_CS(DT_DRV_INST(inst)) + * @param inst instance number + * @return 1 if the instance's bus has a CS pin at index + * DT_INST_REG_ADDR(inst), 0 otherwise + */ +#define DT_INST_SPI_DEV_HAS_CS(inst) DT_SPI_DEV_HAS_CS(DT_DRV_INST(inst)) + +/** + * @brief Get GPIO controller name for a SPI device instance + * This is equivalent to DT_SPI_DEV_CS_GPIO_LABEL(DT_DRV_INST(inst)). + * @brief inst instance number + * @return label property of the instance's chip select GPIO controller + */ +#define DT_INST_SPI_DEV_CS_GPIO_LABEL(inst) \ + DT_SPI_DEV_CS_GPIO_LABEL(DT_DRV_INST(inst)) + +/** + * @brief Get GPIO specifier "pin" value for a SPI device instance + * This is equivalent to DT_SPI_DEV_CS_GPIO_PIN(DT_DRV_INST(inst)). + * @brief inst a SPI device instance number + * @return pin number of the instance's chip select GPIO + */ +#define DT_INST_SPI_DEV_CS_GPIO_PIN(inst) \ + DT_SPI_DEV_CS_GPIO_PIN(DT_DRV_INST(inst)) + +/** + * @brief Get GPIO specifier "flags" value for a SPI device instance + * This is equivalent to DT_SPI_DEV_CS_GPIO_FLAGS(DT_DRV_INST(inst)). + * @brief inst a SPI device instance number + * @return flags value of the instance's chip select GPIO specifier + */ +#define DT_INST_SPI_DEV_CS_GPIO_FLAGS(inst) \ + DT_SPI_DEV_CS_GPIO_FLAGS(DT_DRV_INST(inst)) + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DEVICETREE_SPI_H_ */ diff --git a/samples/basic/blinky/README.rst b/samples/basic/blinky/README.rst index 9adb3c692147d..674ab1099eae1 100644 --- a/samples/basic/blinky/README.rst +++ b/samples/basic/blinky/README.rst @@ -6,29 +6,31 @@ Blinky sample Overview ******** -The Blinky example shows how to configure GPIO pins as outputs which can also be -used to drive LEDs on the hardware usually delivered as "User LEDs" on many of -the supported boards in Zephyr. +Blinky is a simple application which blinks an LED forever. + +The source code shows how to configure GPIO pins as outputs, then turn them on +and off. .. _blinky-sample-requirements: Requirements ************ -The demo assumes that an LED is connected to one of GPIO lines. The -sample code is configured to work on boards that have defined the ``led0`` -alias in their :ref:`board's devicetree description file -`, :file:`.dts`. -Doing so will generate these variables: +The board must have an LED connected via a GPIO pin. These are called "User +LEDs" on many of Zephyr's :ref:`boards`. The LED must be configured using the +``led0`` :ref:`dt-guide` alias in the :ref:`BOARD.dts file +`. + +You will see this error if you try to build Blinky for an unsupported board: + +.. code-block:: none -- ``DT_ALIAS_LED0_GPIOS_CONTROLLER`` -- ``DT_ALIAS_LED0_GPIOS_PIN`` + Unsupported board: led0 devicetree alias is not defined Building and Running ******************** -This sample does not output anything to the console. It can be built and -flashed to a board as follows: +Build and flash Blinky as follows, changing ``reel_board`` for your board: .. zephyr-app-commands:: :zephyr-app: samples/basic/blinky @@ -36,5 +38,4 @@ flashed to a board as follows: :goals: build flash :compact: -After flashing the image to the board, the user LED on the board should start to -blink. +After flashing, the LED starts to blink. Blinky does not print to the console. diff --git a/samples/basic/blinky/src/main.c b/samples/basic/blinky/src/main.c index 21f333091946d..198ad58ddf439 100644 --- a/samples/basic/blinky/src/main.c +++ b/samples/basic/blinky/src/main.c @@ -6,27 +6,32 @@ #include #include +#include #include /* 1000 msec = 1 sec */ #define SLEEP_TIME_MS 1000 -#ifndef DT_ALIAS_LED0_GPIOS_FLAGS -#define FLAGS 0 -#else -#define FLAGS DT_ALIAS_LED0_GPIOS_FLAGS -#endif +/* The devicetree node identifier for the "led0" alias. */ +#define LED0_NODE DT_ALIAS(led0) -/* Make sure the board's devicetree declares led0 in its /aliases. */ -#ifdef DT_ALIAS_LED0_GPIOS_CONTROLLER -#define LED0 DT_ALIAS_LED0_GPIOS_CONTROLLER -#define PIN DT_ALIAS_LED0_GPIOS_PIN +#if DT_HAS_NODE(LED0_NODE) +#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios) +#define PIN DT_GPIO_PIN(LED0_NODE, gpios) +#if DT_PHA_HAS_CELL(LED0_NODE, gpios, flags) +#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios) +#endif #else +/* A build error here means your board isn't set up to blink an LED. */ #error "Unsupported board: led0 devicetree alias is not defined" #define LED0 "" #define PIN 0 #endif +#ifndef FLAGS +#define FLAGS 0 +#endif + void main(void) { struct device *dev; diff --git a/samples/bluetooth/hci_spi/README.rst b/samples/bluetooth/hci_spi/README.rst index e7c39b718a07e..e82b3c4bcd5b6 100644 --- a/samples/bluetooth/hci_spi/README.rst +++ b/samples/bluetooth/hci_spi/README.rst @@ -21,8 +21,8 @@ In order to use this application, you need a board with a Bluetooth controller and SPI slave drivers, and a spare GPIO to use as an interrupt line to the SPI master. -You then need to ensure that your :ref:`devicetree` settings provide a -definition for the slave HCI SPI device:: +You then need to ensure that your :ref:`devicetree ` +settings provide a definition for the slave HCI SPI device:: bt-hci@0 { compatible = "zephyr,bt-hci-spi-slave"; diff --git a/samples/drivers/led_ws2812/README.rst b/samples/drivers/led_ws2812/README.rst index f2e81e3c0707d..c15bcedd6ae80 100644 --- a/samples/drivers/led_ws2812/README.rst +++ b/samples/drivers/led_ws2812/README.rst @@ -48,7 +48,7 @@ To make sure the sample is set up properly for building, you must: which case it will be :option:`CONFIG_WS2812_STRIP_GPIO`. - create a ``led-strip`` :ref:`devicetree alias `, which - refers to a node in your :ref:`devicetree ` with a + refers to a node in your :ref:`devicetree ` with a ``worldsemi,ws2812-spi`` or ``worldsemi,ws2812-gpio`` compatible. The node must be properly configured for the driver backend (SPI or GPIO) and daisy chain length (number of WS2812 chips). diff --git a/samples/subsys/fs/littlefs/README.rst b/samples/subsys/fs/littlefs/README.rst index cefc26656a861..9e79263e41354 100644 --- a/samples/subsys/fs/littlefs/README.rst +++ b/samples/subsys/fs/littlefs/README.rst @@ -18,7 +18,7 @@ Requirements ************ The partition labeled "storage" will be used for the file system; see -:ref:`flash_partitions`. If that area does not already have a +:ref:`legacy_flash_partitions`. If that area does not already have a compatible littlefs file system its contents will be replaced by an empty file system. You will see diagnostics like this:: diff --git a/samples/subsys/usb/dfu/README.rst b/samples/subsys/usb/dfu/README.rst index 88cf46dcec345..ba44ca6d5cb7c 100644 --- a/samples/subsys/usb/dfu/README.rst +++ b/samples/subsys/usb/dfu/README.rst @@ -14,7 +14,7 @@ Requirements This project requires an USB device driver. Currently, the USB DFU class provided by the Zephyr project depends on DFU image manager and -partition layout. Refer to :ref:`flash_partitions` for details about +partition layout. Refer to :ref:`legacy_flash_partitions` for details about partition layout. You SoC must run MCUboot as the stage 1 bootloader. This sample is built as an application for the MCUboot bootloader. diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index fd943b7a2c76d..93ae36344ef0b 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -120,6 +120,12 @@ class EDT: ... }; + chosen_nodes: + A collections.OrderedDict that maps the properties defined on the + devicetree's /chosen node to their values. 'chosen' is indexed by + property name (a string), and values are converted to Node objects. + Note that properties of the /chosen node which can't be converted + to a Node are not included in the value. dts_path: The .dts path passed to __init__() @@ -179,22 +185,32 @@ def get_node(self, path): except DTError as e: _err(e) - def chosen_node(self, name): - """ - Returns the Node pointed at by the property named 'name' in /chosen, or - None if the property is missing - """ + @property + def chosen_nodes(self): + ret = OrderedDict() + try: chosen = self._dt.get_node("/chosen") except DTError: - # No /chosen node - return None + return ret - if name not in chosen.props: - return None + for name, prop in chosen.props.items(): + try: + node = prop.to_path() + except DTError: + # DTS value is not phandle or string, or path doesn't exist + continue + + ret[name] = self._node2enode[node] + + return ret - # to_path() checks that the node exists - return self._node2enode[chosen.props[name].to_path()] + def chosen_node(self, name): + """ + Returns the Node pointed at by the property named 'name' in /chosen, or + None if the property is missing + """ + return self.chosen_nodes.get(name) @property def dts_source(self): @@ -1245,8 +1261,14 @@ def _init_regs(self): .format(address_cells, size_cells)): reg = Register() reg.node = self - reg.addr = _translate(to_num(raw_reg[:4*address_cells]), node) - reg.size = to_num(raw_reg[4*address_cells:]) + if address_cells == 0: + reg.addr = None + else: + reg.addr = _translate(to_num(raw_reg[:4*address_cells]), node) + if size_cells == 0: + reg.size = None + else: + reg.size = to_num(raw_reg[4*address_cells:]) if size_cells != 0 and reg.size == 0: _err("zero-sized 'reg' in {!r} seems meaningless (maybe you " "want a size of one or #size-cells = 0 instead)" @@ -1395,8 +1417,8 @@ class Register: there is no 'reg-names' property addr: - The starting address of the register, in the parent address space. Any - 'ranges' properties are taken into account. + The starting address of the register, in the parent address space, or None + if #address-cells is zero. Any 'ranges' properties are taken into account. size: The length of the register in bytes @@ -1406,8 +1428,10 @@ def __repr__(self): if self.name is not None: fields.append("name: " + self.name) - fields.append("addr: " + hex(self.addr)) - fields.append("size: " + hex(self.size)) + if self.addr is not None: + fields.append("addr: " + hex(self.addr)) + if self.size is not None: + fields.append("size: " + hex(self.size)) return "".format(", ".join(fields)) diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py index 7ed55ab3bf12e..ac46b18351f3d 100755 --- a/scripts/dts/gen_defines.py +++ b/scripts/dts/gen_defines.py @@ -1,17 +1,17 @@ #!/usr/bin/env python3 -# Copyright (c) 2019 Nordic Semiconductor ASA +# Copyright (c) 2019 - 2020 Nordic Semiconductor ASA # Copyright (c) 2019 Linaro Limited # SPDX-License-Identifier: BSD-3-Clause -# This script uses edtlib to generate a header file and a .conf file (both -# containing the same values) from a devicetree (.dts) file. Information from -# binding files in YAML format is used as well. +# This script uses edtlib to generate a header file from a devicetree +# (.dts) file. Information from binding files in YAML format is used +# as well. # # Bindings are files that describe devicetree nodes. Devicetree nodes are # usually mapped to bindings via their 'compatible = "..."' property. # -# See the docstring/comments at the top of edtlib.py for more information. +# See Zephyr's Devicetree user guide for details. # # Note: Do not access private (_-prefixed) identifiers from edtlib here (and # also note that edtlib is not meant to expose the dtlib API directly). @@ -21,15 +21,13 @@ import argparse import os import pathlib +import re import sys import edtlib - def main(): - global conf_file global header_file - global flash_area_num args = parse_args() @@ -45,46 +43,28 @@ def main(): with open(args.dts_out, "w", encoding="utf-8") as f: print(edt.dts_source, file=f) - conf_file = open(args.conf_out, "w", encoding="utf-8") - header_file = open(args.header_out, "w", encoding="utf-8") - flash_area_num = 0 - - write_top_comment(edt) - - for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): - write_node_comment(node) - - # Flash partition nodes are handled as a special case. It - # would be nicer if we had bindings that would let us - # avoid that, but this will do for now. - if node.name.startswith("partition@"): - write_flash_partition(node, flash_area_num) - flash_area_num += 1 - - if node.enabled and node.matching_compat: - write_regs(node) - write_irqs(node) - write_props(node) - write_clocks(node) - write_spi_dev(node) - write_bus(node) - write_existence_flags(node) + with open(args.header_out, "w", encoding="utf-8") as header_file: + write_top_comment(edt) - out_comment("Compatibles appearing on enabled nodes") - for compat in sorted(edt.compat2enabled): - #define DT_COMPAT_ 1 - out(f"COMPAT_{str2ident(compat)}", 1) + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + node.z_path_id = "N_" + "_".join( + f"S_{str2ident(name)}" for name in node.path[1:].split("/")) + write_node_comment(node) - # Definitions derived from /chosen nodes - write_addr_size(edt, "zephyr,ccm", "CCM") - write_addr_size(edt, "zephyr,dtcm", "DTCM") - write_addr_size(edt, "zephyr,ipc_shm", "IPC_SHM") - write_flash(edt) + if not node.enabled: + out_comment("No macros: node is disabled") + continue + if not node.matching_compat: + out_comment("No macros: node has no matching binding") + continue - conf_file.close() - header_file.close() + write_idents_and_existence(node) + write_bus(node) + write_special_props(node) + write_vanilla_props(node) - print(f"Devicetree header saved to '{args.header_out}'") + write_chosen(edt) + write_inst_num(edt) def parse_args(): @@ -100,8 +80,6 @@ def parse_args(): "we allow multiple") parser.add_argument("--header-out", required=True, help="path to write header to") - parser.add_argument("--conf-out", required=True, - help="path to write configuration file to") parser.add_argument("--dts-out", required=True, help="path to write merged DTS source code to (e.g. " "as a debugging aid)") @@ -133,8 +111,7 @@ def write_top_comment(edt): s += """ Definitions derived from these nodes in dependency order are next, -followed by tree-wide information (active compatibles, chosen nodes, -etc.). +followed by /chosen nodes. """ out_comment(s, blank_before=False) @@ -147,13 +124,15 @@ def write_node_comment(node): Devicetree node: {node.path} """ + if node.matching_compat: s += f""" Binding (compatible = {node.matching_compat}): {relativize(node.binding_path)} """ - else: - s += "\nNo matching binding.\n" + s += f""" +Node's path identifier in this file: {node.z_path_id} +""" s += f"\nDependency Ordinal: {node.dep_ordinal}\n" @@ -174,9 +153,6 @@ def write_node_comment(node): node.description.splitlines()) + \ "\n" - if not node.enabled: - s += "\nNode is disabled.\n" - out_comment(s) @@ -196,313 +172,100 @@ def relativize(path): return path -def write_regs(node): - # Writes address/size output for the registers in the node's 'reg' property - - def write_reg(reg, base_ident, val): - # Drop '_0' from the identifier if there's a single register, for - # backwards compatibility - if len(reg.node.regs) > 1: - ident = f"{base_ident}_{reg.node.regs.index(reg)}" - else: - ident = base_ident - - out_node(node, ident, val, - # Name alias from 'reg-names = ...' - f"{str2ident(reg.name)}_{base_ident}" if reg.name else None) +def write_idents_and_existence(node): + # Writes macros related to the node's aliases, labels, etc., + # as well as existence flags. - for reg in node.regs: - write_reg(reg, "BASE_ADDRESS", hex(reg.addr)) - if reg.size: - write_reg(reg, "SIZE", reg.size) - - -def write_props(node): - # Writes any properties defined in the "properties" section of the binding - # for the node - - for prop in node.props.values(): - if not should_write(prop): + # Aliases + idents = [f"N_ALIAS_{str2ident(alias)}" for alias in node.aliases] + # Instances + for compat in node.compats: + if not node.enabled: continue + instance_no = node.edt.compat2enabled[compat].index(node) + idents.append(f"N_INST_{instance_no}_{str2ident(compat)}") + # Node labels + idents.extend(f"N_NODELABEL_{str2ident(label)}" for label in node.labels) - if prop.description is not None: - out_comment(prop.description, blank_before=False) - - ident = str2ident(prop.name) - - if prop.type == "boolean": - out_node(node, ident, 1 if prop.val else 0) - elif prop.type == "string": - out_node_s(node, ident, prop.val) - elif prop.type == "int": - out_node(node, ident, prop.val) - elif prop.type == "array": - for i, val in enumerate(prop.val): - out_node(node, f"{ident}_{i}", val) - out_node_init(node, ident, prop.val) - elif prop.type == "string-array": - for i, val in enumerate(prop.val): - out_node_s(node, f"{ident}_{i}", val) - elif prop.type == "uint8-array": - out_node_init(node, ident, - [f"0x{b:02x}" for b in prop.val]) - else: # prop.type == "phandle-array" - write_phandle_val_list(prop) - - # Generate DT_..._ENUM if there's an 'enum:' key in the binding - if prop.enum_index is not None: - out_node(node, ident + "_ENUM", prop.enum_index) - - -def should_write(prop): - # write_props() helper. Returns True if output should be generated for - # 'prop'. - - # Skip #size-cell and other property starting with #. Also skip mapping - # properties like 'gpio-map'. - if prop.name[0] == "#" or prop.name.endswith("-map"): - return False - - # See write_clocks() - if prop.name == "clocks": - return False - - # For these, Property.val becomes an edtlib.Node, a list of edtlib.Nodes, - # or None. Nothing is generated for them at the moment. - if prop.type in {"phandle", "phandles", "path", "compound"}: - return False - - # Skip properties that we handle elsewhere - if prop.name in { - "reg", "compatible", "status", "interrupts", - "interrupt-controller", "gpio-controller" - }: - return False + out_comment("Existence and alternate IDs:") + out_dt_define(node.z_path_id + "_EXISTS", 1) - return True + # Only determine maxlen if we have any idents + if idents: + maxlen = max(len("DT_" + ident) for ident in idents) + for ident in idents: + out_dt_define(ident, "DT_" + node.z_path_id, width=maxlen) def write_bus(node): - # Generate bus-related #defines + # Macros about the node's bus controller, if there is one - if not node.bus_node: + bus = node.bus_node + if not bus: return - if node.bus_node.label is None: - err(f"missing 'label' property on bus node {node.bus_node!r}") - - # #define DT__BUS_NAME - out_node_s(node, "BUS_NAME", str2ident(node.bus_node.label)) + if not bus.label: + err(f"missing 'label' property on bus node {bus!r}") - for compat in node.compats: - # #define DT__BUS_ 1 - out(f"{str2ident(compat)}_BUS_{str2ident(node.on_bus)}", 1) - - -def write_existence_flags(node): - # Generate #defines of the form - # - # #define DT_INST__ 1 - # - # for enabled nodes. These are flags for which devices exist. - - for compat in node.compats: - instance_no = node.edt.compat2enabled[compat].index(node) - out(f"INST_{instance_no}_{str2ident(compat)}", 1) + out_comment(f"Bus info (controller: '{bus.path}', type: '{node.on_bus}')") + out_dt_define(f"{node.z_path_id}_BUS_{str2ident(node.on_bus)}", 1) + out_dt_define(f"{node.z_path_id}_BUS", f"DT_{bus.z_path_id}") -def node_ident(node): - # Returns an identifier for 'node'. Used e.g. when building macro names. +def write_special_props(node): + # Writes required macros for special case properties, when the + # data cannot otherwise be obtained from write_vanilla_props() + # results - # TODO: Handle PWM on STM - # TODO: Better document the rules of how we generate things + out_comment("Special property macros:") - ident = "" + # Macros that are special to the devicetree specification + write_regs(node) + write_interrupts(node) + write_compatibles(node) - if node.bus_node: - ident += "{}_{:X}_".format( - str2ident(node.bus_node.matching_compat), node.bus_node.unit_addr) - ident += f"{str2ident(node.matching_compat)}_" - - if node.unit_addr is not None: - ident += f"{node.unit_addr:X}" - elif node.parent.unit_addr is not None: - ident += f"{node.parent.unit_addr:X}_{str2ident(node.name)}" - else: - # This is a bit of a hack - ident += str2ident(node.name) - - return ident - - -def node_aliases(node): - # Returns a list of aliases for 'node', used e.g. when building macro names - - return node_path_aliases(node) + node_instance_aliases(node) - - -def node_path_aliases(node): - # Returns a list of aliases for 'node', based on the aliases registered for - # it in the /aliases node. Used e.g. when building macro names. - - if node.matching_compat is None: - return [] - - compat_s = str2ident(node.matching_compat) - - aliases = [] - for alias in node.aliases: - aliases.append(f"ALIAS_{str2ident(alias)}") - # TODO: See if we can remove or deprecate this form - aliases.append(f"{compat_s}_{str2ident(alias)}") - - return aliases - - -def node_instance_aliases(node): - # Returns a list of aliases for 'node', based on the compatible string and - # the instance number (each node with a particular compatible gets its own - # instance number, starting from zero). +def write_regs(node): + # reg property: edtlib knows the right #address-cells and + # #size-cells, and can therefore pack the register base addresses + # and sizes correctly + + idx_vals = [] + name_vals = [] + path_id = node.z_path_id + + if node.regs is not None: + idx_vals.append((f"{path_id}_REG_NUM", len(node.regs))) + + for i, reg in enumerate(node.regs): + if reg.addr is not None: + idx_macro = f"{path_id}_REG_IDX_{i}_VAL_ADDRESS" + idx_vals.append((idx_macro, + f"{reg.addr} /* {hex(reg.addr)} */")) + if reg.name: + name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_ADDRESS" + name_vals.append((name_macro, f"DT_{idx_macro}")) + + if reg.size is not None: + idx_macro = f"{path_id}_REG_IDX_{i}_VAL_SIZE" + idx_vals.append((idx_macro, + f"{reg.size} /* {hex(reg.size)} */")) + if reg.name: + name_macro = f"{path_id}_REG_NAME_{reg.name}_VAL_SIZE" + name_vals.append((name_macro, f"DT_{idx_macro}")) + + for macro, val in idx_vals: + out_dt_define(macro, val) + for macro, val in name_vals: + out_dt_define(macro, val) + +def write_interrupts(node): + # interrupts property: we have some hard-coded logic for interrupt + # mapping here. # - # This is a list since a node can have multiple 'compatible' strings, each - # with their own instance number. - - res = [] - for compat in node.compats: - instance_no = node.edt.compat2enabled[compat].index(node) - res.append(f"INST_{instance_no}_{str2ident(compat)}") - return res - - -def write_addr_size(edt, prop_name, prefix): - # Writes _BASE_ADDRESS and _SIZE for the node pointed at by - # the /chosen property named 'prop_name', if it exists - - node = edt.chosen_node(prop_name) - if not node: - return - - if not node.regs: - err("missing 'reg' property in node pointed at by " - f"/chosen/{prop_name} ({node!r})") - - out_comment(f"/chosen/{prop_name} ({node.path})") - out(f"{prefix}_BASE_ADDRESS", hex(node.regs[0].addr)) - out(f"{prefix}_SIZE", node.regs[0].size//1024) - - -def write_flash(edt): - # Writes chosen and tree-wide flash-related output - - write_flash_node(edt) - write_code_partition(edt) - - if flash_area_num != 0: - out_comment("Number of flash partitions") - out("FLASH_AREA_NUM", flash_area_num) - - -def write_flash_node(edt): - # Writes output for the top-level flash node pointed at by - # zephyr,flash in /chosen - - node = edt.chosen_node("zephyr,flash") - - out_comment(f"/chosen/zephyr,flash ({node.path if node else 'missing'})") - - if not node: - # No flash node. Write dummy values. - out("FLASH_BASE_ADDRESS", 0) - out("FLASH_SIZE", 0) - return - - if len(node.regs) != 1: - err("expected zephyr,flash to have a single register, has " - f"{len(node.regs)}") - - if node.on_bus == "spi" and len(node.bus_node.regs) == 2: - reg = node.bus_node.regs[1] # QSPI flash - else: - reg = node.regs[0] - - out("FLASH_BASE_ADDRESS", hex(reg.addr)) - if reg.size: - out("FLASH_SIZE", reg.size//1024) - - if "erase-block-size" in node.props: - out("FLASH_ERASE_BLOCK_SIZE", node.props["erase-block-size"].val) - - if "write-block-size" in node.props: - out("FLASH_WRITE_BLOCK_SIZE", node.props["write-block-size"].val) - - -def write_code_partition(edt): - # Writes output for the node pointed at by zephyr,code-partition in /chosen - - node = edt.chosen_node("zephyr,code-partition") - - out_comment("/chosen/zephyr,code-partition " - f"({node.path if node else 'missing'})") - - if not node: - # No code partition. Write dummy values. - out("CODE_PARTITION_OFFSET", 0) - out("CODE_PARTITION_SIZE", 0) - return - - if not node.regs: - err(f"missing 'regs' property on {node!r}") - - out("CODE_PARTITION_OFFSET", node.regs[0].addr) - out("CODE_PARTITION_SIZE", node.regs[0].size) - - -def write_flash_partition(partition_node, index): - if partition_node.label is None: - err(f"missing 'label' property on {partition_node!r}") - - # Generate label-based identifiers - write_flash_partition_prefix( - "FLASH_AREA_" + str2ident(partition_node.label), partition_node, index) - - # Generate index-based identifiers - write_flash_partition_prefix(f"FLASH_AREA_{index}", partition_node, index) - - -def write_flash_partition_prefix(prefix, partition_node, index): - # write_flash_partition() helper. Generates identifiers starting with - # 'prefix'. - - out(f"{prefix}_ID", index) - - out(f"{prefix}_READ_ONLY", 1 if partition_node.read_only else 0) - - for i, reg in enumerate(partition_node.regs): - # Also add aliases that point to the first sector (TODO: get rid of the - # aliases?) - out(f"{prefix}_OFFSET_{i}", reg.addr, - aliases=[f"{prefix}_OFFSET"] if i == 0 else []) - out(f"{prefix}_SIZE_{i}", reg.size, - aliases=[f"{prefix}_SIZE"] if i == 0 else []) - - controller = partition_node.flash_controller - if controller.label is not None: - out_s(f"{prefix}_DEV", controller.label) - - -def write_irqs(node): - # Writes IRQ num and data for the interrupts in the node's 'interrupt' - # property - - def irq_name_alias(irq, cell_name): - if not irq.name: - return None - - alias = f"IRQ_{str2ident(irq.name)}" - if cell_name != "irq": - alias += f"_{str2ident(cell_name)}" - return alias + # TODO: can we push map_arm_gic_irq_type() and + # encode_zephyr_multi_level_irq() out of Python and into C with + # macro magic in devicetree.h? def map_arm_gic_irq_type(irq, irq_num): # Maps ARM GIC IRQ (type)+(index) combo to linear IRQ number @@ -532,268 +295,284 @@ def encode_zephyr_multi_level_irq(irq, irq_num): irq_ctrl = irq_ctrl.interrupts[0].controller return irq_num - for irq_i, irq in enumerate(node.interrupts): + idx_vals = [] + name_vals = [] + path_id = node.z_path_id + + if node.interrupts is not None: + idx_vals.append((f"{path_id}_IRQ_NUM", len(node.interrupts))) + + for i, irq in enumerate(node.interrupts): for cell_name, cell_value in irq.data.items(): - ident = f"IRQ_{irq_i}" + name = str2ident(cell_name) + if cell_name == "irq": if "arm,gic" in irq.controller.compats: cell_value = map_arm_gic_irq_type(irq, cell_value) cell_value = encode_zephyr_multi_level_irq(irq, cell_value) - else: - ident += f"_{str2ident(cell_name)}" - out_node(node, ident, cell_value, - name_alias=irq_name_alias(irq, cell_name)) + idx_macro = f"{path_id}_IRQ_IDX_{i}_VAL_{name}" + idx_vals.append((idx_macro, cell_value)) + idx_vals.append((idx_macro + "_EXISTS", 1)) + if irq.name: + name_macro = \ + f"{path_id}_IRQ_NAME_{str2ident(irq.name)}_VAL_{name}" + name_vals.append((name_macro, f"DT_{idx_macro}")) + name_vals.append((name_macro + "_EXISTS", 1)) + for macro, val in idx_vals: + out_dt_define(macro, val) + for macro, val in name_vals: + out_dt_define(macro, val) -def write_spi_dev(node): - # Writes SPI device GPIO chip select data if there is any - cs_gpio = node.spi_cs_gpio - if cs_gpio is not None: - write_phandle_val_list_entry(node, cs_gpio, None, "CS_GPIOS") +def write_compatibles(node): + # Writes a macro for each of the node's compatibles. We don't care + # about whether edtlib / Zephyr's binding language recognizes + # them. The compatibles the node provides are what is important. + for compat in node.compats: + out_dt_define( + f"{node.z_path_id}_COMPAT_MATCHES_{str2ident(compat)}", 1) -def write_phandle_val_list(prop): - # Writes output for a phandle/value list, e.g. - # - # pwms = <&pwm-ctrl-1 10 20 - # &pwm-ctrl-2 30 40>; - # - # prop: - # phandle/value Property instance. - # - # If only one entry appears in 'prop' (the example above has two), the - # generated identifier won't get a '_0' suffix, and the '_COUNT' and - # group initializer are skipped too. - # - # The base identifier is derived from the property name. For example, 'pwms = ...' - # generates output like this: - # - # #define _PWMS_CONTROLLER_0 "PWM_0" (name taken from 'label = ...') - # #define _PWMS_CHANNEL_0 123 (name taken from *-cells in binding) - # #define _PWMS_0 {"PWM_0", 123} - # #define _PWMS_CONTROLLER_1 "PWM_1" - # #define _PWMS_CHANNEL_1 456 - # #define _PWMS_1 {"PWM_1", 456} - # #define _PWMS_COUNT 2 - # #define _PWMS {_PWMS_0, _PWMS_1} - # ... - - # pwms -> PWMS - # foo-gpios -> FOO_GPIOS - ident = str2ident(prop.name) - - initializer_vals = [] - for i, entry in enumerate(prop.val): - initializer_vals.append(write_phandle_val_list_entry( - prop.node, entry, i if len(prop.val) > 1 else None, ident)) - - if len(prop.val) > 1: - out_node(prop.node, ident + "_COUNT", len(initializer_vals)) - out_node_init(prop.node, ident, initializer_vals) - - -def write_phandle_val_list_entry(node, entry, i, ident): - # write_phandle_val_list() helper. We could get rid of it if it wasn't for - # write_spi_dev(). Adds 'i' as an index to identifiers unless it's None. - # - # 'entry' is an edtlib.ControllerAndData instance. + +def write_vanilla_props(node): + # Writes macros for any and all properties defined in the + # "properties" section of the binding for the node. # - # Returns the identifier for the macro that provides the - # initializer for the entire entry. - - initializer_vals = [] - if entry.controller.label is not None: - ctrl_ident = ident + "_CONTROLLER" # e.g. PWMS_CONTROLLER - if entry.name: - name_alias = f"{str2ident(entry.name)}_{ctrl_ident}" - else: - name_alias = None - # Ugly backwards compatibility hack. Only add the index if there's - # more than one entry. - if i is not None: - ctrl_ident += f"_{i}" - initializer_vals.append(quote_str(entry.controller.label)) - out_node_s(node, ctrl_ident, entry.controller.label, name_alias) - - for cell, val in entry.data.items(): - cell_ident = f"{ident}_{str2ident(cell)}" # e.g. PWMS_CHANNEL - if entry.name: - # From e.g. 'pwm-names = ...' - name_alias = f"{str2ident(entry.name)}_{cell_ident}" - else: - name_alias = None - # Backwards compatibility (see above) - if i is not None: - cell_ident += f"_{i}" - out_node(node, cell_ident, val, name_alias) - - initializer_vals += entry.data.values() - - initializer_ident = ident - if entry.name: - name_alias = f"{initializer_ident}_{str2ident(entry.name)}" + # This does generate macros for special properties as well, like + # regs, etc. Just let that be rather than bothering to add + # never-ending amounts of special case code here to skip special + # properties. This function's macros can't conflict with + # write_special_props() macros, because they're in different + # namespaces. Special cases aren't special enough to break the rules. + + macro2val = {} + for prop_name, prop in node.props.items(): + macro = f"{node.z_path_id}_P_{str2ident(prop_name)}" + val = prop2value(prop) + if val is not None: + # DT_N__P_ + macro2val[macro] = val + + if prop.enum_index is not None: + # DT_N__P__ENUM_IDX + macro2val[macro + "_ENUM_IDX"] = prop.enum_index + + if "phandle" in prop.type: + macro2val.update(phandle_macros(prop, macro)) + elif "array" in prop.type: + # DT_N__P__IDX_ + for i, subval in enumerate(prop.val): + if isinstance(subval, str): + macro2val[macro + f"_IDX_{i}"] = quote_str(subval) + else: + macro2val[macro + f"_IDX_{i}"] = subval + + plen = prop_len(prop) + if plen is not None: + # DT_N__P__LEN + macro2val[macro + "_LEN"] = plen + + macro2val[f"{macro}_EXISTS"] = 1 + + if macro2val: + out_comment("Generic property macros:") + for macro, val in macro2val.items(): + out_dt_define(macro, val) else: - name_alias = None - if i is not None: - initializer_ident += f"_{i}" - return out_node_init(node, initializer_ident, initializer_vals, name_alias) + out_comment("(No generic property macros)") -def write_clocks(node): - # Writes clock information. - # - # Most of this ought to be handled in write_props(), but the identifiers - # that get generated for 'clocks' are inconsistent with the with other - # 'phandle-array' properties. - # - # See https://github.com/zephyrproject-rtos/zephyr/pull/19327#issuecomment-534081845. +def prop2value(prop): + # Gets the macro value for property 'prop', if there is + # a single well-defined C rvalue that it can be represented as. + # Returns None if there isn't one. - if "clocks" not in node.props: - return + if prop.type == "string": + return quote_str(prop.val) - for clock_i, clock in enumerate(node.props["clocks"].val): - controller = clock.controller + if prop.type == "int": + return prop.val - if controller.label is not None: - out_node_s(node, "CLOCK_CONTROLLER", controller.label) + if prop.type == "boolean": + return 1 if prop.val else 0 - for name, val in clock.data.items(): - if clock_i == 0: - clk_name_alias = "CLOCK_" + str2ident(name) - else: - clk_name_alias = None + if prop.type in ["array", "uint8-array"]: + return list2init(f"{val} /* {hex(val)} */" for val in prop.val) - out_node(node, f"CLOCK_{str2ident(name)}_{clock_i}", val, - name_alias=clk_name_alias) + if prop.type == "string-array": + return list2init(quote_str(val) for val in prop.val) - if "fixed-clock" not in controller.compats: - continue + # phandle, phandles, phandle-array, path, compound: nothing + return None - if "clock-frequency" not in controller.props: - err(f"{controller!r} is a 'fixed-clock' but lacks a " - "'clock-frequency' property") - out_node(node, "CLOCKS_CLOCK_FREQUENCY", - controller.props["clock-frequency"].val) +def prop_len(prop): + # Returns the property's length if and only if we should generate + # a _LEN macro for the property. Otherwise, returns None. + # + # This deliberately excludes reg and interrupts. + # While they have array type, their lengths as arrays are + # basically nonsense semantically due to #address-cells and + # #size-cells for "reg" and #interrupt-cells for "interrupts". + # + # We have special purpose macros for the number of register blocks + # / interrupt specifiers. Excluding them from this list means + # DT_PROP_LEN(node_id, ...) fails fast at the devicetree.h layer + # with a build error. This forces users to switch to the right + # macros. + if prop.type == "phandle": + return 1 -def str2ident(s): - # Converts 's' to a form suitable for (part of) an identifier + if (prop.type in ["array", "uint8-array", "string-array", + "phandles", "phandle-array"] and + prop.name not in ["reg", "interrupts"]): + return len(prop.val) - return s.replace("-", "_") \ - .replace(",", "_") \ - .replace("@", "_") \ - .replace("/", "_") \ - .replace(".", "_") \ - .replace("+", "PLUS") \ - .upper() + return None -def out_node(node, ident, val, name_alias=None, deprecation_msg=None): - # Writes a - # - # _ = - # - # assignment, along with a set of - # - # _ +def phandle_macros(prop, macro): + # Returns a dict of macros for phandle or phandles property 'prop'. # - # aliases, for each path/instance alias for the node. If 'name_alias' (a - # string) is passed, then these additional aliases are generated: + # The 'macro' argument is the N__P_ bit. # - # _ - # _ (for each node alias) + # These are currently special because we can't serialize their + # values without using label properties, which we're trying to get + # away from needing in Zephyr. (Label properties are great for + # humans, but have drawbacks for code size and boot time.) # - # 'name_alias' is used for reg-names and the like. - # - # If a 'deprecation_msg' string is passed, the generated identifiers will - # generate a warning if used, via __WARN()). - # - # Returns the identifier used for the macro that provides the value - # for 'ident' within 'node', e.g. DT_MFG_MODEL_CTL_GPIOS_PIN. + # The names look a bit weird to make it easier for devicetree.h + # to use the same macros for phandle, phandles, and phandle-array. - node_prefix = node_ident(node) + ret = {} - aliases = [f"{alias}_{ident}" for alias in node_aliases(node)] - if name_alias is not None: - aliases.append(f"{node_prefix}_{name_alias}") - aliases += [f"{alias}_{name_alias}" for alias in node_aliases(node)] + if prop.type == "phandle": + # A phandle is treated as a phandles with fixed length 1. + ret[f"{macro}_IDX_0_PH"] = f"DT_{prop.val.z_path_id}" + elif prop.type == "phandles": + for i, node in enumerate(prop.val): + ret[f"{macro}_IDX_{i}_PH"] = f"DT_{node.z_path_id}" + elif prop.type == "phandle-array": + for i, entry in enumerate(prop.val): + ret.update(controller_and_data_macros(entry, i, macro)) - return out(f"{node_prefix}_{ident}", val, aliases, deprecation_msg) + return ret -def out_node_s(node, ident, s, name_alias=None, deprecation_msg=None): - # Like out_node(), but emits 's' as a string literal +def controller_and_data_macros(entry, i, macro): + # Helper procedure used by phandle_macros(). # - # Returns the generated macro name for 'ident'. + # Its purpose is to write the "controller" (i.e. label property of + # the phandle's node) and associated data macros for a + # ControllerAndData. + + ret = {} + data = entry.data + + # DT_N__P__IDX__PH + ret[f"{macro}_IDX_{i}_PH"] = f"DT_{entry.controller.z_path_id}" + # DT_N__P__IDX__VAL_ + for cell, val in data.items(): + ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}"] = val + ret[f"{macro}_IDX_{i}_VAL_{str2ident(cell)}_EXISTS"] = 1 + + if not entry.name: + return ret + + name = str2ident(entry.name) + # DT_N__P__IDX__NAME + ret[f"{macro}_IDX_{i}_NAME"] = quote_str(entry.name) + # DT_N__P__NAME__PH + ret[f"{macro}_NAME_{name}_PH"] = f"DT_{entry.controller.z_path_id}" + # DT_N__P__NAME__VAL_ + for cell, val in data.items(): + cell_ident = str2ident(cell) + ret[f"{macro}_NAME_{name}_VAL_{cell_ident}"] = \ + f"DT_{macro}_IDX_{i}_VAL_{cell_ident}" + ret[f"{macro}_NAME_{name}_VAL_{cell_ident}_EXISTS"] = 1 + + return ret + + +def write_chosen(edt): + # Tree-wide information such as chosen nodes is printed here. + + out_comment("Chosen nodes\n") + chosen = {} + for name, node in edt.chosen_nodes.items(): + chosen[f"DT_CHOSEN_{str2ident(name)}"] = f"DT_{node.z_path_id}" + chosen[f"DT_CHOSEN_{str2ident(name)}_EXISTS"] = 1 + max_len = max(map(len, chosen)) + for macro, value in chosen.items(): + out_define(macro, value, width=max_len) + + +def write_inst_num(edt): + # Tree-wide information such as number of instances is printed here. + + out_comment("Number of instances\n") + compat_list = [] + + # Walk the nodes to build which compats we need to generate for + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + if not node.enabled: + continue + if not node.matching_compat: + continue + for compat in node.compats: + if compat not in compat_list: + compat_list.append(compat) - return out_node(node, ident, quote_str(s), name_alias, deprecation_msg) + for compat in compat_list: + num_inst = len(edt.compat2enabled[compat]) + out_define(f"DT_N_INST_{str2ident(compat)}_NUM", num_inst) -def out_node_init(node, ident, elms, name_alias=None, deprecation_msg=None): - # Like out_node(), but generates an {e1, e2, ...} initializer with the - # elements in the iterable 'elms'. - # - # Returns the generated macro name for 'ident'. +def str2ident(s): + # Converts 's' to a form suitable for (part of) an identifier - return out_node(node, ident, "{" + ", ".join(map(str, elms)) + "}", - name_alias, deprecation_msg) + return re.sub('[-,.@/+]', '_', s.lower()) -def out_s(ident, val): - # Like out(), but puts quotes around 'val' and escapes any double - # quotes and backslashes within it - # - # Returns the generated macro name for 'ident'. +def list2init(l): + # Converts 'l', a Python list (or iterable), to a C array initializer - return out(ident, quote_str(val)) + return "{" + ", ".join(l) + "}" -def out(ident, val, aliases=(), deprecation_msg=None): - # Writes '#define ' to the header and '=' to the - # the configuration file. +def out_dt_define(macro, val, width=None, deprecation_msg=None): + # Writes "#define DT_ " to the header file # - # Also writes any aliases listed in 'aliases' (an iterable). For the - # header, these look like '#define '. For the configuration - # file, the value is just repeated as '=' for each alias. + # The macro will be left-justified to 'width' characters if that + # is specified, and the value will follow immediately after in + # that case. Otherwise, this function decides how to add + # whitespace between 'macro' and 'val'. # - # See out_node() for the meaning of 'deprecation_msg'. + # If a 'deprecation_msg' string is passed, the generated identifiers will + # generate a warning if used, via __WARN()). # - # Returns the generated macro name for 'ident'. + # Returns the full generated macro for 'macro', with leading "DT_". + ret = "DT_" + macro + out_define(ret, val, width=width, deprecation_msg=deprecation_msg) + return ret - out_define(ident, val, deprecation_msg, header_file) - primary_ident = f"DT_{ident}" - # Exclude things that aren't single token values from .conf. At - # the moment the only such items are unquoted string - # representations of initializer lists, which begin with a curly - # brace. - output_to_conf = not (isinstance(val, str) and val.startswith("{")) - if output_to_conf: - print(f"{primary_ident}={val}", file=conf_file) +def out_define(macro, val, width=None, deprecation_msg=None): + # Helper for out_dt_define(). Outputs "#define ", + # adds a deprecation message if given, and allocates whitespace + # unless told not to. - for alias in aliases: - if alias != ident: - out_define(alias, "DT_" + ident, deprecation_msg, header_file) - if output_to_conf: - # For the configuration file, the value is just repeated for all - # the aliases - print(f"DT_{alias}={val}", file=conf_file) + warn = fr' __WARN("{deprecation_msg}")' if deprecation_msg else "" - return primary_ident - - -def out_define(ident, val, deprecation_msg, out_file): - # out() helper for writing a #define. See out_node() for the meaning of - # 'deprecation_msg'. + if width: + s = f"#define {macro.ljust(width)}{warn} {val}" + else: + s = f"#define {macro}{warn} {val}" - s = f"#define DT_{ident:40}" - if deprecation_msg: - s += fr' __WARN("{deprecation_msg}")' - s += f" {val}" - print(s, file=out_file) + print(s, file=header_file) def out_comment(s, blank_before=True): @@ -803,7 +582,6 @@ def out_comment(s, blank_before=True): if blank_before: print(file=header_file) - print(file=conf_file) if "\n" in s: # Format multi-line comments like @@ -827,9 +605,6 @@ def out_comment(s, blank_before=True): # /* foo bar */ print("/* " + s + " */", file=header_file) - print("\n".join("# " + line if line.strip() else "#" - for line in s.splitlines()), file=conf_file) - def escape(s): # Backslash-escapes any double quotes and backslashes in 's' diff --git a/scripts/dts/gen_legacy_defines.py b/scripts/dts/gen_legacy_defines.py new file mode 100755 index 0000000000000..8cef9062ac2b6 --- /dev/null +++ b/scripts/dts/gen_legacy_defines.py @@ -0,0 +1,836 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019 Nordic Semiconductor ASA +# Copyright (c) 2019 Linaro Limited +# SPDX-License-Identifier: BSD-3-Clause + +# This script is similar to gen_defines.py, but is for the legacy +# macro syntax used in e.g. Zephyr 2.2. +# +# It should be considered frozen code. New macro-related functionality +# should be done by modifying the macro namespaces managed by +# gen_defines.py. + +import argparse +import os +import pathlib +import sys + +import edtlib + +def main(): + global conf_file + global header_file + global flash_area_num + + args = parse_args() + + try: + edt = edtlib.EDT(args.dts, args.bindings_dirs, + # Suppress this warning if it's suppressed in dtc + warn_reg_unit_address_mismatch= + "-Wno-simple_bus_reg" not in args.dtc_flags) + except edtlib.EDTError as e: + sys.exit(f"devicetree error: {e}") + + conf_file = open(args.conf_out, "w", encoding="utf-8") + header_file = open(args.header_out, "w", encoding="utf-8") + flash_area_num = 0 + + write_top_comment(edt) + + for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal): + write_node_comment(node) + + # Flash partition nodes are handled as a special case. It + # would be nicer if we had bindings that would let us + # avoid that, but this will do for now. + if node.name.startswith("partition@"): + write_flash_partition(node, flash_area_num) + flash_area_num += 1 + + if node.enabled and node.matching_compat: + write_regs(node) + write_irqs(node) + write_props(node) + write_clocks(node) + write_spi_dev(node) + write_bus(node) + write_existence_flags(node) + + out_comment("Compatibles appearing on enabled nodes") + for compat in sorted(edt.compat2enabled): + #define DT_COMPAT_ 1 + out(f"COMPAT_{str2ident(compat)}", 1) + + # Definitions derived from /chosen nodes + write_addr_size(edt, "zephyr,ccm", "CCM") + write_addr_size(edt, "zephyr,dtcm", "DTCM") + write_addr_size(edt, "zephyr,ipc_shm", "IPC_SHM") + write_flash(edt) + + conf_file.close() + header_file.close() + + +def parse_args(): + # Returns parsed command-line arguments + + parser = argparse.ArgumentParser() + parser.add_argument("--dts", required=True, help="DTS file") + parser.add_argument("--dtc-flags", + help="'dtc' devicetree compiler flags, some of which " + "might be respected here") + parser.add_argument("--bindings-dirs", nargs='+', required=True, + help="directory with bindings in YAML format, " + "we allow multiple") + parser.add_argument("--header-out", required=True, + help="path to write header to") + parser.add_argument("--conf-out", required=True, + help="path to write configuration file to") + + return parser.parse_args() + + +def write_top_comment(edt): + # Writes an overview comment with misc. info at the top of the header and + # configuration file + + s = f"""\ +Generated by gen_legacy_defines.py + +DTS input file: + {edt.dts_path} + +Directories with bindings: + {", ".join(map(relativize, edt.bindings_dirs))} + +Nodes in dependency order (ordinal and path): +""" + + for scc in edt.scc_order(): + if len(scc) > 1: + err("cycle in devicetree involving " + + ", ".join(node.path for node in scc)) + s += f" {scc[0].dep_ordinal:<3} {scc[0].path}\n" + + s += """ +Definitions derived from these nodes in dependency order are next, +followed by tree-wide information (active compatibles, chosen nodes, +etc.). +""" + + out_comment(s, blank_before=False) + + +def write_node_comment(node): + # Writes a comment describing 'node' to the header and configuration file + + s = f"""\ +Devicetree node: + {node.path} +""" + if node.matching_compat: + s += f""" +Binding (compatible = {node.matching_compat}): + {relativize(node.binding_path)} +""" + else: + s += "\nNo matching binding.\n" + + s += f"\nDependency Ordinal: {node.dep_ordinal}\n" + + if node.depends_on: + s += "\nRequires:\n" + for dep in node.depends_on: + s += f" {dep.dep_ordinal:<3} {dep.path}\n" + + if node.required_by: + s += "\nSupports:\n" + for req in node.required_by: + s += f" {req.dep_ordinal:<3} {req.path}\n" + + if node.description: + # Indent description by two spaces + s += "\nDescription:\n" + \ + "\n".join(" " + line for line in + node.description.splitlines()) + \ + "\n" + + if not node.enabled: + s += "\nNode is disabled.\n" + + out_comment(s) + + +def relativize(path): + # If 'path' is within $ZEPHYR_BASE, returns it relative to $ZEPHYR_BASE, + # with a "$ZEPHYR_BASE/..." hint at the start of the string. Otherwise, + # returns 'path' unchanged. + + zbase = os.getenv("ZEPHYR_BASE") + if zbase is None: + return path + + try: + return str("$ZEPHYR_BASE" / pathlib.Path(path).relative_to(zbase)) + except ValueError: + # Not within ZEPHYR_BASE + return path + + +def write_regs(node): + # Writes address/size output for the registers in the node's 'reg' property + + def write_reg(reg, base_ident, val): + # Drop '_0' from the identifier if there's a single register, for + # backwards compatibility + if len(reg.node.regs) > 1: + ident = f"{base_ident}_{reg.node.regs.index(reg)}" + else: + ident = base_ident + + out_node(node, ident, val, + # Name alias from 'reg-names = ...' + f"{str2ident(reg.name)}_{base_ident}" if reg.name else None) + + for reg in node.regs: + write_reg(reg, "BASE_ADDRESS", hex(reg.addr)) + if reg.size: + write_reg(reg, "SIZE", reg.size) + + +def write_props(node): + # Writes any properties defined in the "properties" section of the binding + # for the node + + for prop in node.props.values(): + if not should_write(prop): + continue + + if prop.description is not None: + out_comment(prop.description, blank_before=False) + + ident = str2ident(prop.name) + + if prop.type == "boolean": + out_node(node, ident, 1 if prop.val else 0) + elif prop.type == "string": + out_node_s(node, ident, prop.val) + elif prop.type == "int": + out_node(node, ident, prop.val) + elif prop.type == "array": + for i, val in enumerate(prop.val): + out_node(node, f"{ident}_{i}", val) + out_node_init(node, ident, prop.val) + elif prop.type == "string-array": + for i, val in enumerate(prop.val): + out_node_s(node, f"{ident}_{i}", val) + elif prop.type == "uint8-array": + out_node_init(node, ident, + [f"0x{b:02x}" for b in prop.val]) + else: # prop.type == "phandle-array" + write_phandle_val_list(prop) + + # Generate DT_..._ENUM if there's an 'enum:' key in the binding + if prop.enum_index is not None: + out_node(node, ident + "_ENUM", prop.enum_index) + + +def should_write(prop): + # write_props() helper. Returns True if output should be generated for + # 'prop'. + + # Skip #size-cell and other property starting with #. Also skip mapping + # properties like 'gpio-map'. + if prop.name[0] == "#" or prop.name.endswith("-map"): + return False + + # See write_clocks() + if prop.name == "clocks": + return False + + # For these, Property.val becomes an edtlib.Node, a list of edtlib.Nodes, + # or None. Nothing is generated for them at the moment. + if prop.type in {"phandle", "phandles", "path", "compound"}: + return False + + # Skip properties that we handle elsewhere + if prop.name in { + "reg", "compatible", "status", "interrupts", + "interrupt-controller", "gpio-controller" + }: + return False + + return True + + +def write_bus(node): + # Generate bus-related #defines + + if not node.bus_node: + return + + if node.bus_node.label is None: + err(f"missing 'label' property on bus node {node.bus_node!r}") + + # #define DT__BUS_NAME + out_node_s(node, "BUS_NAME", str2ident(node.bus_node.label)) + + for compat in node.compats: + # #define DT__BUS_ 1 + out(f"{str2ident(compat)}_BUS_{str2ident(node.on_bus)}", 1) + + +def write_existence_flags(node): + # Generate #defines of the form + # + # #define DT_INST__ 1 + # + # for enabled nodes. These are flags for which devices exist. + + for compat in node.compats: + instance_no = node.edt.compat2enabled[compat].index(node) + out(f"INST_{instance_no}_{str2ident(compat)}", 1) + + +def node_ident(node): + # Returns an identifier for 'node'. Used e.g. when building macro names. + + # TODO: Handle PWM on STM + # TODO: Better document the rules of how we generate things + + ident = "" + + if node.bus_node: + ident += "{}_{:X}_".format( + str2ident(node.bus_node.matching_compat), node.bus_node.unit_addr) + + ident += f"{str2ident(node.matching_compat)}_" + + if node.unit_addr is not None: + ident += f"{node.unit_addr:X}" + elif node.parent.unit_addr is not None: + ident += f"{node.parent.unit_addr:X}_{str2ident(node.name)}" + else: + # This is a bit of a hack + ident += str2ident(node.name) + + return ident + + +def node_aliases(node): + # Returns a list of aliases for 'node', used e.g. when building macro names + + return node_path_aliases(node) + node_instance_aliases(node) + + +def node_path_aliases(node): + # Returns a list of aliases for 'node', based on the aliases registered for + # it in the /aliases node. Used e.g. when building macro names. + + if node.matching_compat is None: + return [] + + compat_s = str2ident(node.matching_compat) + + aliases = [] + for alias in node.aliases: + aliases.append(f"ALIAS_{str2ident(alias)}") + # TODO: See if we can remove or deprecate this form + aliases.append(f"{compat_s}_{str2ident(alias)}") + + return aliases + + +def node_instance_aliases(node): + # Returns a list of aliases for 'node', based on the compatible string and + # the instance number (each node with a particular compatible gets its own + # instance number, starting from zero). + # + # This is a list since a node can have multiple 'compatible' strings, each + # with their own instance number. + + res = [] + for compat in node.compats: + instance_no = node.edt.compat2enabled[compat].index(node) + res.append(f"INST_{instance_no}_{str2ident(compat)}") + return res + + +def write_addr_size(edt, prop_name, prefix): + # Writes _BASE_ADDRESS and _SIZE for the node pointed at by + # the /chosen property named 'prop_name', if it exists + + node = edt.chosen_node(prop_name) + if not node: + return + + if not node.regs: + err("missing 'reg' property in node pointed at by " + f"/chosen/{prop_name} ({node!r})") + + out_comment(f"/chosen/{prop_name} ({node.path})") + out(f"{prefix}_BASE_ADDRESS", hex(node.regs[0].addr)) + out(f"{prefix}_SIZE", node.regs[0].size//1024) + + +def write_flash(edt): + # Writes chosen and tree-wide flash-related output + + write_flash_node(edt) + write_code_partition(edt) + + if flash_area_num != 0: + out_comment("Number of flash partitions") + out("FLASH_AREA_NUM", flash_area_num) + + +def write_flash_node(edt): + # Writes output for the top-level flash node pointed at by + # zephyr,flash in /chosen + + node = edt.chosen_node("zephyr,flash") + + out_comment(f"/chosen/zephyr,flash ({node.path if node else 'missing'})") + + if not node: + # No flash node. Write dummy values. + out("FLASH_BASE_ADDRESS", 0) + out("FLASH_SIZE", 0) + return + + if len(node.regs) != 1: + err("expected zephyr,flash to have a single register, has " + f"{len(node.regs)}") + + if node.on_bus == "spi" and len(node.bus_node.regs) == 2: + reg = node.bus_node.regs[1] # QSPI flash + else: + reg = node.regs[0] + + out("FLASH_BASE_ADDRESS", hex(reg.addr)) + if reg.size: + out("FLASH_SIZE", reg.size//1024) + + if "erase-block-size" in node.props: + out("FLASH_ERASE_BLOCK_SIZE", node.props["erase-block-size"].val) + + if "write-block-size" in node.props: + out("FLASH_WRITE_BLOCK_SIZE", node.props["write-block-size"].val) + + +def write_code_partition(edt): + # Writes output for the node pointed at by zephyr,code-partition in /chosen + + node = edt.chosen_node("zephyr,code-partition") + + out_comment("/chosen/zephyr,code-partition " + f"({node.path if node else 'missing'})") + + if not node: + # No code partition. Write dummy values. + out("CODE_PARTITION_OFFSET", 0) + out("CODE_PARTITION_SIZE", 0) + return + + if not node.regs: + err(f"missing 'regs' property on {node!r}") + + out("CODE_PARTITION_OFFSET", node.regs[0].addr) + out("CODE_PARTITION_SIZE", node.regs[0].size) + + +def write_flash_partition(partition_node, index): + if partition_node.label is None: + err(f"missing 'label' property on {partition_node!r}") + + # Generate label-based identifiers + write_flash_partition_prefix( + "FLASH_AREA_" + str2ident(partition_node.label), partition_node, index) + + # Generate index-based identifiers + write_flash_partition_prefix(f"FLASH_AREA_{index}", partition_node, index) + + +def write_flash_partition_prefix(prefix, partition_node, index): + # write_flash_partition() helper. Generates identifiers starting with + # 'prefix'. + + out(f"{prefix}_ID", index) + + out(f"{prefix}_READ_ONLY", 1 if partition_node.read_only else 0) + + for i, reg in enumerate(partition_node.regs): + # Also add aliases that point to the first sector (TODO: get rid of the + # aliases?) + out(f"{prefix}_OFFSET_{i}", reg.addr, + aliases=[f"{prefix}_OFFSET"] if i == 0 else []) + out(f"{prefix}_SIZE_{i}", reg.size, + aliases=[f"{prefix}_SIZE"] if i == 0 else []) + + controller = partition_node.flash_controller + if controller.label is not None: + out_s(f"{prefix}_DEV", controller.label) + + +def write_irqs(node): + # Writes IRQ num and data for the interrupts in the node's 'interrupt' + # property + + def irq_name_alias(irq, cell_name): + if not irq.name: + return None + + alias = f"IRQ_{str2ident(irq.name)}" + if cell_name != "irq": + alias += f"_{str2ident(cell_name)}" + return alias + + def map_arm_gic_irq_type(irq, irq_num): + # Maps ARM GIC IRQ (type)+(index) combo to linear IRQ number + if "type" not in irq.data: + err(f"Expected binding for {irq.controller!r} to have 'type' in " + "interrupt-cells") + irq_type = irq.data["type"] + + if irq_type == 0: # GIC_SPI + return irq_num + 32 + if irq_type == 1: # GIC_PPI + return irq_num + 16 + err(f"Invalid interrupt type specified for {irq!r}") + + def encode_zephyr_multi_level_irq(irq, irq_num): + # See doc/reference/kernel/other/interrupts.rst for details + # on how this encoding works + + irq_ctrl = irq.controller + # Look for interrupt controller parent until we have none + while irq_ctrl.interrupts: + irq_num = (irq_num + 1) << 8 + if "irq" not in irq_ctrl.interrupts[0].data: + err(f"Expected binding for {irq_ctrl!r} to have 'irq' in " + "interrupt-cells") + irq_num |= irq_ctrl.interrupts[0].data["irq"] + irq_ctrl = irq_ctrl.interrupts[0].controller + return irq_num + + for irq_i, irq in enumerate(node.interrupts): + for cell_name, cell_value in irq.data.items(): + ident = f"IRQ_{irq_i}" + if cell_name == "irq": + if "arm,gic" in irq.controller.compats: + cell_value = map_arm_gic_irq_type(irq, cell_value) + cell_value = encode_zephyr_multi_level_irq(irq, cell_value) + else: + ident += f"_{str2ident(cell_name)}" + + out_node(node, ident, cell_value, + name_alias=irq_name_alias(irq, cell_name)) + + +def write_spi_dev(node): + # Writes SPI device GPIO chip select data if there is any + + cs_gpio = node.spi_cs_gpio + if cs_gpio is not None: + write_phandle_val_list_entry(node, cs_gpio, None, "CS_GPIOS") + + +def write_phandle_val_list(prop): + # Writes output for a phandle/value list, e.g. + # + # pwms = <&pwm-ctrl-1 10 20 + # &pwm-ctrl-2 30 40>; + # + # prop: + # phandle/value Property instance. + # + # If only one entry appears in 'prop' (the example above has two), the + # generated identifier won't get a '_0' suffix, and the '_COUNT' and + # group initializer are skipped too. + # + # The base identifier is derived from the property name. For example, 'pwms = ...' + # generates output like this: + # + # #define _PWMS_CONTROLLER_0 "PWM_0" (name taken from 'label = ...') + # #define _PWMS_CHANNEL_0 123 (name taken from *-cells in binding) + # #define _PWMS_0 {"PWM_0", 123} + # #define _PWMS_CONTROLLER_1 "PWM_1" + # #define _PWMS_CHANNEL_1 456 + # #define _PWMS_1 {"PWM_1", 456} + # #define _PWMS_COUNT 2 + # #define _PWMS {_PWMS_0, _PWMS_1} + # ... + + # pwms -> PWMS + # foo-gpios -> FOO_GPIOS + ident = str2ident(prop.name) + + initializer_vals = [] + for i, entry in enumerate(prop.val): + initializer_vals.append(write_phandle_val_list_entry( + prop.node, entry, i if len(prop.val) > 1 else None, ident)) + + if len(prop.val) > 1: + out_node(prop.node, ident + "_COUNT", len(initializer_vals)) + out_node_init(prop.node, ident, initializer_vals) + + +def write_phandle_val_list_entry(node, entry, i, ident): + # write_phandle_val_list() helper. We could get rid of it if it wasn't for + # write_spi_dev(). Adds 'i' as an index to identifiers unless it's None. + # + # 'entry' is an edtlib.ControllerAndData instance. + # + # Returns the identifier for the macro that provides the + # initializer for the entire entry. + + initializer_vals = [] + if entry.controller.label is not None: + ctrl_ident = ident + "_CONTROLLER" # e.g. PWMS_CONTROLLER + if entry.name: + name_alias = f"{str2ident(entry.name)}_{ctrl_ident}" + else: + name_alias = None + # Ugly backwards compatibility hack. Only add the index if there's + # more than one entry. + if i is not None: + ctrl_ident += f"_{i}" + initializer_vals.append(quote_str(entry.controller.label)) + out_node_s(node, ctrl_ident, entry.controller.label, name_alias) + + for cell, val in entry.data.items(): + cell_ident = f"{ident}_{str2ident(cell)}" # e.g. PWMS_CHANNEL + if entry.name: + # From e.g. 'pwm-names = ...' + name_alias = f"{str2ident(entry.name)}_{cell_ident}" + else: + name_alias = None + # Backwards compatibility (see above) + if i is not None: + cell_ident += f"_{i}" + out_node(node, cell_ident, val, name_alias) + + initializer_vals += entry.data.values() + + initializer_ident = ident + if entry.name: + name_alias = f"{initializer_ident}_{str2ident(entry.name)}" + else: + name_alias = None + if i is not None: + initializer_ident += f"_{i}" + return out_node_init(node, initializer_ident, initializer_vals, name_alias) + + +def write_clocks(node): + # Writes clock information. + # + # Most of this ought to be handled in write_props(), but the identifiers + # that get generated for 'clocks' are inconsistent with the with other + # 'phandle-array' properties. + # + # See https://github.com/zephyrproject-rtos/zephyr/pull/19327#issuecomment-534081845. + + if "clocks" not in node.props: + return + + for clock_i, clock in enumerate(node.props["clocks"].val): + controller = clock.controller + + if controller.label is not None: + out_node_s(node, "CLOCK_CONTROLLER", controller.label) + + for name, val in clock.data.items(): + if clock_i == 0: + clk_name_alias = "CLOCK_" + str2ident(name) + else: + clk_name_alias = None + + out_node(node, f"CLOCK_{str2ident(name)}_{clock_i}", val, + name_alias=clk_name_alias) + + if "fixed-clock" not in controller.compats: + continue + + if "clock-frequency" not in controller.props: + err(f"{controller!r} is a 'fixed-clock' but lacks a " + "'clock-frequency' property") + + out_node(node, "CLOCKS_CLOCK_FREQUENCY", + controller.props["clock-frequency"].val) + + +def str2ident(s): + # Converts 's' to a form suitable for (part of) an identifier + + return s.replace("-", "_") \ + .replace(",", "_") \ + .replace("@", "_") \ + .replace("/", "_") \ + .replace(".", "_") \ + .replace("+", "PLUS") \ + .upper() + + +def out_node(node, ident, val, name_alias=None, deprecation_msg=None): + # Writes a + # + # _ = + # + # assignment, along with a set of + # + # _ + # + # aliases, for each path/instance alias for the node. If 'name_alias' (a + # string) is passed, then these additional aliases are generated: + # + # _ + # _ (for each node alias) + # + # 'name_alias' is used for reg-names and the like. + # + # If a 'deprecation_msg' string is passed, the generated identifiers will + # generate a warning if used, via __WARN()). + # + # Returns the identifier used for the macro that provides the value + # for 'ident' within 'node', e.g. DT_MFG_MODEL_CTL_GPIOS_PIN. + + node_prefix = node_ident(node) + + aliases = [f"{alias}_{ident}" for alias in node_aliases(node)] + if name_alias is not None: + aliases.append(f"{node_prefix}_{name_alias}") + aliases += [f"{alias}_{name_alias}" for alias in node_aliases(node)] + + return out(f"{node_prefix}_{ident}", val, aliases, deprecation_msg) + + +def out_node_s(node, ident, s, name_alias=None, deprecation_msg=None): + # Like out_node(), but emits 's' as a string literal + # + # Returns the generated macro name for 'ident'. + + return out_node(node, ident, quote_str(s), name_alias, deprecation_msg) + + +def out_node_init(node, ident, elms, name_alias=None, deprecation_msg=None): + # Like out_node(), but generates an {e1, e2, ...} initializer with the + # elements in the iterable 'elms'. + # + # Returns the generated macro name for 'ident'. + + return out_node(node, ident, "{" + ", ".join(map(str, elms)) + "}", + name_alias, deprecation_msg) + + +def out_s(ident, val): + # Like out(), but puts quotes around 'val' and escapes any double + # quotes and backslashes within it + # + # Returns the generated macro name for 'ident'. + + return out(ident, quote_str(val)) + + +def out(ident, val, aliases=(), deprecation_msg=None): + # Writes '#define ' to the header and '=' to the + # the configuration file. + # + # Also writes any aliases listed in 'aliases' (an iterable). For the + # header, these look like '#define '. For the configuration + # file, the value is just repeated as '=' for each alias. + # + # See out_node() for the meaning of 'deprecation_msg'. + # + # Returns the generated macro name for 'ident'. + + out_define(ident, val, deprecation_msg, header_file) + primary_ident = f"DT_{ident}" + + # Exclude things that aren't single token values from .conf. At + # the moment the only such items are unquoted string + # representations of initializer lists, which begin with a curly + # brace. + output_to_conf = not (isinstance(val, str) and val.startswith("{")) + if output_to_conf: + print(f"{primary_ident}={val}", file=conf_file) + + for alias in aliases: + if alias != ident: + out_define(alias, "DT_" + ident, deprecation_msg, header_file) + if output_to_conf: + # For the configuration file, the value is just repeated for all + # the aliases + print(f"DT_{alias}={val}", file=conf_file) + + return primary_ident + + +def out_define(ident, val, deprecation_msg, out_file): + # out() helper for writing a #define. See out_node() for the meaning of + # 'deprecation_msg'. + + s = f"#define DT_{ident:40}" + if deprecation_msg: + s += fr' __WARN("{deprecation_msg}")' + s += f" {val}" + print(s, file=out_file) + + +def out_comment(s, blank_before=True): + # Writes 's' as a comment to the header and configuration file. 's' is + # allowed to have multiple lines. blank_before=True adds a blank line + # before the comment. + + if blank_before: + print(file=header_file) + print(file=conf_file) + + if "\n" in s: + # Format multi-line comments like + # + # /* + # * first line + # * second line + # * + # * empty line before this line + # */ + res = ["/*"] + for line in s.splitlines(): + # Avoid an extra space after '*' for empty lines. They turn red in + # Vim if space error checking is on, which is annoying. + res.append(" *" if not line.strip() else " * " + line) + res.append(" */") + print("\n".join(res), file=header_file) + else: + # Format single-line comments like + # + # /* foo bar */ + print("/* " + s + " */", file=header_file) + + print("\n".join("# " + line if line.strip() else "#" + for line in s.splitlines()), file=conf_file) + + +def escape(s): + # Backslash-escapes any double quotes and backslashes in 's' + + # \ must be escaped before " to avoid double escaping + return s.replace("\\", "\\\\").replace('"', '\\"') + + +def quote_str(s): + # Puts quotes around 's' and escapes any double quotes and + # backslashes within it + + return f'"{escape(s)}"' + + +def err(s): + raise Exception(s) + + +if __name__ == "__main__": + main() diff --git a/scripts/dts/testedtlib.py b/scripts/dts/testedtlib.py index 4d14209ee6549..8355275229e6e 100755 --- a/scripts/dts/testedtlib.py +++ b/scripts/dts/testedtlib.py @@ -63,10 +63,10 @@ def run(): # verify_streq(edt.get_node("/reg-zero-address-cells/node").regs, - "[, ]") + "[, ]") verify_streq(edt.get_node("/reg-zero-size-cells/node").regs, - "[, ]") + "[, ]") verify_streq(edt.get_node("/reg-ranges/parent/node").regs, "[, , , , , ]") diff --git a/scripts/kconfig/kconfigfunctions.py b/scripts/kconfig/kconfigfunctions.py index 506c43b789405..91b95112608e7 100644 --- a/scripts/kconfig/kconfigfunctions.py +++ b/scripts/kconfig/kconfigfunctions.py @@ -173,6 +173,9 @@ def _node_reg_addr(node, index, unit): if int(index) >= len(node.regs): return 0 + if node.regs[int(index)].addr is None: + return 0 + return node.regs[int(index)].addr >> _dt_units_to_scale(unit) @@ -186,6 +189,9 @@ def _node_reg_size(node, index, unit): if int(index) >= len(node.regs): return 0 + if node.regs[int(index)].size is None: + return 0 + return node.regs[int(index)].size >> _dt_units_to_scale(unit) diff --git a/tests/lib/devicetree/CMakeLists.txt b/tests/lib/devicetree/CMakeLists.txt new file mode 100644 index 0000000000000..33d1fefb4e250 --- /dev/null +++ b/tests/lib/devicetree/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +set(DTS_ROOTS ${CMAKE_CURRENT_LIST_DIR}) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(devicetree) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/lib/devicetree/README b/tests/lib/devicetree/README new file mode 100644 index 0000000000000..a1a1a9a671baf --- /dev/null +++ b/tests/lib/devicetree/README @@ -0,0 +1 @@ +Test cases for the devicetree.h API. diff --git a/tests/lib/devicetree/app.overlay b/tests/lib/devicetree/app.overlay new file mode 100644 index 0000000000000..568ff9a7e6af4 --- /dev/null +++ b/tests/lib/devicetree/app.overlay @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + * + * Application overlay for testing the devicetree.h API. + * + * Names in this file should be chosen in a way that won't conflict + * with real-world devicetree nodes, to allow these tests to run on + * (and be extended to test) real hardware. + */ + +/ { + aliases { + test-alias = &test_nodelabel; + }; + + chosen { + ztest,gpio = &test_nodelabel; + }; + + test { + #address-cells = < 0x1 >; + #size-cells = < 0x1 >; + interrupt-parent = <&test_intc>; + + test_arrays: array-holder { + /* vnd,undefined-compat is for DT_NODE_HAS_COMPAT() */ + compatible = "vnd,array-holder", "vnd,undefined-compat"; + a = <1000 2000 3000>; + b = [aa bb cc dd]; + c = "bar", "baz"; + }; + + test_phandles: phandle-holder-0 { + compatible = "vnd,phandle-holder"; + ph = <&test_gpio_1>; + phs = <&test_gpio_1 &test_gpio_2 &test_i2c>; + gpios = <&test_gpio_1 10 20>, <&test_gpio_2 30 40>; + pha-gpios = <&test_gpio_1 50 60>, <&test_gpio_3 70>, <&test_gpio_2 80 90>; + foos = <&test_gpio_1 100>, <&test_gpio_2 110>; + foo-names = "A", "b-c"; + }; + + test_enum_0: enum-0 { + compatible = "vnd,enum-holder"; + val = "zero"; + }; + + test_enum_1: enum-1 { + compatible = "vnd,enum-holder"; + val = "two"; + }; + + /* + * This should be the only node with this + * compatible in the tree. + */ + disabled-node@0 { + compatible = "vnd,disabled-compat"; + reg = < 0x0 0x1000 >; + label = "DISABLED_NODE_0"; + status = "disabled"; + }; + + disabled_gpio: gpio@0 { + compatible = "vnd,gpio"; + gpio-controller; + reg = < 0x0 0x1000 >; + interrupts = <3 1>; + #gpio-cells = < 0x2 >; + label = "TEST_GPIO_0"; + status = "disabled"; + }; + + test_nodelabel: TEST_NODELABEL_ALLCAPS: test_gpio_1: gpio@deadbeef { + compatible = "vnd,gpio"; + gpio-controller; + reg = < 0xdeadbeef 0x1000 >; + #gpio-cells = < 0x2 >; + #foo-cells = < 0x1 >; + label = "TEST_GPIO_1"; + interrupts = <4 3>; + status = "okay"; + }; + + test_gpio_2: gpio@abcd1234 { + compatible = "vnd,gpio"; + gpio-controller; + reg = < 0xabcd1234 0x500 0x98765432 0xff >; + reg-names = "one", "two"; + #gpio-cells = < 0x2 >; + #foo-cells = < 0x1 >; + interrupts = <5 2>; + label = "TEST_GPIO_2"; + status = "okay"; + }; + + test_gpio_3: gpio@1234 { + compatible = "vnd,gpio-one-cell"; + gpio-controller; + reg = < 0x1234 0x500 >; + #gpio-cells = < 0x1 >; + label = "TEST_GPIO_3"; + status = "okay"; + }; + + test_i2c: i2c@11112222 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,i2c"; + reg = < 0x11112222 0x1000 >; + label = "TEST_I2C_CTLR"; + status = "okay"; + clock-frequency = < 100000 >; + interrupts = <6 2 7 1>; + interrupt-names = "status", "error"; + + test-i2c-dev@10 { + compatible = "vnd,i2c-device"; + label = "TEST_I2C_DEV_10"; + reg = < 0x10 >; + }; + }; + + test_spi: spi@33334444 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,spi"; + reg = < 0x33334444 0x1000 >; + interrupts = <8 3 9 0 10 1>; + label = "TEST_SPI_CTLR"; + status = "okay"; + clock-frequency = < 2000000 >; + + cs-gpios = <&test_gpio_1 0x10 0x20>, + <&test_gpio_2 0x30 0x40>; + + /* all vnd,spi-device instances should have CS */ + + test-spi-dev@0 { + compatible = "vnd,spi-device"; + label = "TEST_SPI_DEV_0"; + reg = <0>; + spi-max-frequency = < 2000000 >; + }; + + test-spi-dev@1 { + compatible = "vnd,spi-device"; + label = "TEST_SPI_DEV_1"; + reg = <1>; + spi-max-frequency = < 2000000 >; + }; + }; + + test_spi_no_cs: spi@55556666 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,spi"; + reg = < 0x55556666 0x1000 >; + label = "TEST_SPI_CTLR_NO_CS"; + status = "okay"; + clock-frequency = < 2000000 >; + + /* no vnd,spi-device-2 instances should have CS */ + test_spi_dev_no_cs: test-spi-dev@0 { + compatible = "vnd,spi-device-2"; + label = "TEST_SPI_DEV_NO_CS"; + reg = <0>; + spi-max-frequency = < 2000000 >; + }; + }; + + test_i2c_1: i2c@77778888 { + #address-cells = < 1 >; + #size-cells = < 0 >; + compatible = "vnd,i2c"; + reg = < 0x77778888 0x1000 >; + label = "TEST_I2C_CTLR_1"; + status = "okay"; + clock-frequency = < 100000 >; + interrupts = <11 3 12 2>; + interrupt-names = "status", "error"; + }; + + test_adc_1: adc@10002000 { + reg = <0x10002000 0x1000>; + compatible = "vnd,adc"; + label = "TEST_ADC_1"; + status = "okay"; + #io-channel-cells = <1>; + }; + + test_adc_2: adc@10003000 { + reg = <0x10003000 0x1000>; + compatible = "vnd,adc"; + label = "TEST_ADC_2"; + status = "okay"; + #io-channel-cells = <1>; + }; + + /* there should only be one of these */ + test_temp_sensor: temperature-sensor { + compatible = "vnd,adc-temp-sensor"; + label = "TEST_TEMP"; + io-channels = <&test_adc_1 10>, <&test_adc_2 20>; + io-channel-names = "ch1", "ch2"; + clocks = <&test_clk 3 7>, <&test_fixed_clk>, <&test_clk 8 2>; + }; + + /* there should only be one of these */ + test_reg: reg-holder@9999aaaa { + compatible = "vnd,reg-holder"; + reg = < 0x9999aaaa 0x1000 0xbbbbcccc 0x3f >; + status = "okay"; + reg-names = "first", "second"; + misc-prop = <1234>; + }; + + test_intc: interrupt-controller@bbbbcccc { + compatible = "vnd,intc"; + reg = <0xbbbbcccc 0x1000>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* there should only be one of these */ + test_irq: interrupt-holder { + compatible = "vnd,interrupt-holder"; + status = "okay"; + interrupts = <30 3 40 5 60 7>; + interrupt-names = "err", "stat", "done"; + }; + + test_fixed_clk: test-fixed-clock { + compatible = "fixed-clock"; + clock-frequency = <25000000>; + #clock-cells = <0>; + }; + + test_clk: test-clock { + compatible = "vnd,clock"; + label = "TEST_CLOCK"; + #clock-cells = <2>; + }; + }; +}; diff --git a/tests/lib/devicetree/prj.conf b/tests/lib/devicetree/prj.conf new file mode 100644 index 0000000000000..9467c2926896d --- /dev/null +++ b/tests/lib/devicetree/prj.conf @@ -0,0 +1 @@ +CONFIG_ZTEST=y diff --git a/tests/lib/devicetree/src/main.c b/tests/lib/devicetree/src/main.c new file mode 100644 index 0000000000000..3136d3223bacd --- /dev/null +++ b/tests/lib/devicetree/src/main.c @@ -0,0 +1,1136 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define TEST_DEADBEEF DT_PATH(test, gpio_deadbeef) +#define TEST_ABCD1234 DT_PATH(test, gpio_abcd1234) +#define TEST_ALIAS DT_ALIAS(test_alias) +#define TEST_NODELABEL DT_NODELABEL(test_nodelabel) +#define TEST_INST DT_INST(0, vnd_gpio) +#define TEST_ARRAYS DT_NODELABEL(test_arrays) +#define TEST_PH DT_NODELABEL(test_phandles) +#define TEST_IRQ DT_NODELABEL(test_irq) +#define TEST_TEMP DT_NODELABEL(test_temp_sensor) + +static void test_path_props(void) +{ + zassert_true(!strcmp(DT_LABEL(TEST_DEADBEEF), "TEST_GPIO_1"), + "deadbeef label != TEST_GPIO_1"); + zassert_equal(DT_NUM_REGS(TEST_DEADBEEF), 1, + "deadbeef num reg"); + zassert_equal(DT_REG_ADDR(TEST_DEADBEEF), 0xdeadbeef, + "deadbeef reg[0] addr"); + zassert_equal(DT_REG_SIZE(TEST_DEADBEEF), 0x1000, + "deadbeef reg[0] len"); + zassert_equal(DT_PROP(TEST_DEADBEEF, gpio_controller), 1, + "deadbeef gpio-controller"); + zassert_equal(DT_PROP(TEST_DEADBEEF, ngpios), 32, "deadbeef ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_DEADBEEF, status), "okay"), + "deadbeef status"); + zassert_equal(DT_PROP_LEN(TEST_DEADBEEF, compatible), 1, + "deadbeef compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_DEADBEEF, compatible, 0), + "vnd,gpio"), + "deadbeef compatible[0]"); + zassert_true(DT_NODE_HAS_PROP(TEST_DEADBEEF, status), + "deadbeef status"); + zassert_false(DT_NODE_HAS_PROP(TEST_DEADBEEF, foobar), + "deadbeef foobar"); + + zassert_true(!strcmp(DT_LABEL(TEST_ABCD1234), "TEST_GPIO_2"), + "abcd1234 label != TEST_GPIO_2"); + zassert_equal(DT_NUM_REGS(TEST_ABCD1234), 2, + "abcd1234 num regs"); + zassert_equal(DT_PROP(TEST_ABCD1234, gpio_controller), 1, + "abcd1234 gpio-controller"); + zassert_equal(DT_PROP(TEST_ABCD1234, ngpios), 32, "abcd1234 ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_ABCD1234, status), "okay"), + "abcd1234 status"); + zassert_equal(DT_PROP_LEN(TEST_ABCD1234, compatible), 1, + "abcd1234 compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ABCD1234, compatible, 0), + "vnd,gpio"), + "abcd1234 compatible[0]"); +} + +static void test_alias_props(void) +{ + zassert_equal(DT_NUM_REGS(TEST_ALIAS), 1, "num regs"); + zassert_equal(DT_REG_ADDR(TEST_ALIAS), 0xdeadbeef, "reg[0] addr"); + zassert_equal(DT_REG_SIZE(TEST_ALIAS), 0x1000, "reg[0] len"); + zassert_true(!strcmp(DT_LABEL(TEST_ALIAS), "TEST_GPIO_1"), "label"); + zassert_equal(DT_PROP(TEST_ALIAS, gpio_controller), 1, + "gpio-controller"); + zassert_equal(DT_PROP(TEST_ALIAS, ngpios), 32, "ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_ALIAS, status), "okay"), "status"); + zassert_equal(DT_PROP_LEN(TEST_ALIAS, compatible), 1, + "compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ALIAS, compatible, 0), + "vnd,gpio"), + "compatible[0]"); +} + +static void test_nodelabel_props(void) +{ + zassert_equal(DT_NUM_REGS(TEST_NODELABEL), 1, "num regs"); + zassert_equal(DT_REG_ADDR(TEST_NODELABEL), 0xdeadbeef, "reg[0] addr"); + zassert_equal(DT_REG_SIZE(TEST_NODELABEL), 0x1000, "reg[0] len"); + zassert_true(!strcmp(DT_LABEL(TEST_NODELABEL), "TEST_GPIO_1"), "label"); + zassert_equal(DT_PROP(TEST_NODELABEL, gpio_controller), 1, + "gpio-controller"); + zassert_equal(DT_PROP(TEST_NODELABEL, ngpios), 32, "ngpios"); + zassert_true(!strcmp(DT_PROP(TEST_NODELABEL, status), "okay"), + "status"); + zassert_equal(DT_PROP_LEN(TEST_NODELABEL, compatible), 1, + "compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_NODELABEL, compatible, 0), + "vnd,gpio"), + "compatible[0]"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_gpio +static void test_inst_props(void) +{ + const char *label_startswith = "TEST_GPIO_"; + + /* + * Careful: + * + * We can only test properties that are shared across all + * instances of this compatible here. + */ + + zassert_equal(DT_PROP(TEST_INST, gpio_controller), 1, + "gpio-controller"); + zassert_true(!strcmp(DT_PROP(TEST_INST, status), "okay"), + "status"); + zassert_equal(DT_PROP_LEN(TEST_INST, compatible), 1, + "compatible len"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_INST, compatible, 0), + "vnd,gpio"), + "compatible[0]"); + + zassert_equal(DT_INST_NODE_HAS_PROP(0, gpio_controller), 1, + "inst 0 has no gpio-controller"); + zassert_equal(DT_INST_PROP(0, gpio_controller), 1, + "inst 0 gpio-controller is not 1"); + zassert_equal(DT_INST_NODE_HAS_PROP(0, xxxx), 0, + "inst 0 has xxxx prop"); + zassert_true(!strcmp(DT_INST_PROP(0, status), "okay"), + "inst 0 status"); + zassert_equal(DT_INST_PROP_LEN(0, compatible), 1, + "inst 0 compatible len"); + zassert_true(!strcmp(DT_INST_PROP_BY_IDX(0, compatible, 0), + "vnd,gpio"), + "inst 0 compatible[0]"); + zassert_true(!strncmp(label_startswith, DT_INST_LABEL(0), + strlen(label_startswith)), + "inst 0 label"); +} + +static void test_has_path(void) +{ + zassert_equal(DT_HAS_NODE(DT_PATH(test, gpio_0)), 0, "gpio@0"); + zassert_equal(DT_HAS_NODE(DT_PATH(test, gpio_deadbeef)), 1, + "gpio@deadbeef"); + zassert_equal(DT_HAS_NODE(DT_PATH(test, gpio_abcd1234)), 1, + "gpio@abcd1234"); +} + +static void test_has_alias(void) +{ + zassert_equal(DT_HAS_NODE(DT_ALIAS(test_alias)), 1, "test-alias"); + zassert_equal(DT_HAS_NODE(DT_ALIAS(test_undef)), 0, "test-undef"); +} + +static void test_has_inst(void) +{ + zassert_equal(DT_HAS_NODE(DT_INST(0, vnd_gpio)), 1, "vnd,gpio #0"); + zassert_equal(DT_HAS_NODE(DT_INST(1, vnd_gpio)), 1, "vnd,gpio #1"); + zassert_equal(DT_HAS_NODE(DT_INST(2, vnd_gpio)), 0, "vnd,gpio #2"); +} + +static void test_has_nodelabel(void) +{ + zassert_equal(DT_HAS_NODE(DT_NODELABEL(disabled_gpio)), 0, + "disabled_gpio"); + zassert_equal(DT_HAS_NODE(DT_NODELABEL(test_nodelabel)), 1, + "test_nodelabel"); + zassert_equal(DT_HAS_NODE(DT_NODELABEL(test_nodelabel_allcaps)), 1, + "TEST_NODELABEL_ALLCAPS"); +} + +#define TA_HAS_COMPAT(compat) DT_NODE_HAS_COMPAT(TEST_ARRAYS, compat) + +static void test_has_compat(void) +{ + unsigned int compats; + + zassert_true(DT_HAS_COMPAT(vnd_gpio), "vnd,gpio"); + zassert_true(DT_HAS_COMPAT(vnd_gpio), "vnd,i2c"); + zassert_false(DT_HAS_COMPAT(vnd_disabled_compat), + "vnd,disabled-compat"); + + zassert_equal(TA_HAS_COMPAT(vnd_array_holder), 1, "vnd,array-holder"); + zassert_equal(TA_HAS_COMPAT(vnd_undefined_compat), 1, + "vnd,undefined-compat"); + zassert_equal(TA_HAS_COMPAT(vnd_not_a_test_array_compat), 0, + "not present"); + compats = ((TA_HAS_COMPAT(vnd_array_holder) << 0) | + (TA_HAS_COMPAT(vnd_undefined_compat) << 1) | + (TA_HAS_COMPAT(vnd_not_a_test_array_compat) << 2)); + zassert_equal(compats, 0x3, "as bit array"); +} + +#define TEST_I2C_DEV DT_PATH(test, i2c_11112222, test_i2c_dev_10) +#define TEST_I2C_BUS DT_BUS(TEST_I2C_DEV) + +#define TEST_SPI DT_NODELABEL(test_spi) + +#define TEST_SPI_DEV_0 DT_PATH(test, spi_33334444, test_spi_dev_0) +#define TEST_SPI_BUS_0 DT_BUS(TEST_SPI_DEV_0) + +#define TEST_SPI_DEV_1 DT_PATH(test, spi_33334444, test_spi_dev_1) +#define TEST_SPI_BUS_1 DT_BUS(TEST_SPI_DEV_1) + +#define TEST_SPI_NO_CS DT_NODELABEL(test_spi_no_cs) +#define TEST_SPI_DEV_NO_CS DT_NODELABEL(test_spi_no_cs) + +static void test_bus(void) +{ + /* common prefixes of expected labels: */ + const char *i2c_bus = "TEST_I2C_CTLR"; + const char *i2c_dev = "TEST_I2C_DEV"; + const char *spi_bus = "TEST_SPI_CTLR"; + const char *spi_dev = "TEST_SPI_DEV"; + const char *gpio = "TEST_GPIO_"; + int pin, flags; + + zassert_true(!strcmp(DT_LABEL(TEST_I2C_BUS), "TEST_I2C_CTLR"), "i2c"); + zassert_true(!strcmp(DT_LABEL(TEST_SPI_BUS_0), "TEST_SPI_CTLR"), + "spi 0"); + zassert_true(!strcmp(DT_LABEL(TEST_SPI_BUS_1), "TEST_SPI_CTLR"), + "spi 1"); + + zassert_equal(DT_SPI_DEV_HAS_CS(TEST_SPI_DEV_0), 1, "no cs"); + zassert_equal(DT_SPI_DEV_HAS_CS(TEST_SPI_DEV_NO_CS), 0, "has cs"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_spi_device_2 + /* there is only one instance, and it has no CS */ + zassert_equal(DT_INST_SPI_DEV_HAS_CS(0), 0, + "inst of vnd,spi-device-2 with cs"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_spi_device + /* + * DT_INST_SPI_DEV: use with care here. We could be matching + * either vnd,spi-device. + */ + zassert_equal(DT_INST_SPI_DEV_HAS_CS(0), 1, + "inst of vnd,spi-device without cs"); + + zassert_true(!strncmp(gpio, DT_INST_SPI_DEV_CS_GPIO_LABEL(0), + strlen(gpio)), + "inst 0 cs label"); + + pin = DT_INST_SPI_DEV_CS_GPIO_PIN(0); + zassert_true((pin == 0x10) || (pin == 0x30), "inst 0 cs pin"); + + flags = DT_INST_SPI_DEV_CS_GPIO_FLAGS(0); + zassert_true((flags == 0x20) || (flags == 0x40), "inst 0 cs flags"); + + zassert_equal(DT_ON_BUS(TEST_SPI_DEV_0, spi), 1, "spidev not on spi"); + zassert_equal(DT_ON_BUS(TEST_SPI_DEV_0, i2c), 0, "spidev on i2c"); + + zassert_equal(DT_ON_BUS(TEST_I2C_DEV, i2c), 1, "i2cdev not on i2c"); + zassert_equal(DT_ON_BUS(TEST_I2C_DEV, spi), 0, "i2cdev on spi"); + + zassert_true(!strcmp(DT_BUS_LABEL(TEST_I2C_DEV), "TEST_I2C_CTLR"), + "bus label"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_spi_device + zassert_equal(DT_INST_ON_BUS(0, spi), 1, "spi inst 0 not on spi"); + zassert_equal(DT_INST_ON_BUS(0, i2c), 0, "spi inst 0 on i2c"); + + zassert_equal(DT_ANY_INST_ON_BUS(spi), 1, "no spi is on spi"); + zassert_equal(DT_ANY_INST_ON_BUS(i2c), 0, "a spi is on i2c"); + + zassert_true(!strncmp(spi_dev, DT_INST_LABEL(0), strlen(spi_dev)), + "inst 0 spi dev label"); + zassert_true(!strncmp(spi_bus, DT_INST_BUS_LABEL(0), strlen(spi_bus)), + "inst 0 spi bus label"); + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_i2c_device + zassert_equal(DT_INST_ON_BUS(0, i2c), 1, "i2c inst 0 not on i2c"); + zassert_equal(DT_INST_ON_BUS(0, spi), 0, "i2c inst 0 on spi"); + + zassert_equal(DT_ANY_INST_ON_BUS(i2c), 1, "no i2c is on i2c"); + zassert_equal(DT_ANY_INST_ON_BUS(spi), 0, "an i2c is on spi"); + + zassert_true(!strncmp(i2c_dev, DT_INST_LABEL(0), strlen(i2c_dev)), + "inst 0 i2c dev label"); + zassert_true(!strncmp(i2c_bus, DT_INST_BUS_LABEL(0), strlen(i2c_bus)), + "inst 0 i2c bus label"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_reg_holder +static void test_reg(void) +{ + /* DT_REG_HAS_IDX */ + zassert_true(DT_REG_HAS_IDX(TEST_ABCD1234, 0), "has idx 0"); + zassert_true(DT_REG_HAS_IDX(TEST_ABCD1234, 1), "has idx 1"); + zassert_false(DT_REG_HAS_IDX(TEST_ABCD1234, 2), "!has idx 2"); + + /* DT_REG_ADDR_BY_IDX */ + zassert_equal(DT_REG_ADDR_BY_IDX(TEST_ABCD1234, 0), 0xabcd1234, + "abcd1234 reg[1] addr"); + zassert_equal(DT_REG_ADDR_BY_IDX(TEST_ABCD1234, 1), 0x98765432, + "abcd1234 reg[1] addr"); + + /* DT_REG_SIZE_BY_IDX */ + zassert_equal(DT_REG_SIZE_BY_IDX(TEST_ABCD1234, 0), 0x500, + "abcd1234 reg[1] size"); + zassert_equal(DT_REG_SIZE_BY_IDX(TEST_ABCD1234, 1), 0xff, + "abcd1234 reg[1] size"); + + /* DT_REG_ADDR */ + zassert_equal(DT_REG_ADDR(TEST_ABCD1234), 0xabcd1234, + "abcd1234 reg[0] addr"); + + /* DT_REG_SIZE */ + zassert_equal(DT_REG_SIZE(TEST_ABCD1234), 0x500, + "abcd1234 reg[0] size"); + + /* DT_REG_ADDR_BY_NAME */ + zassert_equal(DT_REG_ADDR_BY_NAME(TEST_ABCD1234, one), 0xabcd1234, + "abcd1234 reg[one] addr"); + zassert_equal(DT_REG_ADDR_BY_NAME(TEST_ABCD1234, two), 0x98765432, + "abcd1234 reg[two] addr"); + + /* DT_REG_SIZE_BY_NAME */ + zassert_equal(DT_REG_SIZE_BY_NAME(TEST_ABCD1234, one), 0x500, + "abcd1234 reg[one] size"); + zassert_equal(DT_REG_SIZE_BY_NAME(TEST_ABCD1234, two), 0xff, + "abcd1234 reg[two] size"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_REG_HAS_IDX */ + zassert_true(DT_INST_REG_HAS_IDX(0, 0), "has idx 0"); + zassert_true(DT_INST_REG_HAS_IDX(0, 1), "has idx 1"); + zassert_false(DT_INST_REG_HAS_IDX(0, 2), "!has idx 2"); + + /* DT_INST_REG_ADDR_BY_IDX */ + zassert_equal(DT_INST_REG_ADDR_BY_IDX(0, 0), 0x9999aaaa, + "abcd1234 reg[1] addr"); + zassert_equal(DT_INST_REG_ADDR_BY_IDX(0, 1), 0xbbbbcccc, + "abcd1234 reg[1] addr"); + + /* DT_INST_REG_SIZE_BY_IDX */ + zassert_equal(DT_INST_REG_SIZE_BY_IDX(0, 0), 0x1000, + "abcd1234 reg[1] size"); + zassert_equal(DT_INST_REG_SIZE_BY_IDX(0, 1), 0x3f, + "abcd1234 reg[1] size"); + + /* DT_INST_REG_ADDR */ + zassert_equal(DT_INST_REG_ADDR(0), 0x9999aaaa, + "abcd1234 reg[0] addr"); + + /* DT_INST_REG_SIZE */ + zassert_equal(DT_INST_REG_SIZE(0), 0x1000, + "abcd1234 reg[0] size"); + + /* DT_INST_REG_ADDR_BY_NAME */ + zassert_equal(DT_INST_REG_ADDR_BY_NAME(0, first), 0x9999aaaa, + "abcd1234 reg[one] addr"); + zassert_equal(DT_INST_REG_ADDR_BY_NAME(0, second), 0xbbbbcccc, + "abcd1234 reg[two] addr"); + + /* DT_INST_REG_SIZE_BY_NAME */ + zassert_equal(DT_INST_REG_SIZE_BY_NAME(0, first), 0x1000, + "abcd1234 reg[one] size"); + zassert_equal(DT_INST_REG_SIZE_BY_NAME(0, second), 0x3f, + "abcd1234 reg[two] size"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_interrupt_holder +static void test_irq(void) +{ + /* DT_NUM_IRQS */ + zassert_equal(DT_NUM_IRQS(TEST_DEADBEEF), 1, "deadbeef num_irqs"); + zassert_equal(DT_NUM_IRQS(TEST_I2C_BUS), 2, "TEST_I2C_BUS num_irqs"); + zassert_equal(DT_NUM_IRQS(TEST_SPI), 3, "TEST_SPI num_irqs"); + + /* DT_IRQ_HAS_IDX */ + zassert_true(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 0), "spi has idx 0"); + zassert_true(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 1), "spi has idx 1"); + zassert_true(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 2), "spi has idx 2"); + zassert_false(DT_IRQ_HAS_IDX(TEST_SPI_BUS_0, 3), "!spi has idx 3"); + + zassert_true(DT_IRQ_HAS_IDX(TEST_DEADBEEF, 0), "deadbeef has idx 0"); + zassert_false(DT_IRQ_HAS_IDX(TEST_DEADBEEF, 1), "!deadbeef has idx 1"); + + zassert_true(DT_IRQ_HAS_IDX(TEST_I2C_BUS, 0), "i2c has idx 0"); + zassert_true(DT_IRQ_HAS_IDX(TEST_I2C_BUS, 1), "i2c has idx 1"); + zassert_false(DT_IRQ_HAS_IDX(TEST_I2C_BUS, 2), "!i2c has idx 2"); + + /* DT_IRQ_BY_IDX */ + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 0, irq), 8, "irq 0"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 1, irq), 9, "irq 1"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 2, irq), 10, "irq 2"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 0, priority), 3, "irq 0 pri"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 1, priority), 0, "irq 1 pri"); + zassert_equal(DT_IRQ_BY_IDX(TEST_SPI_BUS_0, 2, priority), 1, "irq 2 pri"); + + /* DT_IRQ_BY_NAME */ + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, status, irq), 6, "irq status"); + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, error, irq), 7, "irq error"); + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, status, priority), 2, + "irq status pri"); + zassert_equal(DT_IRQ_BY_NAME(TEST_I2C_BUS, error, priority), 1, + "irq error pri"); + + /* DT_IRQ_HAS_CELL_AT_IDX */ + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 0, irq), "no irq 0"); + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 0, priority), + "no irq 0 pri"); + zassert_false(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 0, foo), 0, + "has irq 0 foo"); + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 2, irq), "no irq 2"); + zassert_true(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 2, priority), + "no irq 2 pri"); + zassert_false(DT_IRQ_HAS_CELL_AT_IDX(TEST_IRQ, 2, foo), + "has irq 2 foo"); + + /* DT_IRQ_HAS_CELL */ + zassert_true(DT_IRQ_HAS_CELL(TEST_IRQ, irq), "no irq"); + zassert_true(DT_IRQ_HAS_CELL(TEST_IRQ, priority), "no irq pri"); + zassert_false(DT_IRQ_HAS_CELL(TEST_IRQ, foo), "has irq foo"); + + /* DT_IRQ_HAS_NAME */ + zassert_true(DT_IRQ_HAS_NAME(TEST_IRQ, err), "no err irq"); + zassert_true(DT_IRQ_HAS_NAME(TEST_IRQ, stat), "no stat irq"); + zassert_true(DT_IRQ_HAS_NAME(TEST_IRQ, done), "no done irq"); + zassert_false(DT_IRQ_HAS_NAME(TEST_IRQ, alpha), "no err irq"); + + /* DT_IRQ */ + zassert_equal(DT_IRQ(TEST_I2C_BUS, irq), 6, "irq"); + zassert_equal(DT_IRQ(TEST_I2C_BUS, priority), 2, "irq pri"); + + /* DT_IRQN */ + zassert_equal(DT_IRQN(TEST_I2C_BUS), 6, "irqn"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_IRQ_HAS_IDX */ + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 0), 1, "inst 0 irq 0 missing"); + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 1), 1, "inst 0 irq 1 missing"); + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 2), 1, "inst 0 irq 2 missing"); + zassert_equal(DT_INST_IRQ_HAS_IDX(0, 3), 0, "inst 0 irq 3 present"); + + /* DT_INST_IRQ_BY_IDX */ + zassert_equal(DT_INST_IRQ_BY_IDX(0, 0, irq), 30, "inst 0 irq 0"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 1, irq), 40, "inst 0 irq 1"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 2, irq), 60, "inst 0 irq 2"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 0, priority), 3, "inst 0 irq 0 pri"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 1, priority), 5, "inst 0 irq 1 pri"); + zassert_equal(DT_INST_IRQ_BY_IDX(0, 2, priority), 7, "inst 0 irq 2 pri"); + + /* DT_INST_IRQ_BY_NAME */ + zassert_equal(DT_INST_IRQ_BY_NAME(0, err, irq), 30, "inst 0 irq error"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, stat, irq), 40, "inst 0 irq status"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, done, irq), 60, "inst 0 done error"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, err, priority), 3, + "inst 0 irq error pri"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, stat, priority), 5, + "inst 0 irq status pri"); + zassert_equal(DT_INST_IRQ_BY_NAME(0, done, priority), 7, + "inst 0 irq done pri"); + + /* DT_INST_IRQ */ + zassert_equal(DT_INST_IRQ(0, irq), 30, "inst 0 irq"); + zassert_equal(DT_INST_IRQ(0, priority), 3, "inst 0 irq pri"); + + /* DT_INST_IRQN */ + zassert_equal(DT_INST_IRQN(0), 30, "inst 0 irqn"); + + /* DT_INST_IRQ_HAS_CELL_AT_IDX */ + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 0, irq), "no inst irq 0"); + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 0, priority), + "no inst irq 0 pri"); + zassert_false(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 0, foo), + "has inst irq 0 foo"); + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 2, irq), "no inst irq 2"); + zassert_true(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 2, priority), + "no inst irq 2 pri"); + zassert_false(DT_INST_IRQ_HAS_CELL_AT_IDX(0, 2, foo), + "has inst irq 2 foo"); + + /* DT_INST_IRQ_HAS_CELL */ + zassert_true(DT_INST_IRQ_HAS_CELL(0, irq), "no inst 0 irq"); + zassert_true(DT_INST_IRQ_HAS_CELL(0, priority), "no inst 0 irq pri"); + zassert_false(DT_INST_IRQ_HAS_CELL(0, foo), "has inst 0 irq foo"); + + /* DT_INST_IRQ_HAS_NAME */ + zassert_true(DT_INST_IRQ_HAS_NAME(0, err), "no inst err irq"); + zassert_true(DT_INST_IRQ_HAS_NAME(0, stat), "no inst err irq"); + zassert_true(DT_INST_IRQ_HAS_NAME(0, done), "no inst err irq"); + zassert_false(DT_INST_IRQ_HAS_NAME(0, alpha), "no inst err irq"); + +} + +struct gpios_struct { + const char *name; + gpio_pin_t pin; + gpio_flags_t flags; +}; + +/* Helper macro that UTIL_LISTIFY can use and produces an element with comma */ +#define DT_PROP_ELEM_BY_PHANDLE(idx, node_id, ph_prop, prop) \ + DT_PROP_BY_PHANDLE_IDX(node_id, ph_prop, idx, prop), +#define DT_PHANDLE_LISTIFY(node_id, ph_prop, prop) \ + { \ + UTIL_LISTIFY(DT_PROP_LEN(node_id, ph_prop), \ + DT_PROP_ELEM_BY_PHANDLE, \ + node_id, \ + ph_prop, \ + label) \ + } + +/* Helper macro that UTIL_LISTIFY can use and produces an element with comma */ +#define DT_GPIO_ELEM(idx, node_id, prop) \ + { \ + DT_PROP(DT_PHANDLE_BY_IDX(node_id, prop, idx), label), \ + DT_PHA_BY_IDX(node_id, prop, idx, pin),\ + DT_PHA_BY_IDX(node_id, prop, idx, flags),\ + }, +#define DT_GPIO_LISTIFY(node_id, prop) \ + { UTIL_LISTIFY(DT_PROP_LEN(node_id, prop), DT_GPIO_ELEM, \ + node_id, prop) } + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_phandle_holder +static void test_phandles(void) +{ + const char *ph_label = DT_PROP_BY_PHANDLE(TEST_PH, ph, label); + const char *phs_labels[] = DT_PHANDLE_LISTIFY(TEST_PH, phs, label); + struct gpios_struct gps[] = DT_GPIO_LISTIFY(TEST_PH, gpios); + + /* phandle */ + zassert_true(DT_NODE_HAS_PROP(TEST_PH, ph), "ph"); + /* DT_PROP_BY_PHANDLE */ + zassert_true(!strcmp(ph_label, "TEST_GPIO_1"), "ph label"); + + /* phandles */ + zassert_true(DT_NODE_HAS_PROP(TEST_PH, phs), "phs"); + zassert_equal(ARRAY_SIZE(phs_labels), 3, "phs size"); + zassert_equal(DT_PROP_LEN(TEST_PH, phs), 3, "phs len"); + + /* DT_PROP_BY_PHANDLE_IDX */ + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, phs, 0, label), + "TEST_GPIO_1"), "phs 0"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, phs, 1, label), + "TEST_GPIO_2"), "phs 1"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, phs, 2, label), + "TEST_I2C_CTLR"), "phs 2"); + zassert_true(!strcmp(phs_labels[0], "TEST_GPIO_1"), "phs_labels[0]"); + zassert_true(!strcmp(phs_labels[1], "TEST_GPIO_2"), "phs_labels[1]"); + zassert_true(!strcmp(phs_labels[2], "TEST_I2C_CTLR"), "phs_labels[2]"); + zassert_equal(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 0, + gpio_controller), + 1, + "gpios[0].gpio_controller"); + zassert_equal(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 1, + gpio_controller), + 1, + "gpios[1].gpio_controller"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 0, label), + "TEST_GPIO_1"), + "gpios[0].label"); + zassert_true(!strcmp(DT_PROP_BY_PHANDLE_IDX(TEST_PH, gpios, 1, label), + "TEST_GPIO_2"), + "gpios[1].label"); + + /* phandle-array */ + zassert_true(DT_NODE_HAS_PROP(TEST_PH, gpios), "gpios"); + zassert_equal(ARRAY_SIZE(gps), 2, "gpios size"); + zassert_equal(DT_PROP_LEN(TEST_PH, gpios), 2, "pha len"); + + /* DT_PHA_HAS_IDX */ + zassert_true(DT_PROP_HAS_IDX(TEST_PH, gpios, 0), "has idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_PH, gpios, 1), "has idx 1"); + zassert_false(DT_PROP_HAS_IDX(TEST_PH, gpios, 2), "!has idx 2"); + + /* DT_PHA_HAS_CELL_AT_IDX */ + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, gpios, 1, pin), + "has gpio 1 pin"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, gpios, 1, flags), + "has gpio 1 flags"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 1, pin), + "no pha-gpio 1 pin"); + /* index 1 only has pin, no flags */ + zassert_false(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 1, flags), + "no pha-gpio 1 flags"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 2, pin), + "no pha-gpio 2 pin"); + zassert_true(DT_PHA_HAS_CELL_AT_IDX(TEST_PH, pha_gpios, 2, flags), + "no pha-gpio 2 flags"); + + /* DT_PHA_HAS_CELL */ + zassert_true(DT_PHA_HAS_CELL(TEST_PH, gpios, flags), "has gpio flags"); + zassert_false(DT_PHA_HAS_CELL(TEST_PH, gpios, bar), "no gpio bar"); + + /* DT_PHANDLE_BY_IDX */ + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_IDX(TEST_PH, gpios, 0)), + "TEST_GPIO_1"), + "gpios 0"); + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_IDX(TEST_PH, gpios, 1)), + "TEST_GPIO_2"), + "gpios 1"); + + /* DT_PHANDLE */ + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE(TEST_PH, gpios)), + "TEST_GPIO_1"), + "gpios"); + + /* DT_PHA */ + zassert_equal(DT_PHA(TEST_PH, gpios, pin), 10, "pin 0 implicit"); + zassert_equal(DT_PHA(TEST_PH, gpios, flags), 20, "flags 0 implicit"); + + /* DT_PHA_BY_IDX */ + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 0, pin), 10, "pin 0"); + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 0, flags), 20, "flags 0"); + + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 1, pin), 30, "pin 1"); + zassert_equal(DT_PHA_BY_IDX(TEST_PH, gpios, 1, flags), 40, "flags 1"); + + /* DT_PHA_BY_NAME */ + zassert_equal(DT_PHA_BY_NAME(TEST_PH, foos, a, foocell), 100, + "foocell A"); + zassert_equal(DT_PHA_BY_NAME(TEST_PH, foos, b_c, foocell), 110, + "foocell b-c"); + + /* DT_PHANDLE_BY_NAME */ + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_NAME(TEST_PH, foos, a)), + "TEST_GPIO_1"), + "phandle A"); + zassert_true(!strcmp(DT_LABEL(DT_PHANDLE_BY_NAME(TEST_PH, foos, b_c)), + "TEST_GPIO_2"), + "phandle b-c"); + + /* array initializers */ + zassert_true(!strcmp(gps[0].name, "TEST_GPIO_1"), "gps[0].name"); + zassert_equal(gps[0].pin, 10, "gps[0].pin"); + zassert_equal(gps[0].flags, 20, "gps[0].flags"); + + zassert_true(!strcmp(gps[1].name, "TEST_GPIO_2"), "gps[1].name"); + zassert_equal(gps[1].pin, 30, "gps[1].pin"); + zassert_equal(gps[1].flags, 40, "gps[1].flags"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_PROP_BY_PHANDLE */ + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE(0, ph, label), + "TEST_GPIO_1"), "ph label"); + + zassert_true(!strcmp(ph_label, "TEST_GPIO_1"), "ph label"); + + /* DT_INST_PROP_BY_PHANDLE_IDX */ + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, phs, 0, label), + "TEST_GPIO_1"), "phs 0"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, phs, 1, label), + "TEST_GPIO_2"), "phs 1"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, phs, 2, label), + "TEST_I2C_CTLR"), "phs 2"); + zassert_true(!strcmp(phs_labels[0], "TEST_GPIO_1"), "phs_labels[0]"); + zassert_true(!strcmp(phs_labels[1], "TEST_GPIO_2"), "phs_labels[1]"); + zassert_true(!strcmp(phs_labels[2], "TEST_I2C_CTLR"), "phs_labels[2]"); + zassert_equal(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 0, + gpio_controller), + 1, + "gpios[0].gpio_controller"); + zassert_equal(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 1, + gpio_controller), + 1, + "gpios[1].gpio_controller"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 0, label), + "TEST_GPIO_1"), + "gpios[0].label"); + zassert_true(!strcmp(DT_INST_PROP_BY_PHANDLE_IDX(0, gpios, 1, label), + "TEST_GPIO_2"), + "gpios[1].label"); + +#if 0 + /* DT_INST_PHA_HAS_IDX */ + zassert_true(DT_INST_PROP_HAS_IDX(0, gpios, 0), "has idx 0"); + zassert_true(DT_INST_PROP_HAS_IDX(0, gpios, 1), "has idx 1"); + zassert_false(DT_INST_PROP_HAS_IDX(0, gpios, 2), "!has idx 2"); + + /* DT_INST_PHA_HAS_CELL_AT_IDX */ + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, gpios, 1, pin), + "has gpio 1 pin"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, gpios, 1, flags), + "has gpio 1 flags"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 1, pin), + "no pha-gpio 1 pin"); + /* index 1 only has pin, no flags */ + zassert_false(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 1, flags), + "no pha-gpio 1 flags"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 2, pin), + "no pha-gpio 2 pin"); + zassert_true(DT_INST_PHA_HAS_CELL_AT_IDX(0, pha_gpios, 2, flags), + "no pha-gpio 2 flags"); + + /* DT_INST_PHA_HAS_CELL */ + zassert_true(DT_INST_PHA_HAS_CELL(0, gpios, flags), "has gpio flags"); + zassert_false(DT_INST_PHA_HAS_CELL(0, gpios, bar), "no gpio bar"); +#endif + + /* DT_INST_PHANDLE_BY_IDX */ + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_IDX(0, gpios, 0)), + "TEST_GPIO_1"), + "gpios 0"); + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_IDX(0, gpios, 1)), + "TEST_GPIO_2"), + "gpios 1"); + + /* DT_INST_PHANDLE */ + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE(0, gpios)), + "TEST_GPIO_1"), + "gpios"); + + /* DT_INST_PHA */ + zassert_equal(DT_INST_PHA(0, gpios, pin), 10, "pin 0 implicit"); + zassert_equal(DT_INST_PHA(0, gpios, flags), 20, "flags 0 implicit"); + + /* DT_INST_PHA_BY_IDX */ + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 0, pin), 10, "pin 0"); + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 0, flags), 20, "flags 0"); + + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 1, pin), 30, "pin 1"); + zassert_equal(DT_INST_PHA_BY_IDX(0, gpios, 1, flags), 40, "flags 1"); + + /* DT_INST_PHA_BY_NAME */ + zassert_equal(DT_INST_PHA_BY_NAME(0, foos, a, foocell), 100, + "foocell A"); + zassert_equal(DT_INST_PHA_BY_NAME(0, foos, b_c, foocell), 110, + "foocell b-c"); + + /* DT_INST_PHANDLE_BY_NAME */ + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_NAME(0, foos, a)), + "TEST_GPIO_1"), + "phandle A"); + zassert_true(!strcmp(DT_LABEL(DT_INST_PHANDLE_BY_NAME(0, foos, b_c)), + "TEST_GPIO_2"), + "phandle b-c"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_phandle_holder +static void test_gpio(void) +{ + + /* DT_GPIO_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_GPIO_LABEL_BY_IDX(TEST_PH, gpios, 0), + "TEST_GPIO_1"), + "gpio 0 name idx"); + zassert_true(!strcmp(DT_GPIO_LABEL_BY_IDX(TEST_PH, gpios, 1), + "TEST_GPIO_2"), + "gpio 1 name idx"); + + /* DT_GPIO_LABEL */ + zassert_true(!strcmp(DT_GPIO_LABEL(TEST_PH, gpios), "TEST_GPIO_1"), + "gpio 0 name"); + + /* DT_GPIO_PIN_BY_IDX */ + zassert_equal(DT_GPIO_PIN_BY_IDX(TEST_PH, gpios, 0), 10, "gpio 0 pin idx"); + zassert_equal(DT_GPIO_PIN_BY_IDX(TEST_PH, gpios, 1), 30, "gpio 1 pin idx"); + + /* DT_GPIO_PIN */ + zassert_equal(DT_GPIO_PIN(TEST_PH, gpios), 10, "gpio 0 pin"); + + /* DT_GPIO_FLAGS_BY_IDX */ + zassert_equal(DT_GPIO_FLAGS_BY_IDX(TEST_PH, gpios, 0), + 20, "gpio 0 flags idx"); + zassert_equal(DT_GPIO_FLAGS_BY_IDX(TEST_PH, gpios, 1), + 40, "gpio 1 flags idx"); + + /* DT_GPIO_FLAGS */ + zassert_equal(DT_GPIO_FLAGS(TEST_PH, gpios), 20, "gpio 0 flags"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_GPIO_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_INST_GPIO_LABEL_BY_IDX(0, gpios, 0), + "TEST_GPIO_1"), + "gpio inst 0 name idx"); + zassert_true(!strcmp(DT_INST_GPIO_LABEL_BY_IDX(0, gpios, 1), + "TEST_GPIO_2"), + "gpio inst 1 name idx"); + + /* DT_INST_GPIO_LABEL */ + zassert_true(!strcmp(DT_INST_GPIO_LABEL(0, gpios), "TEST_GPIO_1"), + "gpio inst 0 name"); + + /* DT_INST_GPIO_PIN_BY_IDX */ + zassert_equal(DT_INST_GPIO_PIN_BY_IDX(0, gpios, 0), + 10, "gpio inst 0 pin idx"); + zassert_equal(DT_INST_GPIO_PIN_BY_IDX(0, gpios, 1), + 30, "gpio inst 1 pin idx"); + + /* DT_INST_GPIO_PIN */ + zassert_equal(DT_INST_GPIO_PIN(0, gpios), 10, "gpio inst 0 pin"); + + /* DT_INST_GPIO_FLAGS_BY_IDX */ + zassert_equal(DT_INST_GPIO_FLAGS_BY_IDX(0, gpios, 0), + 20, "gpio inst 0 flags idx"); + zassert_equal(DT_INST_GPIO_FLAGS_BY_IDX(0, gpios, 1), + 40, "gpio inst 1 flags idx"); + + /* DT_INST_GPIO_FLAGS */ + zassert_equal(DT_INST_GPIO_FLAGS(0, gpios), 20, "gpio inst 0 flags"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_adc_temp_sensor +static void test_io_channels(void) +{ + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_IDX(TEST_TEMP, 0), + "TEST_ADC_1"), + "label 0"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_IDX(TEST_TEMP, 1), + "TEST_ADC_2"), + "label 1"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_NAME(TEST_TEMP, ch1), + "TEST_ADC_1"), + "label ch1"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL_BY_NAME(TEST_TEMP, ch2), + "TEST_ADC_2"), + "label ch2"); + zassert_true(!strcmp(DT_IO_CHANNELS_LABEL(TEST_TEMP), + "TEST_ADC_1"), + "label 0 implicit"); + + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_IDX(0, 0), + "TEST_ADC_1"), + "inst label 0"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_IDX(0, 1), + "TEST_ADC_2"), + "inst label 1"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_NAME(0, ch1), + "TEST_ADC_1"), + "inst ch1"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL_BY_NAME(0, ch2), + "TEST_ADC_2"), + "inst ch2"); + zassert_true(!strcmp(DT_INST_IO_CHANNELS_LABEL(0), + "TEST_ADC_1"), + "inst ch1 implicit"); + + zassert_equal(DT_IO_CHANNELS_INPUT_BY_IDX(TEST_TEMP, 0), 10, "input 0"); + zassert_equal(DT_IO_CHANNELS_INPUT_BY_IDX(TEST_TEMP, 1), 20, "input 1"); + zassert_equal(DT_IO_CHANNELS_INPUT_BY_NAME(TEST_TEMP, ch1), 10, + "input ch1"); + zassert_equal(DT_IO_CHANNELS_INPUT_BY_NAME(TEST_TEMP, ch2), 20, + "input ch2"); + zassert_equal(DT_IO_CHANNELS_INPUT(TEST_TEMP), 10, "input 0 implicit"); + + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 0), 10, "input 0"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_IDX(0, 1), 20, "input 1"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_NAME(0, ch1), 10, "input ch1"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT_BY_NAME(0, ch2), 20, "input ch2"); + zassert_equal(DT_INST_IO_CHANNELS_INPUT(0), 10, "input 0 implicit"); +} + +#define TO_STRING(x) TO_STRING_(x) +#define TO_STRING_(x) #x + +static void test_macro_names(void) +{ + /* white box */ + zassert_true(!strcmp(TO_STRING(DT_PATH(test, gpio_deadbeef)), + "DT_N_S_test_S_gpio_deadbeef"), + "path"); + zassert_true(!strcmp(TO_STRING(DT_ALIAS(test_alias)), + "DT_N_S_test_S_gpio_deadbeef"), + "alias"); + zassert_true(!strcmp(TO_STRING(DT_NODELABEL(test_nodelabel)), + "DT_N_S_test_S_gpio_deadbeef"), + "nodelabel"); + zassert_true(!strcmp(TO_STRING(DT_NODELABEL(test_nodelabel_allcaps)), + "DT_N_S_test_S_gpio_deadbeef"), + "nodelabel (all caps)"); +} + +static int a[] = DT_PROP(TEST_ARRAYS, a); +static unsigned char b[] = DT_PROP(TEST_ARRAYS, b); +static char *c[] = DT_PROP(TEST_ARRAYS, c); + +static void test_arrays(void) +{ + zassert_equal(ARRAY_SIZE(a), 3, "a size"); + zassert_equal(ARRAY_SIZE(b), 4, "b size"); + zassert_equal(ARRAY_SIZE(c), 2, "c size"); + + zassert_equal(a[0], 1000, "a[0]"); + zassert_equal(a[1], 2000, "a[1]"); + zassert_equal(a[2], 3000, "a[2]"); + + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 0), "a idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 1), "a idx 1"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 2), "a idx 2"); + zassert_false(DT_PROP_HAS_IDX(TEST_ARRAYS, a, 3), "!a idx 3"); + + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 0), a[0], "a 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 1), a[1], "a 1"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, a, 2), a[2], "a 2"); + + zassert_equal(DT_PROP_LEN(TEST_ARRAYS, a), 3, "a len"); + + zassert_equal(b[0], 0xaa, "b[0]"); + zassert_equal(b[1], 0xbb, "b[1]"); + zassert_equal(b[2], 0xcc, "b[2]"); + zassert_equal(b[3], 0xdd, "b[3]"); + + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 0), "b idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 1), "b idx 1"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 2), "b idx 2"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 3), "b idx 3"); + zassert_false(DT_PROP_HAS_IDX(TEST_ARRAYS, b, 4), "!b idx 4"); + + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 0), b[0], "b 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 1), b[1], "b 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 2), b[2], "b 0"); + zassert_equal(DT_PROP_BY_IDX(TEST_ARRAYS, b, 3), b[3], "b 0"); + + zassert_equal(DT_PROP_LEN(TEST_ARRAYS, b), 4, "b len"); + + zassert_true(!strcmp(c[0], "bar"), "c[0]"); + zassert_true(!strcmp(c[1], "baz"), "c[1]"); + + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, c, 0), "c idx 0"); + zassert_true(DT_PROP_HAS_IDX(TEST_ARRAYS, c, 1), "c idx 1"); + zassert_false(DT_PROP_HAS_IDX(TEST_ARRAYS, c, 2), "!c idx 2"); + + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ARRAYS, c, 0), c[0]), "c 0"); + zassert_true(!strcmp(DT_PROP_BY_IDX(TEST_ARRAYS, c, 1), c[1]), "c 1"); + + zassert_equal(DT_PROP_LEN(TEST_ARRAYS, c), 2, "c len"); +} + +struct test_gpio_info { + u32_t reg_addr; + u32_t reg_len; +}; + +struct test_gpio_data { + bool init_called; + bool is_gpio_ctlr; +}; + +static int test_gpio_init(struct device *dev) +{ + struct test_gpio_data *data = dev->driver_data; + + data->init_called = 1; + return 0; +} + +#define INST(num) DT_INST(num, vnd_gpio) + +static const struct gpio_driver_api test_api; + +#define TEST_GPIO_INIT(num) \ + static struct test_gpio_data gpio_data_##num = { \ + .is_gpio_ctlr = DT_PROP(INST(num), \ + gpio_controller), \ + }; \ + static const struct test_gpio_info gpio_info_##num = { \ + .reg_addr = DT_REG_ADDR(INST(num)), \ + .reg_len = DT_REG_SIZE(INST(num)), \ + }; \ + DEVICE_AND_API_INIT(test_gpio_dev_##num, \ + DT_LABEL(INST(num)), \ + test_gpio_init, \ + &gpio_data_##num, \ + &gpio_info_##num, \ + POST_KERNEL, \ + CONFIG_APPLICATION_INIT_PRIORITY, \ + &test_api) + +#if DT_HAS_NODE(INST(0)) +TEST_GPIO_INIT(0); +#endif +#if DT_HAS_NODE(INST(1)) +TEST_GPIO_INIT(1); +#endif + +static inline struct test_gpio_data *to_data(struct device *dev) +{ + return (struct test_gpio_data *)dev->driver_data; +} + +static inline const struct test_gpio_info *to_info(struct device *dev) +{ + return (struct test_gpio_info *)dev->config->config_info; +} + +static void test_devices(void) +{ + struct device *dev0; + struct device *dev1; + struct device *dev_abcd; + + zassert_true(DT_HAS_NODE(INST(0)), "inst 0 device"); + zassert_true(DT_HAS_NODE(INST(1)), "inst 1 device"); + zassert_false(DT_HAS_NODE(INST(2)), "inst 2 device"); + + zassert_equal(DT_NUM_INST(vnd_gpio), 2, "wrong number of gpio devs"); + + dev0 = device_get_binding(DT_LABEL(INST(0))); + dev1 = device_get_binding(DT_LABEL(INST(1))); + + zassert_not_null(dev0, "null device " DT_LABEL(INST(0))); + zassert_not_null(dev1, "null device " DT_LABEL(INST(1))); + + zassert_true(to_data(dev0)->is_gpio_ctlr, "dev0 not a gpio"); + zassert_true(to_data(dev1)->is_gpio_ctlr, "dev1 not a gpio"); + zassert_true(to_data(dev0)->init_called, "dev0 not initialized"); + zassert_true(to_data(dev1)->init_called, "dev1 not initialized"); + + dev_abcd = device_get_binding(DT_LABEL(TEST_ABCD1234)); + zassert_not_null(dev_abcd, "abcd"); + zassert_equal(to_info(dev_abcd)->reg_addr, 0xabcd1234, "abcd addr"); + zassert_equal(to_info(dev_abcd)->reg_len, 0x500, "abcd len"); +} + +static void test_cs_gpios(void) +{ + zassert_equal(DT_SPI_HAS_CS(TEST_SPI_NO_CS), 0, "unexpected cs"); + zassert_equal(DT_SPI_NUM_CS(TEST_SPI_NO_CS), 0, "wrong no. of cs"); + + zassert_equal(DT_SPI_HAS_CS(TEST_SPI), 1, "missing cs"); + zassert_equal(DT_SPI_NUM_CS(TEST_SPI), 2, "wrong no. of cs"); + + zassert_true(!strcmp(DT_SPI_DEV_CS_GPIO_LABEL(TEST_SPI_DEV_0), + "TEST_GPIO_1"), + "dev 0 cs gpio name"); + zassert_equal(DT_SPI_DEV_CS_GPIO_PIN(TEST_SPI_DEV_0), 0x10, + "dev 0 cs gpio pin"); + zassert_equal(DT_SPI_DEV_CS_GPIO_FLAGS(TEST_SPI_DEV_0), 0x20, + "dev 0 cs gpio flags"); +} + +static void test_chosen(void) +{ + zassert_equal(DT_HAS_CHOSEN(ztest_xxxx), 0, "ztest_xxxx"); + zassert_equal(DT_HAS_CHOSEN(ztest_gpio), 1, "ztest_gpio"); + zassert_true(!strcmp(TO_STRING(DT_CHOSEN(ztest_gpio)), + "DT_N_S_test_S_gpio_deadbeef"), + "chosen"); +} + +static void test_enums(void) +{ + zassert_equal(DT_ENUM_IDX(DT_NODELABEL(test_enum_0), val), 0, "0"); +} + +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT vnd_adc_temp_sensor +static void test_clocks(void) +{ + /* DT_CLOCKS_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_CLOCKS_LABEL_BY_IDX(TEST_TEMP, 0), + "TEST_CLOCK"), + "label 0"); + + /* DT_CLOCKS_LABEL */ + zassert_true(!strcmp(DT_CLOCKS_LABEL(TEST_TEMP), "TEST_CLOCK"), + "label 0"); + + /* DT_CLOCKS_CELL_BY_IDX */ + zassert_equal(DT_CLOCKS_CELL_BY_IDX(TEST_TEMP, bits, 2), 2, + "clk 2 bits"); + zassert_equal(DT_CLOCKS_CELL_BY_IDX(TEST_TEMP, bus, 2), 8, "clk 2 bus"); + + /* DT_CLOCKS_CELL */ + zassert_equal(DT_CLOCKS_CELL(TEST_TEMP, bits), 7, "clk bits"); + zassert_equal(DT_CLOCKS_CELL(TEST_TEMP, bus), 3, "clk bus"); + + /* clock-freq on fixed clock */ + zassert_equal(DT_PROP_BY_PHANDLE_IDX(TEST_TEMP, clocks, 1, + clock_frequency), 25000000, + "fixed clk freq"); + + /* DT_INST */ + zassert_equal(DT_NUM_INST(DT_DRV_COMPAT), 1, "one instance"); + + /* DT_INST_CLOCKS_LABEL_BY_IDX */ + zassert_true(!strcmp(DT_INST_CLOCKS_LABEL_BY_IDX(0, 0), + "TEST_CLOCK"), + "label 0"); + + /* DT_INST_CLOCKS_LABEL */ + zassert_true(!strcmp(DT_INST_CLOCKS_LABEL(0), "TEST_CLOCK"), + "label 0"); + + /* DT_INST_CLOCKS_CELL_BY_IDX */ + zassert_equal(DT_INST_CLOCKS_CELL_BY_IDX(0, bits, 2), 2, + "clk 2 bits"); + zassert_equal(DT_INST_CLOCKS_CELL_BY_IDX(0, bus, 2), 8, "clk 2 bus"); + + /* DT_INST_CLOCKS_CELL */ + zassert_equal(DT_INST_CLOCKS_CELL(0, bits), 7, "clk bits"); + zassert_equal(DT_INST_CLOCKS_CELL(0, bus), 3, "clk bus"); + + /* clock-freq on fixed clock */ + zassert_equal(DT_INST_PROP_BY_PHANDLE_IDX(0, clocks, 1, + clock_frequency), 25000000, + "fixed clk freq"); +} + +void test_main(void) +{ + ztest_test_suite(devicetree_api, + ztest_unit_test(test_path_props), + ztest_unit_test(test_alias_props), + ztest_unit_test(test_nodelabel_props), + ztest_unit_test(test_inst_props), + ztest_unit_test(test_has_path), + ztest_unit_test(test_has_alias), + ztest_unit_test(test_has_inst), + ztest_unit_test(test_has_nodelabel), + ztest_unit_test(test_has_compat), + ztest_unit_test(test_bus), + ztest_unit_test(test_reg), + ztest_unit_test(test_irq), + ztest_unit_test(test_phandles), + ztest_unit_test(test_gpio), + ztest_unit_test(test_io_channels), + ztest_unit_test(test_macro_names), + ztest_unit_test(test_arrays), + ztest_unit_test(test_devices), + ztest_unit_test(test_cs_gpios), + ztest_unit_test(test_chosen), + ztest_unit_test(test_enums), + ztest_unit_test(test_clocks) + ); + ztest_run_test_suite(devicetree_api); +} diff --git a/tests/lib/devicetree/testcase.yaml b/tests/lib/devicetree/testcase.yaml new file mode 100644 index 0000000000000..e558d819d18f0 --- /dev/null +++ b/tests/lib/devicetree/testcase.yaml @@ -0,0 +1,6 @@ +tests: + libraries.devicetree: + tags: devicetree + # We only need this to run on one platform so use native_posix as it + # will mostly likely be the fastest. + platform_whitelist: native_posix