cmake: dts: Add dt_comp_path() cmake function

Add a new cmake extension function:

  dt_comp_path(<var> COMPATIBLE <compatible> [INDEX <idx>])

to get a list of paths for the nodes with the given <compatible>. This
is useful when we have to cycle through several nodes with the same
compatible.

Signed-off-by: Carlo Caione <ccaione@baylibre.com>
diff --git a/cmake/extensions.cmake b/cmake/extensions.cmake
index 9746fb1..5564d47 100644
--- a/cmake/extensions.cmake
+++ b/cmake/extensions.cmake
@@ -2855,6 +2855,60 @@
   endif()
 endfunction()
 
+# Usage:
+#
+#   dt_comp_path(<var> COMPATIBLE <compatible> [INDEX <idx>])
+#
+# Get a list of paths for the nodes with the given compatible. The value will
+# be returned in the <var> parameter.
+# <var> will be undefined if no such compatible exists.
+#
+# For details and considerations about the format of <path> and the returned
+# parameter refer to dt_prop().
+#
+# <var>                  : Return variable where the property value will be stored
+# COMPATIBLE <compatible>: Compatible for which the list of paths should be
+#                          returned, as it appears in the DTS source
+# INDEX <idx>            : Optional index when retrieving a value in an array property
+
+function(dt_comp_path var)
+  set(req_single_args "COMPATIBLE")
+  set(single_args "INDEX")
+  cmake_parse_arguments(DT_COMP "" "${req_single_args};${single_args}" "" ${ARGN})
+
+  if(${ARGV0} IN_LIST req_single_args)
+    message(FATAL_ERROR "dt_comp_path(${ARGV0} ...) missing return parameter.")
+  endif()
+
+  foreach(arg ${req_single_args})
+    if(NOT DEFINED DT_COMP_${arg})
+      message(FATAL_ERROR "dt_comp_path(${ARGV0} ...) "
+                          "missing required argument: ${arg}"
+      )
+    endif()
+  endforeach()
+
+  get_property(exists TARGET devicetree_target
+      PROPERTY "DT_COMP|${DT_COMP_COMPATIBLE}"
+      SET
+  )
+
+  if(NOT exists)
+    set(${var} PARENT_SCOPE)
+    return()
+  endif()
+
+  get_target_property(val devicetree_target
+      "DT_COMP|${DT_COMP_COMPATIBLE}"
+  )
+
+  if(DEFINED DT_COMP_INDEX)
+    list(GET val ${DT_COMP_INDEX} element)
+    set(${var} "${element}" PARENT_SCOPE)
+  else()
+    set(${var} "${val}" PARENT_SCOPE)
+  endif()
+endfunction()
 
 # Usage:
 #   dt_num_regs(<var> PATH <path>)
diff --git a/scripts/dts/gen_dts_cmake.py b/scripts/dts/gen_dts_cmake.py
index f86d2a8..8afc598 100755
--- a/scripts/dts/gen_dts_cmake.py
+++ b/scripts/dts/gen_dts_cmake.py
@@ -43,6 +43,7 @@
 import os
 import pickle
 import sys
+from collections import defaultdict
 
 sys.path.append(os.path.join(os.path.dirname(__file__), 'python-devicetree',
                              'src'))
@@ -95,6 +96,7 @@
         for alias in node.aliases:
             cmake_props.append(f'"DT_ALIAS|{alias}" "{path}"')
 
+    compatible2paths = defaultdict(list)
     for node in edt.nodes:
         cmake_props.append(f'"DT_NODE|{node.path}" TRUE')
 
@@ -117,6 +119,11 @@
                 cmake_prop = f'DT_PROP|{node.path}|{item}'
                 cmake_props.append(f'"{cmake_prop}" "{cmake_value}"')
 
+                if item == 'compatible':
+                    # compatibles is always an array
+                    for comp in node.props[item].val:
+                        compatible2paths[comp].append(node.path)
+
         if node.regs is not None:
             cmake_props.append(f'"DT_REG|{node.path}|NUM" "{len(node.regs)}"')
             cmake_addr = ''
@@ -136,6 +143,14 @@
             cmake_props.append(f'"DT_REG|{node.path}|ADDR" "{cmake_addr}"')
             cmake_props.append(f'"DT_REG|{node.path}|SIZE" "{cmake_size}"')
 
+    for comp in compatible2paths.keys():
+        cmake_path = ''
+        for path in compatible2paths[comp]:
+            cmake_path = f'{cmake_path}{path};'
+
+        cmake_comp = f'DT_COMP|{comp}'
+        cmake_props.append(f'"{cmake_comp}" "{cmake_path}"')
+
     with open(args.cmake_out, "w", encoding="utf-8") as cmake_file:
         print('add_custom_target(devicetree_target)', file=cmake_file)
         print(file=cmake_file)