Skip to content

Commit da512fd

Browse files
superna9999cfriedt
authored andcommitted
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 <[email protected]>
1 parent 053e54c commit da512fd

File tree

2 files changed

+373
-2
lines changed

2 files changed

+373
-2
lines changed

doc/reference/devicetree/api.rst

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,31 @@ Property access
4646
===============
4747

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

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

5555
.. doxygengroup:: devicetree-generic-prop
5656

57+
.. _devicetree-ranges-property:
58+
59+
``ranges`` property
60+
===================
61+
62+
Use these APIs instead of :ref:`devicetree-property-access` to access the
63+
``ranges`` property. Because this property's semantics are defined by the
64+
devicetree specification, these macros can be used even for nodes without
65+
matching bindings. However, they take on special semantics when the node's
66+
binding indicates it is a PCIe bus node, as defined in the
67+
`PCI Bus Binding to: IEEE Std 1275-1994 Standard for Boot (Initialization Configuration) Firmware`_
68+
69+
.. _PCI Bus Binding to\: IEEE Std 1275-1994 Standard for Boot (Initialization Configuration) Firmware:
70+
https://www.openfirmware.info/data/docs/bus.pci.pdf
71+
72+
.. doxygengroup:: devicetree-ranges-prop
73+
5774
.. _devicetree-reg-property:
5875

5976
``reg`` property

include/devicetree.h

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,360 @@
12901290
*/
12911291
#define DT_PHANDLE(node_id, prop) DT_PHANDLE_BY_IDX(node_id, prop, 0)
12921292

1293+
/**
1294+
* @}
1295+
*/
1296+
1297+
/**
1298+
* @defgroup devicetree-ranges-prop ranges property
1299+
* @ingroup devicetree
1300+
* @{
1301+
*/
1302+
1303+
/**
1304+
* @brief Get the number of range blocks in the ranges property
1305+
*
1306+
* Use this instead of DT_PROP_LEN(node_id, ranges).
1307+
*
1308+
* Example devicetree fragment:
1309+
*
1310+
* pcie0: pcie@0 {
1311+
* compatible = "intel,pcie";
1312+
* reg = <0 1>;
1313+
* #address-cells = <3>;
1314+
* #size-cells = <2>;
1315+
*
1316+
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
1317+
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
1318+
* <0x3000000 0x80 0 0x80 0 0x80 0>;
1319+
* };
1320+
*
1321+
* other: other@1 {
1322+
* reg = <1 1>;
1323+
*
1324+
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
1325+
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
1326+
* };
1327+
*
1328+
* Example usage:
1329+
*
1330+
* DT_NUM_RANGES(DT_NODELABEL(pcie0)) // 3
1331+
* DT_NUM_RANGES(DT_NODELABEL(other)) // 2
1332+
*
1333+
* @param node_id node identifier
1334+
*/
1335+
#define DT_NUM_RANGES(node_id) DT_CAT(node_id, _RANGES_NUM)
1336+
1337+
/**
1338+
* @brief Is "idx" a valid range block index?
1339+
*
1340+
* If this returns 1, then DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(node_id, idx),
1341+
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(node_id, idx) or
1342+
* DT_RANGES_LENGTH_BY_IDX(node_id, idx) are valid.
1343+
* For DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) the return value
1344+
* of DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(node_id, idx) will indicate
1345+
* validity.
1346+
* If it returns 0, it is an error to use those macros with index "idx",
1347+
* including DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx).
1348+
*
1349+
* Example devicetree fragment:
1350+
*
1351+
*
1352+
* pcie0: pcie@0 {
1353+
* compatible = "intel,pcie";
1354+
* reg = <0 1>;
1355+
* #address-cells = <3>;
1356+
* #size-cells = <2>;
1357+
*
1358+
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
1359+
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
1360+
* <0x3000000 0x80 0 0x80 0 0x80 0>;
1361+
* };
1362+
*
1363+
* other: other@1 {
1364+
* reg = <1 1>;
1365+
*
1366+
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
1367+
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
1368+
* };
1369+
*
1370+
* Example usage:
1371+
*
1372+
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 0) // 1
1373+
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 1) // 1
1374+
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 2) // 1
1375+
* DT_RANGES_HAS_IDX(DT_NODELABEL(pcie0), 3) // 0
1376+
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 0) // 1
1377+
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 1) // 1
1378+
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 2) // 0
1379+
* DT_RANGES_HAS_IDX(DT_NODELABEL(other), 3) // 0
1380+
*
1381+
* @param node_id node identifier
1382+
* @param idx index to check
1383+
* @return 1 if "idx" is a valid register block index,
1384+
* 0 otherwise.
1385+
*/
1386+
#define DT_RANGES_HAS_IDX(node_id, idx) \
1387+
IS_ENABLED(DT_CAT4(node_id, _RANGES_IDX_, idx, _EXISTS))
1388+
1389+
/**
1390+
* @brief Does a ranges property have child bus flags at index?
1391+
*
1392+
* If this returns 1, then DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) is valid.
1393+
* If it returns 0, it is an error to use this macro with index "idx".
1394+
* This macro only returns 1 for PCIe buses (i.e. nodes whose bindings specify they
1395+
* are "pcie" bus nodes.)
1396+
*
1397+
* Example devicetree fragment:
1398+
*
1399+
* parent {
1400+
* #address-cells = <2>;
1401+
*
1402+
* pcie0: pcie@0 {
1403+
* compatible = "intel,pcie";
1404+
* reg = <0 0 1>;
1405+
* #address-cells = <3>;
1406+
* #size-cells = <2>;
1407+
*
1408+
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
1409+
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
1410+
* <0x3000000 0x80 0 0x80 0 0x80 0>;
1411+
* };
1412+
*
1413+
* other: other@1 {
1414+
* reg = <0 1 1>;
1415+
*
1416+
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
1417+
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
1418+
* };
1419+
* };
1420+
*
1421+
* Example usage:
1422+
*
1423+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 0) // 1
1424+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 1) // 1
1425+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 2) // 1
1426+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(pcie0), 3) // 0
1427+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 0) // 0
1428+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 1) // 0
1429+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 2) // 0
1430+
* DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(DT_NODELABEL(other), 3) // 0
1431+
*
1432+
* @param node_id node identifier
1433+
* @param idx logical index into the ranges array
1434+
* @return 1 if "idx" is a valid child bus flags index,
1435+
* 0 otherwise.
1436+
*/
1437+
#define DT_RANGES_HAS_CHILD_BUS_FLAGS_AT_IDX(node_id, idx) \
1438+
IS_ENABLED(DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_CHILD_BUS_FLAGS_EXISTS))
1439+
1440+
/**
1441+
* @brief Get the ranges property child bus flags at index
1442+
*
1443+
* When the node is a PCIe bus, the Child Bus Address has an extra cell used to store some
1444+
* flags, thus this cell is extracted from the Child Bus Address as Child Bus Flags field.
1445+
*
1446+
* Example devicetree fragments:
1447+
*
1448+
* parent {
1449+
* #address-cells = <2>;
1450+
*
1451+
* pcie0: pcie@0 {
1452+
* compatible = "intel,pcie";
1453+
* reg = <0 0 1>;
1454+
* #address-cells = <3>;
1455+
* #size-cells = <2>;
1456+
*
1457+
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
1458+
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
1459+
* <0x3000000 0x80 0 0x80 0 0x80 0>;
1460+
* };
1461+
* };
1462+
*
1463+
* Example usage:
1464+
*
1465+
* DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(DT_NODELABEL(pcie0), 0) // 0x1000000
1466+
* DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x2000000
1467+
* DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x3000000
1468+
*
1469+
* @param node_id node identifier
1470+
* @param idx logical index into the ranges array
1471+
* @returns range child bus flags field at idx
1472+
*/
1473+
#define DT_RANGES_CHILD_BUS_FLAGS_BY_IDX(node_id, idx) \
1474+
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_CHILD_BUS_FLAGS)
1475+
1476+
/**
1477+
* @brief Get the ranges property child bus address at index
1478+
*
1479+
* When the node is a PCIe bus, the Child Bus Address has an extra cell used to store some
1480+
* flags, thus this cell is removed from the Child Bus Address.
1481+
*
1482+
* Example devicetree fragments:
1483+
*
1484+
* parent {
1485+
* #address-cells = <2>;
1486+
*
1487+
* pcie0: pcie@0 {
1488+
* compatible = "intel,pcie";
1489+
* reg = <0 0 1>;
1490+
* #address-cells = <3>;
1491+
* #size-cells = <2>;
1492+
*
1493+
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
1494+
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
1495+
* <0x3000000 0x80 0 0x80 0 0x80 0>;
1496+
* };
1497+
*
1498+
* other: other@1 {
1499+
* reg = <0 1 1>;
1500+
*
1501+
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
1502+
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
1503+
* };
1504+
* };
1505+
*
1506+
* Example usage:
1507+
*
1508+
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 0) // 0
1509+
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x10000000
1510+
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x8000000000
1511+
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 0) // 0
1512+
* DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 1) // 0x10000000
1513+
*
1514+
* @param node_id node identifier
1515+
* @param idx logical index into the ranges array
1516+
* @returns range child bus address field at idx
1517+
*/
1518+
#define DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(node_id, idx) \
1519+
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_CHILD_BUS_ADDRESS)
1520+
1521+
/**
1522+
* @brief Get the ranges property parent bus address at index
1523+
*
1524+
* Similarly to DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(), this properly accounts
1525+
* for child bus flags cells when the node is a PCIe bus.
1526+
*
1527+
* Example devicetree fragment:
1528+
*
1529+
* parent {
1530+
* #address-cells = <2>;
1531+
*
1532+
* pcie0: pcie@0 {
1533+
* compatible = "intel,pcie";
1534+
* reg = <0 0 1>;
1535+
* #address-cells = <3>;
1536+
* #size-cells = <2>;
1537+
*
1538+
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
1539+
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
1540+
* <0x3000000 0x80 0 0x80 0 0x80 0>;
1541+
* };
1542+
*
1543+
* other: other@1 {
1544+
* reg = <0 1 1>;
1545+
*
1546+
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
1547+
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
1548+
* };
1549+
* };
1550+
*
1551+
* Example usage:
1552+
*
1553+
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 0) // 0x3eff0000
1554+
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x10000000
1555+
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x8000000000
1556+
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 0) // 0x3eff0000
1557+
* DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(DT_NODELABEL(other), 1) // 0x10000000
1558+
*
1559+
* @param node_id node identifier
1560+
* @param idx logical index into the ranges array
1561+
* @returns range parent bus address field at idx
1562+
*/
1563+
#define DT_RANGES_PARENT_BUS_ADDRESS_BY_IDX(node_id, idx) \
1564+
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_PARENT_BUS_ADDRESS)
1565+
1566+
/**
1567+
* @brief Get the ranges property length at index
1568+
*
1569+
* Similarly to DT_RANGES_CHILD_BUS_ADDRESS_BY_IDX(), this properly accounts
1570+
* for child bus flags cells when the node is a PCIe bus.
1571+
*
1572+
* Example devicetree fragment:
1573+
*
1574+
* parent {
1575+
* #address-cells = <2>;
1576+
*
1577+
* pcie0: pcie@0 {
1578+
* compatible = "intel,pcie";
1579+
* reg = <0 0 1>;
1580+
* #address-cells = <3>;
1581+
* #size-cells = <2>;
1582+
*
1583+
* ranges = <0x1000000 0 0 0 0x3eff0000 0 0x10000>,
1584+
* <0x2000000 0 0x10000000 0 0x10000000 0 0x2eff0000>,
1585+
* <0x3000000 0x80 0 0x80 0 0x80 0>;
1586+
* };
1587+
*
1588+
* other: other@1 {
1589+
* reg = <0 1 1>;
1590+
*
1591+
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
1592+
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
1593+
* };
1594+
* };
1595+
*
1596+
* Example usage:
1597+
*
1598+
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(pcie0), 0) // 0x10000
1599+
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(pcie0), 1) // 0x2eff0000
1600+
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(pcie0), 2) // 0x8000000000
1601+
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(other), 0) // 0x10000
1602+
* DT_RANGES_LENGTH_BY_IDX(DT_NODELABEL(other), 1) // 0x2eff0000
1603+
*
1604+
* @param node_id node identifier
1605+
* @param idx logical index into the ranges array
1606+
* @returns range length field at idx
1607+
*/
1608+
#define DT_RANGES_LENGTH_BY_IDX(node_id, idx) \
1609+
DT_CAT4(node_id, _RANGES_IDX_, idx, _VAL_LENGTH)
1610+
1611+
/**
1612+
* @brief Invokes "fn" for each entry of "node_id" ranges property
1613+
*
1614+
* The macro "fn" must take two parameters, "node_id" which will be the node
1615+
* identifier of the node with the ranges property and "idx" the index of
1616+
* the ranges block.
1617+
*
1618+
* Example devicetree fragment:
1619+
*
1620+
* n: node@0 {
1621+
* reg = <0 0 1>;
1622+
*
1623+
* ranges = <0x0 0x0 0x0 0x3eff0000 0x10000>,
1624+
* <0x0 0x10000000 0x0 0x10000000 0x2eff0000>;
1625+
* };
1626+
*
1627+
* Example usage:
1628+
*
1629+
* #define RANGE_LENGTH(node_id, idx) DT_RANGES_LENGTH_BY_IDX(node_id, idx),
1630+
*
1631+
* const uint64_t *ranges_length[] = {
1632+
* DT_FOREACH_RANGE(DT_NODELABEL(n), RANGE_LENGTH)
1633+
* };
1634+
*
1635+
* This expands to:
1636+
*
1637+
* const char *ranges_length[] = {
1638+
* 0x10000, 0x2eff0000,
1639+
* };
1640+
*
1641+
* @param node_id node identifier
1642+
* @param fn macro to invoke
1643+
*/
1644+
#define DT_FOREACH_RANGE(node_id, fn) \
1645+
DT_CAT(node_id, _FOREACH_RANGE)(fn)
1646+
12931647
/**
12941648
* @}
12951649
*/

0 commit comments

Comments
 (0)