devicetree: add DT_FOREACH_PROP_ELEM(node_id, prop, fn)

It can be convenient to "iterate" over the elements of a property, in
the same way it is convenient to "iterate" over enabled instances.

Add a new macro for doing this, along with a DT_INST_FOREACH_PROP_ELEM
variant.

This is likely to be more convenient than UTIL_LISTIFY or FOR_EACH in
some situations because:

- it handles inputs of any length
- compiler error messages will be shorter and more self-contained
- it is easier to use with phandle-array type properties, which
  require more complicated macro boilerplate when used with
  util_macro.h APIs

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
diff --git a/scripts/dts/gen_defines.py b/scripts/dts/gen_defines.py
index aaddaf5..6ddde90 100755
--- a/scripts/dts/gen_defines.py
+++ b/scripts/dts/gen_defines.py
@@ -32,6 +32,12 @@
 
 from devicetree import edtlib
 
+# The set of binding types whose values can be iterated over with
+# DT_FOREACH_PROP_ELEM(). If you change this, make sure to update the
+# doxygen string for that macro.
+FOREACH_PROP_ELEM_TYPES = set(['string', 'array', 'uint8-array', 'string-array',
+                               'phandles', 'phandle-array'])
+
 class LogFormatter(logging.Formatter):
     '''A log formatter that prints the level name in lower case,
     for compatibility with earlier versions of edtlib.'''
@@ -490,7 +496,8 @@
 
     macro2val = {}
     for prop_name, prop in node.props.items():
-        macro = f"{node.z_path_id}_P_{str2ident(prop_name)}"
+        prop_id = str2ident(prop_name)
+        macro = f"{node.z_path_id}_P_{prop_id}"
         val = prop2value(prop)
         if val is not None:
             # DT_N_<node-id>_P_<prop-id>
@@ -523,6 +530,12 @@
                     macro2val[macro + f"_IDX_{i}"] = subval
                 macro2val[macro + f"_IDX_{i}_EXISTS"] = 1
 
+        if prop.type in FOREACH_PROP_ELEM_TYPES:
+            # DT_N_<node-id>_P_<prop-id>_FOREACH_PROP_ELEM
+            macro2val[f"{macro}_FOREACH_PROP_ELEM(fn)"] = \
+                ' \\\n\t'.join(f'fn(DT_{node.z_path_id}, {prop_id}, {i})'
+                              for i in range(len(prop.val)))
+
         plen = prop_len(prop)
         if plen is not None:
             # DT_N_<node-id>_P_<prop-id>_LEN