Add a function to scan through all the subnodes of a given node
recusively for bootph* property. If found, propagate it to all
of its parent node up the hierarchy so as to bind the respective
drivers of the nodes in the pre-relocation stage. 

The current model skips the node if there is no bootph* found in
it, even if the property is present in one of the subnodes. The 
issue is tracked in [0].

Signed-off-by: Moteen Shah <[email protected]>

References: 
[0] https://source.denx.de/u-boot/custodians/u-boot-dm/-/issues/21

---
 drivers/core/lists.c | 88 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 86 insertions(+), 2 deletions(-)

diff --git a/drivers/core/lists.c b/drivers/core/lists.c
index c7be504b6fc..5be556ea951 100644
--- a/drivers/core/lists.c
+++ b/drivers/core/lists.c
@@ -17,6 +17,7 @@
 #include <dm/platdata.h>
 #include <dm/uclass.h>
 #include <dm/util.h>
+#include <fdt_support.h>
 #include <fdtdec.h>
 #include <linux/compiler.h>
 
@@ -196,6 +197,80 @@ static int driver_check_compatible(const struct udevice_id 
*of_match,
        return -ENOENT;
 }
 
+/**
+ * scan_and_prop_bootph() - Check if a driver matches a compatible string
+ *
+ * @param of_node:     Parent node for child traversal
+ * @param fdt: Blob to use
+ */
+static int scan_and_prop_bootph(ofnode node, void *fdt)
+{
+       static int bootph_index = -1;
+       static const char * const bootph_props[] = {
+               "bootph-all",
+               "bootph-some-ram",
+               "bootph-pre-ram",
+               "bootph-pre-sram",
+       };
+       int offset;
+       int ret = -ENOENT;
+       ofnode subnode;
+
+       /* Note the index where bootph-* was found and return success */
+       for (int i = 0; i < ARRAY_SIZE(bootph_props); i++) {
+               if (ofnode_read_bool(node, bootph_props[i])) {
+                       bootph_index = i;
+                       return 0;
+               }
+       }
+
+       ofnode_for_each_subnode(subnode, node) {
+               if (!ofnode_valid(subnode))
+                       continue;
+
+               ret = scan_and_prop_bootph(subnode, fdt);
+               if (ret != -ENOENT && ret)
+                       return ret;
+
+               /*
+                * Break the search if bootph-* is found in any of the subnodes.
+                * Breaking the loop early helps in avoiding unnecessary 
traversal
+                * of the sibling node given that bootph-* has been found in 
previous
+                * sibling node.
+                */
+               if (!ret)
+                       break;
+       }
+
+       /* If no bootph-* found then return no such entry */
+       if (ret)
+               return -ENOENT;
+
+       if (bootph_index != -1) {
+               offset = ofnode_to_offset(node);
+               if (offset < 0) {
+                       log_err("Failed to get offset %d\n", offset);
+                       return -EINVAL;
+               }
+
+               ret = fdt_increase_size(fdt, 32);
+               if (ret) {
+                       log_err("Cannot increase FDT size!\n");
+                       return ret;
+               }
+
+               ret = fdt_setprop_empty(fdt, offset, 
bootph_props[bootph_index]);
+               if (ret) {
+                       log_err("Failed to add bootph prop to node:%s ret=%d\n",
+                               ofnode_get_name(node), ret);
+                       return ret;
+               }
+               bootph_index = -1;
+       }
+
+       return ret;
+}
+
 int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
                   struct driver *drv, bool pre_reloc_only)
 {
@@ -205,6 +280,7 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, 
struct udevice **devp,
        struct driver *entry;
        struct udevice *dev;
        bool found = false;
+       void *fdt = (void *)gd->fdt_blob;
        const char *name, *compat_list, *compat;
        int compat_length, i;
        int result = 0;
@@ -256,8 +332,16 @@ int lists_bind_fdt(struct udevice *parent, ofnode node, 
struct udevice **devp,
                if (pre_reloc_only) {
                        if (!ofnode_pre_reloc(node) &&
                            !(entry->flags & DM_FLAG_PRE_RELOC)) {
-                               log_debug("Skipping device pre-relocation\n");
-                               return 0;
+                               if (IS_ENABLED(CONFIG_BIND_FROM_CHILD_BOOTPH)) {
+                                       ret = scan_and_prop_bootph(node, fdt);
+                                       if (ret) {
+                                               log_debug("Skipping device 
pre-relocation\n");
+                                               return 0;
+                                       }
+                               } else {
+                                       log_debug("Skipping device 
pre-relocation\n");
+                                       return 0;
+                               }
                        }
                }
 
-- 
2.34.1

Reply via email to