devicetree: add DT ranges public API

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 <narmstrong@baylibre.com>
This commit is contained in:
Neil Armstrong 2021-06-30 17:38:14 +02:00 committed by Christopher Friedt
commit da512fd4d4
2 changed files with 373 additions and 2 deletions

View file

@ -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

View file

@ -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)
/**
* @}
*/