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

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