[PATCH 4/9] dtc: dt-check-style: Handle sorting of top-level nodes and properties
From: Krzysztof Kozlowski
Date: Sun Jul 26 2026 - 11:51:14 EST
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 sorting by the node name, not the unit
address, for the top-level part.
Also the properties have one exception: 'model' property is supposed to
be before 'compatible'. This cannot be applied to entire DTS, because
sound cards have also 'model' where it is supposed to follow standard
rules (after 'compatible'). Root node is just special.
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@xxxxxxxxxxxxxxxx>
---
scripts/dtc/dt-check-style | 42 +++++++++++++++++++---
.../dt-style-selftest/bad/dts-digit-node-order.dts | 40 +++++++++++++++++++++
.../dt-style-selftest/bad/dts-property-order.dts | 5 +++
.../expected/dts-digit-node-order.dts.txt | 2 ++
.../expected/dts-property-order.dts.txt | 12 ++++---
.../expected/dts-property-order.dtso.txt | 1 +
.../good/dts-digit-node-order.dts | 3 --
.../dt-style-selftest/good/dts-property-order.dts | 5 +++
8 files changed, 97 insertions(+), 13 deletions(-)
diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index 1937c6feb57e..db4451e30dfb 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
@@ -496,15 +497,21 @@ def _walk_bodies(lines):
in the input. Skips ref-nodes (&label) since those don't have an
intrinsic ordering."""
body_stack = [[]]
+ 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()
yield body_stack.pop()
continue
while body_stack:
@@ -525,12 +532,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:
@@ -597,6 +610,22 @@ 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)
+ return (2, 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 +662,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 +706,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-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-property-order.dts b/scripts/dtc/dt-style-selftest/bad/dts-property-order.dts
index f31abb6ceae4..eb8b8b29fec5 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-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-property-order.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-property-order.dts.txt
index 4bc21328625f..65977f96331d 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,8 @@
# 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: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-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-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>;
--
2.53.0