Skip to content

Commit 2bab58e

Browse files
committed
scripts: dts: edtlib: Handle *-map property
Add `_map_phandle_val_list()` to handle `*-map` properties. Since `*-map` properties are made able to be treated as a variation of phandle-array, assign sequential cell names for each group of specifiers (child_specifier_0, child_specifier_1, ..., parent_specifier_0, ...). Signed-off-by: TOKITA Hiroshi <[email protected]>
1 parent 1aa7679 commit 2bab58e

File tree

5 files changed

+199
-5
lines changed

5 files changed

+199
-5
lines changed

scripts/dts/python-devicetree/src/devicetree/edtlib.py

+107-5
Original file line numberDiff line numberDiff line change
@@ -1499,9 +1499,8 @@ def _init_prop(self, prop_spec: PropertySpec,
14991499
"is different from the 'const' value specified in "
15001500
f"{self.binding_path} ({const!r})")
15011501

1502-
# Skip properties that start with '#', like '#size-cells', and mapping
1503-
# properties like 'gpio-map'/'interrupt-map'
1504-
if name[0] == "#" or name.endswith("-map"):
1502+
# Skip properties that start with '#', like '#size-cells'
1503+
if name[0] == "#":
15051504
return
15061505

15071506
self.props[name] = Property(prop_spec, val, self)
@@ -1598,7 +1597,10 @@ def _prop_val(
15981597
f"with '{name} = < &foo ... &bar 1 ... &baz 2 3 >' "
15991598
f"(a mix of phandles and numbers), not '{prop}'")
16001599

1601-
return self._standard_phandle_val_list(prop, specifier_space)
1600+
if prop.name.endswith("-map"):
1601+
return self._map_phandle_val_list(prop, specifier_space)
1602+
else:
1603+
return self._standard_phandle_val_list(prop, specifier_space)
16021604

16031605
if prop_type == "path":
16041606
return self.edt._node2enode[prop.to_path()]
@@ -1773,6 +1775,105 @@ def _init_interrupts(self) -> None:
17731775

17741776
_add_names(node, "interrupt", self.interrupts)
17751777

1778+
def _map_phandle_val_list(
1779+
self, prop: dtlib_Property, specifier_space: Optional[str]
1780+
) -> list[Optional[ControllerAndData]]:
1781+
if not specifier_space:
1782+
specifier_space = prop.name[:-4] # Strip '-map'
1783+
1784+
res: list[Optional[ControllerAndData]] = []
1785+
1786+
raw = prop.value
1787+
while raw:
1788+
if len(raw) < 4:
1789+
# Not enough room for phandle
1790+
_err("bad value for " + repr(prop))
1791+
1792+
def count_cells_num(node: dtlib_Node, specifier: str) -> int:
1793+
"""
1794+
Calculate the number of cells in the node.
1795+
When calculating the number of interrupt cells,
1796+
add up the values of the address and size cells.
1797+
"""
1798+
1799+
if not node:
1800+
_err("node is None.")
1801+
1802+
num = node.props[f"#{specifier}-cells"].to_num()
1803+
1804+
if specifier == "interrupt":
1805+
parent_props = None
1806+
if node.parent:
1807+
parent_props = node.parent.props
1808+
1809+
if "#address-cells" in node.props:
1810+
num = num + node.props["#address-cells"].to_num()
1811+
elif parent_props and "#address-cells" in parent_props:
1812+
num = num + parent_props["#address-cells"].to_num()
1813+
else:
1814+
_err("Neither the node nor its parent has `#address-cells` property")
1815+
1816+
if "#size-cells" in node.props:
1817+
num = num + node.props["#size-cells"].to_num()
1818+
elif parent_props and "#address-cells" in parent_props:
1819+
num = num + parent_props["#size-cells"].to_num()
1820+
else:
1821+
_err("Neither the node nor its parent has `#size-cells` property")
1822+
1823+
return num
1824+
1825+
child_specifier_num = count_cells_num(prop.node, specifier_space)
1826+
1827+
child_specifiers = to_nums(raw[: 4 * child_specifier_num])
1828+
raw = raw[4 * child_specifier_num :]
1829+
phandle = to_num(raw[:4])
1830+
raw = raw[4:]
1831+
1832+
controller_node: dtlib_Node = prop.node.dt.phandle2node.get(phandle)
1833+
if not controller_node:
1834+
_err(f"controller node cannot be found from phandle:{phandle}")
1835+
1836+
parent_specifier_num = count_cells_num(controller_node, specifier_space)
1837+
parent_specifiers = to_nums(raw[: 4 * parent_specifier_num])
1838+
raw = raw[4 * parent_specifier_num :]
1839+
1840+
# Although this is rare, if a cell-name is specified for the map node,
1841+
# it will be reflected.
1842+
# If not specified, the name of child_specifier_[i] will be set.
1843+
values: dict[str, int] = {}
1844+
for i, v in enumerate(child_specifiers):
1845+
cell_name = f"child_specifier_{i}"
1846+
if specifier_space in self._binding.specifier2cells and (
1847+
i < len(self._binding.specifier2cells)
1848+
):
1849+
cell_name = self._bindings.specifier2cells[i]
1850+
1851+
values[cell_name] = v
1852+
1853+
# The cell name for parent_specifier cannot be determined.
1854+
# For convenience, we assign it the name parent_specifier_[i].
1855+
for i, v in enumerate(parent_specifiers):
1856+
values[f"parent_specifier_{i}"] = v
1857+
1858+
controller: Node = self.edt._node2enode[controller_node]
1859+
if not controller:
1860+
_err("controller cannot be found from: " + repr(controller_node))
1861+
1862+
res.append(
1863+
ControllerAndData(
1864+
node=self,
1865+
controller=controller,
1866+
data=values,
1867+
name=None,
1868+
basename=specifier_space,
1869+
)
1870+
)
1871+
1872+
if len(raw) != 0:
1873+
_err(f"unexpected prop.value remainings: {raw}")
1874+
1875+
return res
1876+
17761877
def _standard_phandle_val_list(
17771878
self,
17781879
prop: dtlib_Property,
@@ -2717,9 +2818,10 @@ def _check_prop_by_type(prop_name: str,
27172818

27182819
if (prop_type == "phandle-array"
27192820
and not prop_name.endswith("s")
2821+
and not prop_name.endswith("-map")
27202822
and "specifier-space" not in options):
27212823
_err(f"'{prop_name}' in 'properties:' in {binding_path} "
2722-
f"has type 'phandle-array' and its name does not end in 's', "
2824+
f"has type 'phandle-array' and its name does not end in 's' or '-map', "
27232825
f"but no 'specifier-space' was provided.")
27242826

27252827
# If you change const_types, be sure to update the type annotation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) 2025 TOKITA Hiroshi
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
compatible: "gpio-nexus"
5+
6+
description: gpio-nexus definition
7+
8+
properties:
9+
gpio-map:
10+
type: phandle-array
11+
required: true
12+
13+
gpio-map-mask:
14+
type: array
15+
16+
gpio-map-pass-thru:
17+
type: array
18+
19+
"#gpio-cells":
20+
type: int
21+
required: true
22+
description: Number of items to expect in a gpio specifier
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright (c) 2025 TOKITA Hiroshi
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
compatible: "interrupt-nexus"
5+
6+
description: interrupt-nexus definition
7+
8+
properties:
9+
interrupt-map:
10+
type: phandle-array
11+
required: true
12+
13+
interrupt-map-mask:
14+
type: array
15+
16+
interrupt-map-pass-thru:
17+
type: array
18+
19+
"#interrupt-cells":
20+
type: int
21+
required: true
22+
description: Number of items to expect in a interrupt specifier

scripts/dts/python-devicetree/tests/test.dts

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
interrupt-controller;
7272
};
7373
nexus {
74+
compatible = "interrupt-nexus";
7475
#interrupt-cells = <2>;
7576
interrupt-map = <
7677
0 0 0 0 &{/interrupt-map-test/controller-0} 0 0
@@ -104,6 +105,7 @@
104105
interrupt-controller;
105106
};
106107
nexus {
108+
compatible = "interrupt-nexus";
107109
#interrupt-cells = <2>;
108110
interrupt-map = <
109111
6 6 6 6 &{/interrupt-map-bitops-test/controller} 2 1
@@ -399,6 +401,7 @@
399401
&{/gpio-map/connector} 1 2>;
400402
};
401403
connector {
404+
compatible = "gpio-nexus";
402405
#gpio-cells = <2>;
403406
// Use different data lengths for source and
404407
// destination to make it a bit trickier

scripts/dts/python-devicetree/tests/test_edtlib.py

+45
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,51 @@ def verify_regs(node, expected_tuples):
209209
verify_regs(edt.get_node("/reg-nested-ranges/grandparent/parent/node"),
210210
[(None, 0x30000000200000001, 0x1)])
211211

212+
def test_map():
213+
'''Tests for map properties'''
214+
with from_here():
215+
edt = edtlib.EDT("test.dts", ["test-bindings"])
216+
217+
def verify_maps(
218+
node,
219+
specifier,
220+
expected_maps,
221+
expected_child_cell,
222+
expected_child_addr,
223+
expected_parent_cells,
224+
expected_parent_addrs,
225+
):
226+
227+
maps = []
228+
for d in node.props[specifier +'-map'].val:
229+
parent_specs = len([k for k in d.data if k.startswith('parent_specifier_')])
230+
maps.extend(list(d.data.values())[:-parent_specs])
231+
maps.extend([d.controller.path])
232+
maps.extend(list(d.data.values())[-parent_specs:])
233+
234+
assert maps == expected_maps
235+
236+
verify_maps(edt.get_node("/interrupt-map-test/nexus"), specifier="interrupt",
237+
expected_maps=[0, 0, 0, 0, "/interrupt-map-test/controller-0", 0, 0,
238+
0, 0, 0, 1, "/interrupt-map-test/controller-1", 0, 0, 0, 1,
239+
0, 0, 0, 2, "/interrupt-map-test/controller-2", 0, 0, 0, 0, 0, 2,
240+
0, 1, 0, 0, "/interrupt-map-test/controller-0", 0, 3,
241+
0, 1, 0, 1, "/interrupt-map-test/controller-1", 0, 0, 0, 4,
242+
0, 1, 0, 2, "/interrupt-map-test/controller-2", 0, 0, 0, 0, 0, 5],
243+
expected_child_cell=2, expected_child_addr=2,
244+
expected_parent_cells=[1, 2, 3, 1, 2, 3], expected_parent_addrs=[1, 2, 3, 1, 2, 3])
245+
246+
verify_maps(edt.get_node("/interrupt-map-bitops-test/nexus"), specifier="interrupt",
247+
expected_maps=[6, 6, 6, 6, "/interrupt-map-bitops-test/controller", 2, 1],
248+
expected_child_cell=2, expected_child_addr=2,
249+
expected_parent_cells=[2], expected_parent_addrs=[0])
250+
251+
verify_maps(edt.get_node("/gpio-map/connector"), specifier="gpio",
252+
expected_maps=[1, 2, "/gpio-map/destination", 5,
253+
3, 4, "/gpio-map/destination", 6],
254+
expected_child_cell=2, expected_child_addr=0,
255+
expected_parent_cells=[1, 1], expected_parent_addrs=[0, 0])
256+
212257
def test_pinctrl():
213258
'''Test 'pinctrl-<index>'.'''
214259
with from_here():

0 commit comments

Comments
 (0)