dm: core: Support sorting devices with dm tree

Add a -s flag to sort the top-level devices in order of uclass ID.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/test/py/tests/test_dm.py b/test/py/tests/test_dm.py
index ea93061..68d4ea1 100644
--- a/test/py/tests/test_dm.py
+++ b/test/py/tests/test_dm.py
@@ -16,6 +16,44 @@
     for driver in drivers:
         assert driver in response
 
+    # check sorting - output looks something like this:
+    #  testacpi      0  [   ]   testacpi_drv          |-- acpi-test
+    #  testacpi      1  [   ]   testacpi_drv          |   `-- child
+    #  pci_emul_p    1  [   ]   pci_emul_parent_drv   |-- pci-emul2
+    #  pci_emul      5  [   ]   sandbox_swap_case_em  |   `-- emul2@1f,0
+
+    # The number of '|   ' and '--' matches indicate the indent level. We start
+    # checking sorting only after UCLASS_AXI_EMUL after which the names should
+    # be sorted.
+
+    response = u_boot_console.run_command('dm tree -s')
+    lines = response.split('\n')[2:]
+    stack = []   # holds where we were up to at the previous indent level
+    prev = ''    # uclass name of previous line
+    start = False
+    for line in lines:
+        indent = line.count('|   ') + ('--' in line)
+        cur = line.split()[0]
+        if not start:
+            if cur != 'axi_emul':
+                continue
+            start = True
+
+        # Handle going up or down an indent level
+        if indent > len(stack):
+            stack.append(prev)
+            prev = ''
+        elif indent < len(stack):
+            prev = stack.pop()
+
+        # Check that the current uclass name is not alphabetically before the
+        # previous one
+        if 'emul' not in cur and cur < prev:
+            print('indent', cur >= prev, indent, prev, cur, stack)
+            assert cur >= prev
+            prev = cur
+
+
 @pytest.mark.buildconfigspec('cmd_dm')
 def test_dm_drivers(u_boot_console):
     """Test that each driver in `dm compat` is also listed in `dm drivers`."""