Skip to content

Commit

Permalink
devicetree: add DT ranges public API
Browse files Browse the repository at this point in the history
This adds the macro public API to handle the DT ranges properties.

This also updates the devicetree/api.rst documentation.

Signed-off-by: Neil Armstrong <[email protected]>
  • Loading branch information
superna9999 authored and cfriedt committed Nov 10, 2021
1 parent 053e54c commit da512fd
Show file tree
Hide file tree
Showing 2 changed files with 373 additions and 2 deletions.
21 changes: 19 additions & 2 deletions doc/reference/devicetree/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,31 @@ Property access
===============

The following general-purpose macros can be used to access node properties.
There are special-purpose APIs for accessing the :ref:`devicetree-reg-property`
and :ref:`devicetree-interrupts-property`.
There are special-purpose APIs for accessing the :ref:`devicetree-ranges-property`,
:ref:`devicetree-reg-property` and :ref:`devicetree-interrupts-property`.

Property values can be read using these macros even if the node is disabled,
as long as it has a matching binding.

.. doxygengroup:: devicetree-generic-prop

.. _devicetree-ranges-property:

``ranges`` property
===================

Use these APIs instead of :ref:`devicetree-property-access` to access the
``ranges`` property. Because this property's semantics are defined by the
devicetree specification, these macros can be used even for nodes without
matching bindings. However, they take on special semantics when the node's
binding indicates it is a PCIe bus node, as defined in the
`PCI Bus Binding to: IEEE Std 1275-1994 Standard for Boot (Initialization Configuration) Firmware`_

.. _PCI Bus Binding to\: IEEE Std 1275-1994 Standard for Boot (Initialization Configuration) Firmware:
https://www.openfirmware.info/data/docs/bus.pci.pdf

.. doxygengroup:: devicetree-ranges-prop

.. _devicetree-reg-property:

``reg`` property
Expand Down
354 changes: 354 additions & 0 deletions include/devicetree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1290,6 +1290,360 @@
*/
#define DT_PHANDLE(node_id, prop) DT_PHANDLE_BY_IDX(node_id, prop, 0)

/**
* @}
*/

/**
* @defgroup devicetree-ranges-prop ranges property
* @ingroup devicetree
* @{
*/

/**
* @brief Get the number of range blocks in the ranges property
*
* Use this instead of DT_PROP_LEN(node_id, ranges).
*
* Example devicetree fragment:
*
* pcie0: pcie@0 {
* compatible = "intel,pcie";
* reg = <0 1>;
* #address-cells = <3>;
* #size-cells = <2>;
*
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
* <0x3000000 0x80 0 0x80 0 0x80 0>;
* };
*
* other: other@1 {
* reg = <1 1>;
*
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
* };
*
* Example usage:
*
* DT_NUM_RANGES(DT_NODELABEL(pcie0)) // 3
* DT_NUM_RANGES(DT_NODELABEL(other)) // 2
*
* @param node_id node identifier
*/
#define DT_NUM_RANGES(node_id) DT_CAT(node_id, _RANGES_NUM)

/**
* @brief Is "idx" a valid range block index?
*
* If this returns 1, then DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(node_id, idx),
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(node_id, idx) or
* DT_RANGES_LENGTH_BY_IDX(node_id, idx) are valid.
* For DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) the return value
* of DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(node_id, idx) will indicate
* validity.
* If it returns 0, it is an error to use those macros with index "idx",
* including DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx).
*
* Example devicetree fragment:
*
*
* pcie0: pcie@0 {
* compatible = "intel,pcie";
* reg = <0 1>;
* #address-cells = <3>;
* #size-cells = <2>;
*
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
* <0x3000000 0x80 0 0x80 0 0x80 0>;
* };
*
* other: other@1 {
* reg = <1 1>;
*
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
* };
*
* Example usage:
*
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 0) // 1
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 1) // 1
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 2) // 1
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 3) // 0
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 0) // 1
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 1) // 1
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 2) // 0
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 3) // 0
*
* @param node_id node identifier
* @param idx index to check
* @return 1 if "idx" is a valid register block index,
* 0 otherwise.
*/
#define DT_RANGES_HAS_IDX(node_id, idx) \
IS_ENABLED(DT_CAT4(node_id, _RANGES_IDX_, idx, _EXISTS))

/**
* @brief Does a ranges property have child bus flags at index?
*
* If this returns 1, then DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) is valid.
* If it returns 0, it is an error to use this macro with index "idx".
* This macro only returns 1 for PCIe buses (i.e. nodes whose bindings specify they
* are "pcie" bus nodes.)
*
* Example devicetree fragment:
*
* parent {
* #address-cells = <2>;
*
* pcie0: pcie@0 {
* compatible = "intel,pcie";
* reg = <0 0 1>;
* #address-cells = <3>;
* #size-cells = <2>;
*
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
* <0x3000000 0x80 0 0x80 0 0x80 0>;
* };
*
* other: other@1 {
* reg = <0 1 1>;
*
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
* };
* };
*
* Example usage:
*
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 0) // 1
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 1) // 1
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 2) // 1
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 3) // 0
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 0) // 0
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 1) // 0
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 2) // 0
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 3) // 0
*
* @param node_id node identifier
* @param idx logical index into the ranges array
* @return 1 if "idx" is a valid child bus flags index,
* 0 otherwise.
*/
#define DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(node_id, idx) \
IS_ENABLED(DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_CHILD_BUS_FLAGS_EXISTS))

/**
* @brief Get the ranges property child bus flags at index
*
* When the node is a PCIe bus, the Child Bus Address has an extra cell used to store some
* flags, thus this cell is extracted from the Child Bus Address as Child Bus Flags field.
*
* Example devicetree fragments:
*
* parent {
* #address-cells = <2>;
*
* pcie0: pcie@0 {
* compatible = "intel,pcie";
* reg = <0 0 1>;
* #address-cells = <3>;
* #size-cells = <2>;
*
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
* <0x3000000 0x80 0 0x80 0 0x80 0>;
* };
* };
*
* Example usage:
*
* DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(DT_NODELABEL(pcie0), 0) // 0x1000000
* DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x2000000
* DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x3000000
*
* @param node_id node identifier
* @param idx logical index into the ranges array
* @returns range child bus flags field at idx
*/
#define DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) \
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_CHILD_BUS_FLAGS)

/**
* @brief Get the ranges property child bus address at index
*
* When the node is a PCIe bus, the Child Bus Address has an extra cell used to store some
* flags, thus this cell is removed from the Child Bus Address.
*
* Example devicetree fragments:
*
* parent {
* #address-cells = <2>;
*
* pcie0: pcie@0 {
* compatible = "intel,pcie";
* reg = <0 0 1>;
* #address-cells = <3>;
* #size-cells = <2>;
*
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
* <0x3000000 0x80 0 0x80 0 0x80 0>;
* };
*
* other: other@1 {
* reg = <0 1 1>;
*
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
* };
* };
*
* Example usage:
*
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 0) // 0
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x10000000
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x8000000000
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 0) // 0
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 1) // 0x10000000
*
* @param node_id node identifier
* @param idx logical index into the ranges array
* @returns range child bus address field at idx
*/
#define DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(node_id, idx) \
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_CHILD_BUS_ADDRESS)

/**
* @brief Get the ranges property parent bus address at index
*
* Similarly to DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(), this properly accounts
* for child bus flags cells when the node is a PCIe bus.
*
* Example devicetree fragment:
*
* parent {
* #address-cells = <2>;
*
* pcie0: pcie@0 {
* compatible = "intel,pcie";
* reg = <0 0 1>;
* #address-cells = <3>;
* #size-cells = <2>;
*
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
* <0x3000000 0x80 0 0x80 0 0x80 0>;
* };
*
* other: other@1 {
* reg = <0 1 1>;
*
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
* };
* };
*
* Example usage:
*
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 0) // 0x3eff0000
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x10000000
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x8000000000
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 0) // 0x3eff0000
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 1) // 0x10000000
*
* @param node_id node identifier
* @param idx logical index into the ranges array
* @returns range parent bus address field at idx
*/
#define DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(node_id, idx) \
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_PARENT_BUS_ADDRESS)

/**
* @brief Get the ranges property length at index
*
* Similarly to DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(), this properly accounts
* for child bus flags cells when the node is a PCIe bus.
*
* Example devicetree fragment:
*
* parent {
* #address-cells = <2>;
*
* pcie0: pcie@0 {
* compatible = "intel,pcie";
* reg = <0 0 1>;
* #address-cells = <3>;
* #size-cells = <2>;
*
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
* <0x3000000 0x80 0 0x80 0 0x80 0>;
* };
*
* other: other@1 {
* reg = <0 1 1>;
*
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
* };
* };
*
* Example usage:
*
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(pcie0), 0) // 0x10000
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x2eff0000
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x8000000000
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(other), 0) // 0x10000
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(other), 1) // 0x2eff0000
*
* @param node_id node identifier
* @param idx logical index into the ranges array
* @returns range length field at idx
*/
#define DT_RANGES_LENGTH_BY_IDX(node_id, idx) \
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_LENGTH)

/**
* @brief Invokes "fn" for each entry of "node_id" ranges property
*
* The macro "fn" must take two parameters, "node_id" which will be the node
* identifier of the node with the ranges property and "idx" the index of
* the ranges block.
*
* Example devicetree fragment:
*
* n: node@0 {
* reg = <0 0 1>;
*
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
* };
*
* Example usage:
*
* #define RANGE_LENGTH(node_id, idx) DT_RANGES_LENGTH_BY_IDX(node_id, idx),
*
* const uint64_t *ranges_length[] = {
* DT_FOREACH_RANGE(DT_NODELABEL(n), RANGE_LENGTH)
* };
*
* This expands to:
*
* const char *ranges_length[] = {
* 0x10000, 0x2eff0000,
* };
*
* @param node_id node identifier
* @param fn macro to invoke
*/
#define DT_FOREACH_RANGE(node_id, fn) \
DT_CAT(node_id, _FOREACH_RANGE)(fn)

/**
* @}
*/
Expand Down

0 comments on commit da512fd

Please sign in to comment.