[PATCH v3 04/11] dtc: dt-check-style: Handle sorting of top-level nodes and properties
Krzysztof Kozlowski <[email protected]>
| Newsgroups | org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260803-n-dts-style-checker-continued-v3-4-6c9776928cea@oss.qualcomm.com> |
Top-level DTS (but not example in the bindings) has only two nodes with unit-addresses: memory@ and soc@. There are two special cases here, in terms of coding style: 1. The unit-address of memory is often not known thus set to @0, because it is filled up by bootloader. 2. There is mixture of non-unit-address and unit-address nodes. Therefore usually the DTS chooses for the top-level part sorting by the node name, not the unit address. Also the properties have one exception: 'model' property is supposed to be before the 'compatible'. This cannot be applied to the entire DTS, because sound cards have also 'model' where it is supposed to follow standard rules (after the 'compatible'). Root node is just special. Signed-off-by: Krzysztof Kozlowski <[email protected]> --- scripts/dtc/dt-check-style | 60 +++++++++++++++++++--- .../bad/dts-child-name-order.dtso | 33 ++++++++++++ .../dt-style-selftest/bad/dts-digit-node-order.dts | 40 +++++++++++++++ .../bad/dts-digit-node-order.dtso | 41 +++++++++++++++ .../dt-style-selftest/bad/dts-property-order.dts | 5 ++ .../expected/dts-child-name-order.dts.txt | 1 + .../expected/dts-child-name-order.dtso.txt | 3 ++ .../expected/dts-digit-node-order.dts.txt | 2 + .../expected/dts-digit-node-order.dtso.txt | 2 + .../expected/dts-property-order.dts.txt | 13 +++-- .../expected/dts-property-order.dtso.txt | 1 + .../good/dts-child-name-order.dtso | 33 ++++++++++++ .../good/dts-digit-node-order.dts | 3 -- .../good/dts-digit-node-order.dtso | 41 +++++++++++++++ .../dt-style-selftest/good/dts-property-order.dts | 5 ++ .../dt-style-selftest/good/dts-property-order.dtso | 47 +++++++++++++++++ 16 files changed, 315 insertions(+), 15 deletions(-) diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style index d9f76d6d9179..f9e5762862a5 100755 --- a/scripts/dtc/dt-check-style +++ b/scripts/dtc/dt-check-style @@ -79,7 +79,7 @@ def is_preprocessor(stripped): class DtsLine: __slots__ = ('lineno', 'raw', 'linetype', 'indent_str', 'stripped', 'is_root', 'prop_name', 'continuations', - 'node_name', 'node_addr', 'label', 'ref_name', 'depth', + 'node_name', 'node_addr', 'label', 'ref_name', 'parent', 'depth', 'closures') def __init__(self, lineno, raw, linetype, depth, indent_str, stripped, is_root = False): @@ -96,6 +96,7 @@ class DtsLine: self.node_addr = None self.label = None self.ref_name = None + self.parent = None # DtsLine of parent node self.closures = 1 # count of '}' on a NODE_CLOSE line @@ -495,16 +496,24 @@ def _walk_bodies(lines): """Yield lists of immediate-child NODE_OPEN lines for each node body in the input. Skips ref-nodes (&label) since those don't have an intrinsic ordering.""" + # Array of stacked nodes (parent/child) body_stack = [[]] + # Current stack of nodes, purely to track parent relationship for each node + node_stack = [] + parent_dl = None for dl in lines: if dl.linetype == LineType.NODE_OPEN: + dl.parent = parent_dl + node_stack.append(dl) body_stack[-1].append(dl) body_stack.append([]) + parent_dl = dl continue if dl.linetype == LineType.NODE_CLOSE: if len(body_stack) <= 1: # Unbalanced; ignore to avoid crashing on malformed input continue + parent_dl = node_stack.pop().parent yield body_stack.pop() continue while body_stack: @@ -525,12 +534,18 @@ def _natural_sort_key(s): def check_child_address_order(ctx): """Addressed siblings (foo@N) must appear in ascending address - order within their parent node body.""" + order within their parent node body. + Exception: Top-level in DTS follows name order, regardless of unit address + in memory@N and soc@N nodes + """ for children in _walk_bodies(ctx.lines): addressed = [] for c in children: if c.node_addr is None: continue + if c.parent and c.parent.is_root: + # Top-level does not use unit address sorting usually + continue try: parts = tuple(int(p, 16) for p in c.node_addr.split(',')) except ValueError: @@ -548,12 +563,16 @@ def check_child_name_order(ctx): """Unaddressed siblings must appear in natural-sort order by node name within their parent node body. Addressed children are scoped by check_child_address_order; reference nodes (&label { ... }) and - the root node are skipped.""" + the root node are skipped. + However root node has children with and without unit address, and + sorting should be only by name.""" for children in _walk_bodies(ctx.lines): unaddressed = [] for c in children: if c.node_addr is not None: - continue + # Skip nodes with unit address, except when sorting top-level + if not c.parent or not c.parent.is_root: + continue if c.node_name is None: continue if c.is_root: @@ -597,6 +616,30 @@ def _property_bucket(name): return (5 if ',' in stripped else 4, None) +def _property_bucket_root(name): + """Return the canonical bucket index for a property: + 0 model (for root nodes only) + 1 compatible + Plus a sub-key inside the bucket for fixed slots (device_type, compatible, + reg, reg-names, ranges, status). 'standard' and 'vendor' return None for + the sub-key, signalling that the within-bucket key is computed by + the pairing rules.""" + stripped = name.lstrip('#') + if name == 'model': + return (0, 0) + if name == 'compatible': + return (1, 0) + if name == 'reg': + return (2, 0) + if name == 'reg-names': + return (2, 1) + if name == 'ranges': + return (3, 0) + if name == 'status': + return (6, 0) + return (5 if ',' in stripped else 4, None) + + # Declarative pairing rules: each is a callable # (name, all_names) -> anchor_name_or_None # If a rule returns an anchor, the property sorts immediately after the @@ -633,13 +676,16 @@ def _pair_x_names(name, all_names): PAIRING_RULES = (_pair_pinctrl_names, _pair_x_names) -def _property_sort_key(name, all_names): +def _property_sort_key(dl, name, all_names): """Sort key for a property among its node-body siblings. Format: (bucket, within_key, tiebreak). 'within_key' for standard/vendor buckets follows pairing rules: a property paired with anchor X sorts as if it were X with a higher tiebreak.""" - bucket, fixed_sub = _property_bucket(name) + if dl.is_root: + bucket, fixed_sub = _property_bucket_root(name) + else: + bucket, fixed_sub = _property_bucket(name) if fixed_sub is not None: return (bucket, (), fixed_sub) @@ -674,7 +720,7 @@ def check_property_order(ctx): if len(props) < 2: continue all_names = [p.prop_name for p in props] - keyed = [(p, _property_sort_key(p.prop_name, all_names)) + keyed = [(p, _property_sort_key(dl, p.prop_name, all_names)) for p in props] for k in range(1, len(keyed)): if keyed[k][1] < keyed[k - 1][1]: diff --git a/scripts/dtc/dt-style-selftest/bad/dts-child-name-order.dtso b/scripts/dtc/dt-style-selftest/bad/dts-child-name-order.dtso new file mode 100644 index 000000000000..74b49be69e98 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/dts-child-name-order.dtso @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/dts-v1/; +/plugin/; + +&{/} { + #address-cells = <1>; + #size-cells = <1>; + + pmu { + compatible = "example,pmu"; + + /* Include labels to be sure they do not affect sorting */ + foo: foo { + label = "foo"; + }; + + label_bar: bar { + label = "bar"; + }; + }; + + memory@a0000000 { + device_type = "memory"; + reg = <0x0 0xa0000000 0x0 0x0>; + }; + + pmu-2 { + compatible = "example,pmu"; + + /* Just reference labels to avoid strict warnings */ + example,foo = <&foo>, <&label_bar>; + }; +}; diff --git a/scripts/dtc/dt-style-selftest/bad/dts-digit-node-order.dts b/scripts/dtc/dt-style-selftest/bad/dts-digit-node-order.dts new file mode 100644 index 000000000000..74c956398324 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/dts-digit-node-order.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + memory@a0000000 { + device_type = "memory"; + reg = <0x0 0xa0000000 0x0 0x0>; + }; + + pmu { + compatible = "example,pmu"; + }; + + soc@0 { + compatible = "simple-bus"; + ranges = <0 0 0 0xc0000000>; + + #address-cells = <1>; + #size-cells = <1>; + + serial@20000 { + compatible = "example,serial"; + reg = <0x20000 0x1000>; + }; + + interrupt-controller@10000 { + compatible = "example,intc"; + reg = <0x10000 0x1000>; + interrupts = <1 2 3>; + }; + + serial@30000 { + compatible = "example,serial"; + reg = <0x30000 0x1000>; + }; + }; +}; diff --git a/scripts/dtc/dt-style-selftest/bad/dts-digit-node-order.dtso b/scripts/dtc/dt-style-selftest/bad/dts-digit-node-order.dtso new file mode 100644 index 000000000000..052c02935a45 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/dts-digit-node-order.dtso @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/dts-v1/; +/plugin/; + +&{/} { + #address-cells = <1>; + #size-cells = <1>; + + memory@a0000000 { + device_type = "memory"; + reg = <0x0 0xa0000000 0x0 0x0>; + }; + + pmu { + compatible = "example,pmu"; + }; + + soc@0 { + compatible = "simple-bus"; + ranges = <0 0 0 0xc0000000>; + + #address-cells = <1>; + #size-cells = <1>; + + serial@20000 { + compatible = "example,serial"; + reg = <0x20000 0x1000>; + }; + + interrupt-controller@10000 { + compatible = "example,intc"; + reg = <0x10000 0x1000>; + interrupts = <1 2 3>; + }; + + serial@30000 { + compatible = "example,serial"; + reg = <0x30000 0x1000>; + }; + }; +}; diff --git a/scripts/dtc/dt-style-selftest/bad/dts-property-order.dts b/scripts/dtc/dt-style-selftest/bad/dts-property-order.dts index 782cd55ec283..e675e5e05a46 100644 --- a/scripts/dtc/dt-style-selftest/bad/dts-property-order.dts +++ b/scripts/dtc/dt-style-selftest/bad/dts-property-order.dts @@ -6,6 +6,11 @@ /dts-v1/; / { + compatible = "example,test-board", "example,test-soc"; + model = "DT style selftest"; + qcom,board-id = <8 0>; + chassis-type = "handset"; + cpus { #address-cells = <1>; #size-cells = <0>; diff --git a/scripts/dtc/dt-style-selftest/expected/dts-child-name-order.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-child-name-order.dts.txt index e2eea0862102..312a45ed913a 100644 --- a/scripts/dtc/dt-style-selftest/expected/dts-child-name-order.dts.txt +++ b/scripts/dtc/dt-style-selftest/expected/dts-child-name-order.dts.txt @@ -1,2 +1,3 @@ # mode=strict bad/dts-child-name-order.dts:16: [child-name-order] child node 'bar' out of name order +bad/dts-child-name-order.dts:21: [child-name-order] child node 'memory' out of name order diff --git a/scripts/dtc/dt-style-selftest/expected/dts-child-name-order.dtso.txt b/scripts/dtc/dt-style-selftest/expected/dts-child-name-order.dtso.txt new file mode 100644 index 000000000000..e44ceb24ece8 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/dts-child-name-order.dtso.txt @@ -0,0 +1,3 @@ +# mode=strict +bad/dts-child-name-order.dtso:17: [child-name-order] child node 'bar' out of name order +bad/dts-child-name-order.dtso:22: [child-name-order] child node 'memory' out of name order diff --git a/scripts/dtc/dt-style-selftest/expected/dts-digit-node-order.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-digit-node-order.dts.txt new file mode 100644 index 000000000000..1f41acdea0b0 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/dts-digit-node-order.dts.txt @@ -0,0 +1,2 @@ +# mode=strict +bad/dts-digit-node-order.dts:29: [child-address-order] child node @10000 out of address order diff --git a/scripts/dtc/dt-style-selftest/expected/dts-digit-node-order.dtso.txt b/scripts/dtc/dt-style-selftest/expected/dts-digit-node-order.dtso.txt new file mode 100644 index 000000000000..21db32f6e639 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/dts-digit-node-order.dtso.txt @@ -0,0 +1,2 @@ +# mode=strict +bad/dts-digit-node-order.dtso:30: [child-address-order] child node @10000 out of address order diff --git a/scripts/dtc/dt-style-selftest/expected/dts-property-order.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-property-order.dts.txt index 4bc21328625f..29283f3451c7 100644 --- a/scripts/dtc/dt-style-selftest/expected/dts-property-order.dts.txt +++ b/scripts/dtc/dt-style-selftest/expected/dts-property-order.dts.txt @@ -1,6 +1,9 @@ # mode=strict -bad/dts-property-order.dts:15: [property-order] property 'compatible' out of canonical order (should sort before 'reg') -bad/dts-property-order.dts:16: [property-order] property 'device_type' out of canonical order (should sort before 'compatible') -bad/dts-property-order.dts:25: [property-order] property 'dma-coherent' out of canonical order (should sort before 'status') -bad/dts-property-order.dts:30: [property-order] property 'compatible' out of canonical order (should sort before 'ranges') -bad/dts-property-order.dts:40: [property-order] property 'compatible' out of canonical order (should sort before 'interrupts') +bad/dts-property-order.dts:8: [redundant-whitespace] extra whitespace before { +bad/dts-property-order.dts:10: [property-order] property 'model' out of canonical order (should sort before 'compatible') +bad/dts-property-order.dts:12: [property-order] property 'chassis-type' out of canonical order (should sort before 'qcom,board-id') +bad/dts-property-order.dts:20: [property-order] property 'compatible' out of canonical order (should sort before 'reg') +bad/dts-property-order.dts:21: [property-order] property 'device_type' out of canonical order (should sort before 'compatible') +bad/dts-property-order.dts:30: [property-order] property 'dma-coherent' out of canonical order (should sort before 'status') +bad/dts-property-order.dts:35: [property-order] property 'compatible' out of canonical order (should sort before 'ranges') +bad/dts-property-order.dts:45: [property-order] property 'compatible' out of canonical order (should sort before 'interrupts') diff --git a/scripts/dtc/dt-style-selftest/expected/dts-property-order.dtso.txt b/scripts/dtc/dt-style-selftest/expected/dts-property-order.dtso.txt index a78b8c042aa5..124183fab2ad 100644 --- a/scripts/dtc/dt-style-selftest/expected/dts-property-order.dtso.txt +++ b/scripts/dtc/dt-style-selftest/expected/dts-property-order.dtso.txt @@ -1,4 +1,5 @@ # mode=strict +bad/dts-property-order.dtso:11: [property-order] property 'model' out of canonical order (should sort before 'compatible') bad/dts-property-order.dtso:13: [property-order] property 'chassis-type' out of canonical order (should sort before 'qcom,board-id') bad/dts-property-order.dtso:21: [property-order] property 'compatible' out of canonical order (should sort before 'reg') bad/dts-property-order.dtso:22: [property-order] property 'device_type' out of canonical order (should sort before 'compatible') diff --git a/scripts/dtc/dt-style-selftest/good/dts-child-name-order.dtso b/scripts/dtc/dt-style-selftest/good/dts-child-name-order.dtso new file mode 100644 index 000000000000..477b13b1ac2e --- /dev/null +++ b/scripts/dtc/dt-style-selftest/good/dts-child-name-order.dtso @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/dts-v1/; +/plugin/; + +&{/} { + #address-cells = <1>; + #size-cells = <1>; + + memory@a0000000 { + device_type = "memory"; + reg = <0x0 0xa0000000 0x0 0x0>; + }; + + pmu { + compatible = "example,pmu"; + + /* Include labels to be sure they do not affect sorting */ + label_bar: bar { + label = "bar"; + }; + + foo: foo { + label = "foo"; + }; + }; + + pmu-2 { + compatible = "example,pmu"; + + /* Just reference labels to avoid strict warnings */ + example,foo = <&foo>, <&label_bar>; + }; +}; diff --git a/scripts/dtc/dt-style-selftest/good/dts-digit-node-order.dts b/scripts/dtc/dt-style-selftest/good/dts-digit-node-order.dts index cdf3f91ebe01..2b21dde7f3c8 100644 --- a/scripts/dtc/dt-style-selftest/good/dts-digit-node-order.dts +++ b/scripts/dtc/dt-style-selftest/good/dts-digit-node-order.dts @@ -5,13 +5,10 @@ / { #address-cells = <1>; #size-cells = <1>; - /* TODO: uncomment when child-address-order is fixed for top-level */ - /* memory@a0000000 { device_type = "memory"; reg = <0x0 0xa0000000 0x0 0x0>; }; - */ pmu { compatible = "example,pmu"; diff --git a/scripts/dtc/dt-style-selftest/good/dts-digit-node-order.dtso b/scripts/dtc/dt-style-selftest/good/dts-digit-node-order.dtso new file mode 100644 index 000000000000..79ea2deec789 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/good/dts-digit-node-order.dtso @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/dts-v1/; +/plugin/; + +&{/} { + #address-cells = <1>; + #size-cells = <1>; + + memory@a0000000 { + device_type = "memory"; + reg = <0x0 0xa0000000 0x0 0x0>; + }; + + pmu { + compatible = "example,pmu"; + }; + + soc@0 { + compatible = "simple-bus"; + ranges = <0 0 0 0xc0000000>; + + #address-cells = <1>; + #size-cells = <1>; + + interrupt-controller@10000 { + compatible = "example,intc"; + reg = <0x10000 0x1000>; + interrupts = <1 2 3>; + }; + + serial@20000 { + compatible = "example,serial"; + reg = <0x20000 0x1000>; + }; + + serial@30000 { + compatible = "example,serial"; + reg = <0x30000 0x1000>; + }; + }; +}; diff --git a/scripts/dtc/dt-style-selftest/good/dts-property-order.dts b/scripts/dtc/dt-style-selftest/good/dts-property-order.dts index 0e183e3459cd..3d847cc9fa3e 100644 --- a/scripts/dtc/dt-style-selftest/good/dts-property-order.dts +++ b/scripts/dtc/dt-style-selftest/good/dts-property-order.dts @@ -6,6 +6,11 @@ /dts-v1/; / { + model = "DT style selftest"; + compatible = "example,test-board", "example,test-soc"; + chassis-type = "handset"; + qcom,board-id = <8 0>; + cpus { #address-cells = <1>; #size-cells = <0>; diff --git a/scripts/dtc/dt-style-selftest/good/dts-property-order.dtso b/scripts/dtc/dt-style-selftest/good/dts-property-order.dtso new file mode 100644 index 000000000000..5ae78541f68b --- /dev/null +++ b/scripts/dtc/dt-style-selftest/good/dts-property-order.dtso @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/* + * Test fixture: Incorrect property order + */ + +/dts-v1/; +/plugin/; + +&{/} { + model = "DT style selftest"; + compatible = "example,test-board", "example,test-soc"; + chassis-type = "handset"; + qcom,board-id = <8 0>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a57"; + reg = <0x0 0x0>; + enable-method = "psci"; + }; + }; + + pmu { + compatible = "example,pmu"; + dma-coherent; + + status = "disabled"; + }; + + soc@0 { + compatible = "simple-bus"; + ranges = <0 0 0 0xc0000000>; + + #address-cells = <1>; + #size-cells = <1>; + + interrupt-controller@10000 { + compatible = "example,intc"; + reg = <0x10000 0x1000>; + interrupts = <1 2 3>; + }; + }; +}; -- 2.53.0