[PATCH v3 7/7] dtc: dt-check-style: Relax property ordering rules (drop alphabetical)

From: Krzysztof Kozlowski

Date: Wed Sep 09 2026 - 09:25:00 EST


Existing rules checked whether properties follow DTS Coding Style
guidelines in respect of common properties and standard vs vendor
properties, plus additionally it enforced alphabetical order within each
of such group.

The in-tree DTS does not follow such style at all, so this lead to many
warnings. There is little value in fixing these warnings, because DTS
with non-alphabetical order of properties is exactly the same readable,
even though DTS Coding Style mentions natural order. Actually groupping
properties logically, e.g. all supplies together regardless of their
name, is more readable than pure natural sort.

Relax the property order rules to the most important aspects
(device_type, reg/reg-names, ranges, standard properties, vendor
properties, status) and skip the alphabetical sorting completely.

Assisted-by: LLM
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@xxxxxxxxxxxxxxxx>
---
.../devicetree/bindings/dts-coding-style.rst | 5 +-
scripts/dtc/dt-check-style | 137 +++++++++------------
.../dt-style-selftest/bad/dts-property-order.dts | 3 +
.../dt-style-selftest/bad/dts-property-order.dtso | 3 +
.../dtc/dt-style-selftest/bad/yaml-prop-order.yaml | 5 +
.../expected/dts-property-order.dts.txt | 7 +-
.../expected/dts-property-order.dtso.txt | 7 +-
.../expected/dts-property-pairing.dts.txt | 6 +-
.../expected/yaml-prop-order.yaml.txt | 1 +
.../expected/yaml-prop-pairing.yaml.txt | 6 +-
.../dt-style-selftest/good/dts-property-order.dts | 3 +
.../dt-style-selftest/good/dts-property-order.dtso | 3 +
12 files changed, 91 insertions(+), 95 deletions(-)

diff --git a/Documentation/devicetree/bindings/dts-coding-style.rst b/Documentation/devicetree/bindings/dts-coding-style.rst
index 63648db377e1..441436b7fa80 100644
--- a/Documentation/devicetree/bindings/dts-coding-style.rst
+++ b/Documentation/devicetree/bindings/dts-coding-style.rst
@@ -135,8 +135,9 @@ The above-described ordering follows this approach:
3. Status is the last information to annotate that device node is or is not
finished (board resources are needed).

-The individual properties inside each group shall use natural sort order by
-the property name.
+The individual properties inside each group shall use usually natural sort
+order by the property name, with exceptions of logical grouping of properties,
+e.g. supplies.

Example::

diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style
index 9fcb2eeaf7fe..99b22364d866 100755
--- a/scripts/dtc/dt-check-style
+++ b/scripts/dtc/dt-check-style
@@ -714,65 +714,40 @@ def check_property_name(ctx):
yield (dl.lineno, f'property name "{dl.prop_name}" is using discouraged style')


-def _property_bucket(name):
- """Return the canonical bucket index for a property:
- 0 device_type
+def _property_bucket(name, is_root=False):
+ """Return the canonical bucket (group) index for a property:
+ 0 device_type ('model' in the root node)
1 compatible
- 2 reg / reg-names
+ 2 reg, reg-names
3 ranges
4 standard properties (no vendor comma in #-stripped name)
5 vendor-specific properties
6 status
- 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."""
+ Properties sharing a bucket compare equal: their relative order
+ within the group is not checked, only the pairing rules below
+ apply."""
stripped = name.lstrip('#')
- if name == 'device_type':
- return (0, 0)
+ if is_root:
+ if name == 'model':
+ return 0
+ elif name == 'device_type':
+ return 0
if name == 'compatible':
- return (1, 0)
- if name == 'reg':
- return (2, 0)
- if name == 'reg-names':
- return (2, 1)
+ return 1
+ if name in ('reg', 'reg-names'):
+ return 2
if name == 'ranges':
- return (3, 0)
+ return 3
if name == 'status':
- return (6, 0)
- 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)
+ return 6
+ return 5 if ',' in stripped else 4


# 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
+# If a rule returns an anchor, the property must be placed after the
# anchor. Rules are tried in order; the first match wins. If none
-# matches, the within-bucket key falls back to natural sort by the
-# #-stripped name.
+# matches, the property can be placed anywhere within its bucket.

def _pair_pinctrl_names(name, all_names):
"""pinctrl-names follows the highest pinctrl-N in the same node."""
@@ -787,8 +762,7 @@ def _pair_pinctrl_names(name, all_names):
def _pair_x_names(name, all_names):
"""Generic <x>-names follows its owning property. The owner is
usually plural (clocks/clock-names, dmas/dma-names,
- resets/reset-names) but occasionally singular (reg/reg-names is
- handled by the fixed slot above; this rule catches anything else)."""
+ resets/reset-names) but occasionally singular (reg/reg-names)."""
if not name.endswith('-names'):
return None
base = name[:-len('-names')]
@@ -812,33 +786,27 @@ PAIRING_RULES = (_pair_pinctrl_names, _pair_x_names,
_pair_address_size_cells)


-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."""
- 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)
-
+def _property_anchor(name, all_names):
+ """Return the property this one must be placed after, or None."""
for rule in PAIRING_RULES:
anchor = rule(name, all_names)
if anchor is not None:
- return (bucket, _natural_sort_key(anchor.lstrip('#')), 1)
-
- return (bucket, _natural_sort_key(name.lstrip('#')), 0)
+ return anchor
+ return None


def check_property_order(ctx):
- """Properties within a node body must appear in canonical order:
- compatible, reg(/reg-names), ranges, then the standard group, then
- the vendor-specific group, then status. Inside the standard and
- vendor groups, pairing rules apply (e.g. <x>-names follows <x>);
- everything else falls back to natural sort by the #-stripped name."""
+ """Properties within a node body must be grouped in canonical
+ order: device_type ('model' for the root node), compatible,
+ reg(/reg-names), ranges, then the standard group, then the
+ vendor-specific group, then status.
+
+ The order of properties within one group is free, e.g. it does not
+ matter whether '#address-cells' comes before or after 'clocks'.
+ Paired properties still keep their relative order: <x>-names
+ follows <x> (clocks/clock-names, reg/reg-names, ...), pinctrl-names
+ follows the pinctrl-N states and #size-cells follows
+ #address-cells."""
lines = ctx.lines
for i, dl in enumerate(lines):
if dl.linetype != LineType.NODE_OPEN:
@@ -856,16 +824,26 @@ def check_property_order(ctx):
if len(props) < 2:
continue
all_names = [p.prop_name for p in props]
- 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]:
- p = keyed[k][0]
- prev = keyed[k - 1][0]
- yield (p.lineno,
- 'property %r out of canonical order '
- '(should sort before %r)' %
- (p.prop_name, prev.prop_name))
+ buckets = [_property_bucket(p.prop_name, dl.is_root) for p in props]
+ findings = []
+ # Group (bucket) order
+ for k in range(1, len(props)):
+ if buckets[k] < buckets[k - 1]:
+ findings.append((props[k].lineno,
+ 'property %r out of canonical order '
+ '(should sort before %r)' %
+ (props[k].prop_name,
+ props[k - 1].prop_name)))
+ # Pairing within a group: the anchor must come first
+ seen = set()
+ for p in props:
+ anchor = _property_anchor(p.prop_name, all_names)
+ if anchor is not None and anchor not in seen:
+ findings.append((p.lineno,
+ 'property %r must be placed after %r' %
+ (p.prop_name, anchor)))
+ seen.add(p.prop_name)
+ yield from sorted(findings)


def _check_redundant_whitespace(dl):
@@ -1161,7 +1139,8 @@ RULES = [
'property names use only recommended characters (see DTS Coding Style)',
check_property_name),
Rule('property-order', 'strict',
- 'canonical bucket + pairing + natural-sort order of properties',
+ 'canonical group + pairing order of properties (order within '
+ 'a group is not checked)',
check_property_order),
# See also check_redundant_whitespace() and check_value_whitespace()
Rule('redundant-whitespace-strict', 'strict',
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 ebe561e38766..eb36daa4fe72 100644
--- a/scripts/dtc/dt-style-selftest/bad/dts-property-order.dts
+++ b/scripts/dtc/dt-style-selftest/bad/dts-property-order.dts
@@ -43,6 +43,9 @@ interrupt-controller@10000 {
<4 5 6>,
<7 8 9>;
compatible = "example,intc";
+ qcom,calibration-variant = "foo";
+ clocks = <6>;
+ qcom,opp-fuse-level = <1>;
};
};
};
diff --git a/scripts/dtc/dt-style-selftest/bad/dts-property-order.dtso b/scripts/dtc/dt-style-selftest/bad/dts-property-order.dtso
index 64604fa6b8c3..b2e564f2af2b 100644
--- a/scripts/dtc/dt-style-selftest/bad/dts-property-order.dtso
+++ b/scripts/dtc/dt-style-selftest/bad/dts-property-order.dtso
@@ -44,6 +44,9 @@ interrupt-controller@10000 {
<4 5 6>,
<7 8 9>;
compatible = "example,intc";
+ qcom,calibration-variant = "foo";
+ clocks = <6>;
+ qcom,opp-fuse-level = <1>;
};
};
};
diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml b/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml
index bf1480e97209..4d8a50d0e35a 100644
--- a/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml
+++ b/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml
@@ -26,4 +26,9 @@ examples:
device@1000 {
reg = <0x1000 0x100>;
compatible = "example,test-prop-order";
+
+ interrupts = <1>;
+ qcom,calibration-variant = "foo";
+ clocks = <6>;
+ qcom,opp-fuse-level = <1>;
};
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 0ab832ccf07a..155b072f0ac2 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
@@ -6,6 +6,7 @@ bad/dts-property-order.dts:21: [property-order] property 'device_type' out of ca
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')
-bad/dts-property-order.dts:50: [redundant-whitespace] extra whitespace before {
-bad/dts-property-order.dts:52: [property-order] property 'model' out of canonical order (should sort before 'compatible')
-bad/dts-property-order.dts:57: [property-order] property 'model' out of canonical order (should sort before 'compatible')
+bad/dts-property-order.dts:47: [property-order] property 'clocks' out of canonical order (should sort before 'qcom,calibration-variant')
+bad/dts-property-order.dts:53: [redundant-whitespace] extra whitespace before {
+bad/dts-property-order.dts:55: [property-order] property 'model' out of canonical order (should sort before 'compatible')
+bad/dts-property-order.dts:60: [property-order] property 'model' out of canonical order (should sort before 'compatible')
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 9f2a00916329..48c020d21b64 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
@@ -6,6 +6,7 @@ bad/dts-property-order.dtso:22: [property-order] property 'device_type' out of c
bad/dts-property-order.dtso:31: [property-order] property 'dma-coherent' out of canonical order (should sort before 'status')
bad/dts-property-order.dtso:36: [property-order] property 'compatible' out of canonical order (should sort before 'ranges')
bad/dts-property-order.dtso:46: [property-order] property 'compatible' out of canonical order (should sort before 'interrupts')
-bad/dts-property-order.dtso:51: [redundant-whitespace] extra whitespace before {
-bad/dts-property-order.dtso:53: [property-order] property 'model' out of canonical order (should sort before 'compatible')
-bad/dts-property-order.dtso:58: [property-order] property 'model' out of canonical order (should sort before 'compatible')
+bad/dts-property-order.dtso:48: [property-order] property 'clocks' out of canonical order (should sort before 'qcom,calibration-variant')
+bad/dts-property-order.dtso:54: [redundant-whitespace] extra whitespace before {
+bad/dts-property-order.dtso:56: [property-order] property 'model' out of canonical order (should sort before 'compatible')
+bad/dts-property-order.dtso:61: [property-order] property 'model' out of canonical order (should sort before 'compatible')
diff --git a/scripts/dtc/dt-style-selftest/expected/dts-property-pairing.dts.txt b/scripts/dtc/dt-style-selftest/expected/dts-property-pairing.dts.txt
index dad3d8485012..976471ca8068 100644
--- a/scripts/dtc/dt-style-selftest/expected/dts-property-pairing.dts.txt
+++ b/scripts/dtc/dt-style-selftest/expected/dts-property-pairing.dts.txt
@@ -1,5 +1,3 @@
# mode=strict
-bad/dts-property-pairing.dts:21: [property-order] property 'clocks' out of canonical order (should sort before 'clock-names')
-bad/dts-property-pairing.dts:25: [property-order] property '#address-cells' out of canonical order (should sort before 'interrupts')
-bad/dts-property-pairing.dts:27: [property-order] property 'pinctrl-0' out of canonical order (should sort before 'pinctrl-names')
-bad/dts-property-pairing.dts:28: [property-order] property '#size-cells' out of canonical order (should sort before 'pinctrl-0')
+bad/dts-property-pairing.dts:20: [property-order] property 'clock-names' must be placed after 'clocks'
+bad/dts-property-pairing.dts:26: [property-order] property 'pinctrl-names' must be placed after 'pinctrl-0'
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt
index 578df7209170..bb0dd5db5135 100644
--- a/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt
@@ -1,2 +1,3 @@
# mode=strict
bad/yaml-prop-order.yaml:28: example 0 [property-order] property 'compatible' out of canonical order (should sort before 'reg')
+bad/yaml-prop-order.yaml:32: example 0 [property-order] property 'clocks' out of canonical order (should sort before 'qcom,calibration-variant')
diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt
index 025ec872f1b0..dcdee1cff506 100644
--- a/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt
+++ b/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt
@@ -1,5 +1,3 @@
# mode=strict
-bad/yaml-prop-pairing.yaml:30: example 0 [property-order] property 'clocks' out of canonical order (should sort before 'clock-names')
-bad/yaml-prop-pairing.yaml:31: example 0 [property-order] property '#address-cells' out of canonical order (should sort before 'clocks')
-bad/yaml-prop-pairing.yaml:33: example 0 [property-order] property 'pinctrl-0' out of canonical order (should sort before 'pinctrl-names')
-bad/yaml-prop-pairing.yaml:34: example 0 [property-order] property '#size-cells' out of canonical order (should sort before 'pinctrl-0')
+bad/yaml-prop-pairing.yaml:29: example 0 [property-order] property 'clock-names' must be placed after 'clocks'
+bad/yaml-prop-pairing.yaml:32: example 0 [property-order] property 'pinctrl-names' must be placed after 'pinctrl-0'
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 3d847cc9fa3e..288fcfb9888f 100644
--- a/scripts/dtc/dt-style-selftest/good/dts-property-order.dts
+++ b/scripts/dtc/dt-style-selftest/good/dts-property-order.dts
@@ -40,7 +40,10 @@ soc@0 {
interrupt-controller@10000 {
compatible = "example,intc";
reg = <0x10000 0x1000>;
+ clocks = <6>;
interrupts = <1 2 3>;
+ qcom,calibration-variant = "foo";
+ qcom,opp-fuse-level = <1>;
};
};
};
diff --git a/scripts/dtc/dt-style-selftest/good/dts-property-order.dtso b/scripts/dtc/dt-style-selftest/good/dts-property-order.dtso
index 5ae78541f68b..60c68b3f908a 100644
--- a/scripts/dtc/dt-style-selftest/good/dts-property-order.dtso
+++ b/scripts/dtc/dt-style-selftest/good/dts-property-order.dtso
@@ -41,7 +41,10 @@ soc@0 {
interrupt-controller@10000 {
compatible = "example,intc";
reg = <0x10000 0x1000>;
+ clocks = <6>;
interrupts = <1 2 3>;
+ qcom,calibration-variant = "foo";
+ qcom,opp-fuse-level = <1>;
};
};
};

--
2.53.0