Merge tag 'xilinx-for-v2023.01-rc3' of https://source.denx.de/u-boot/custodians/u...
[platform/kernel/u-boot.git] / drivers / core / ofnode.c
index b1ba8c5..4d56b1a 100644 (file)
@@ -4,6 +4,8 @@
  * Written by Simon Glass <sjg@chromium.org>
  */
 
+#define LOG_CATEGORY   LOGC_DT
+
 #include <common.h>
 #include <dm.h>
 #include <fdtdec.h>
 #include <linux/ioport.h>
 #include <asm/global_data.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
+#if CONFIG_IS_ENABLED(OFNODE_MULTI_TREE)
+static void *oftree_list[CONFIG_OFNODE_MULTI_TREE_MAX];
+static int oftree_count;
+
+void oftree_reset(void)
+{
+       if (gd->flags & GD_FLG_RELOC) {
+               oftree_count = 0;
+               oftree_list[oftree_count++] = (void *)gd->fdt_blob;
+       }
+}
+
+static int oftree_find(const void *fdt)
+{
+       int i;
+
+       for (i = 0; i < oftree_count; i++) {
+               if (fdt == oftree_list[i])
+                       return i;
+       }
+
+       return -1;
+}
+
+static oftree oftree_ensure(void *fdt)
+{
+       oftree tree;
+       int i;
+
+       if (gd->flags & GD_FLG_RELOC) {
+               i = oftree_find(fdt);
+               if (i == -1) {
+                       if (oftree_count == CONFIG_OFNODE_MULTI_TREE_MAX) {
+                               log_warning("Too many registered device trees (max %d)\n",
+                                           CONFIG_OFNODE_MULTI_TREE_MAX);
+                               return oftree_null();
+                       }
+
+                       /* register the new tree */
+                       i = oftree_count++;
+                       oftree_list[i] = fdt;
+                       log_debug("oftree: registered tree %d: %p\n", i, fdt);
+               }
+       } else {
+               if (fdt != gd->fdt_blob) {
+                       log_debug("Cannot only access control FDT before relocation\n");
+                       return oftree_null();
+               }
+       }
+
+       tree.fdt = fdt;
+
+       return tree;
+}
+
+void *ofnode_lookup_fdt(ofnode node)
+{
+       if (gd->flags & GD_FLG_RELOC) {
+               uint i = OFTREE_TREE_ID(node.of_offset);
+
+               if (i > oftree_count) {
+                       log_debug("Invalid tree ID %x\n", i);
+                       return NULL;
+               }
+
+               return oftree_list[i];
+       } else {
+               return (void *)gd->fdt_blob;
+       }
+}
+
+void *ofnode_to_fdt(ofnode node)
+{
+#ifdef OF_CHECKS
+       if (of_live_active())
+               return NULL;
+#endif
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && ofnode_valid(node))
+               return ofnode_lookup_fdt(node);
+
+       /* Use the control FDT by default */
+       return (void *)gd->fdt_blob;
+}
+
+/**
+ * ofnode_to_offset() - convert an ofnode to a flat DT offset
+ *
+ * This cannot be called if the reference contains a node pointer.
+ *
+ * @node: Reference containing offset (possibly invalid)
+ * Return: DT offset (can be -1)
+ */
+int ofnode_to_offset(ofnode node)
+{
+#ifdef OF_CHECKS
+       if (of_live_active())
+               return -1;
+#endif
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && node.of_offset >= 0)
+               return OFTREE_OFFSET(node.of_offset);
+
+       return node.of_offset;
+}
+
+oftree oftree_from_fdt(void *fdt)
+{
+       oftree tree;
+
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE))
+               return oftree_ensure(fdt);
+
+       tree.fdt = fdt;
+
+       return tree;
+}
+
+/**
+ * noffset_to_ofnode() - convert a DT offset to an ofnode
+ *
+ * @other_node: Node in the same tree to use as a reference
+ * @of_offset: DT offset (either valid, or -1)
+ * Return: reference to the associated DT offset
+ */
+ofnode noffset_to_ofnode(ofnode other_node, int of_offset)
+{
+       ofnode node;
+
+       if (of_live_active())
+               node.np = NULL;
+       else if (!CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) || of_offset < 0 ||
+                !ofnode_valid(other_node))
+               node.of_offset = of_offset;
+       else
+               node.of_offset = OFTREE_MAKE_NODE(other_node.of_offset,
+                                                 of_offset);
+
+       return node;
+}
+
+#else /* !OFNODE_MULTI_TREE */
+
+static inline int oftree_find(const void *fdt)
+{
+       return 0;
+}
+
+#endif /* OFNODE_MULTI_TREE */
+
+/**
+ * ofnode_from_tree_offset() - get an ofnode from a tree offset (flat tree)
+ *
+ * Looks up the tree and returns an ofnode with the correct of_offset (i.e.
+ * containing the tree ID).
+ *
+ * If @offset is < 0 then this returns an ofnode with that offset and no tree
+ * ID.
+ *
+ * @tree: tree to check
+ * @offset: offset within that tree (can be < 0)
+ * @return node for that offset, with the correct ID
+ */
+static ofnode ofnode_from_tree_offset(oftree tree, int offset)
+{
+       ofnode node;
+
+       if (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) && offset >= 0) {
+               int tree_id = oftree_find(tree.fdt);
+
+               if (tree_id == -1)
+                       return ofnode_null();
+               node.of_offset = OFTREE_NODE(tree_id, offset);
+       } else {
+               node.of_offset = offset;
+       }
+
+       return node;
+}
+
 bool ofnode_name_eq(ofnode node, const char *name)
 {
        const char *node_name;
@@ -417,9 +599,9 @@ ofnode oftree_get_by_phandle(oftree tree, uint phandle)
        if (of_live_active())
                node = np_to_ofnode(of_find_node_by_phandle(tree.np, phandle));
        else
-               node.of_offset =
+               node = ofnode_from_tree_offset(tree,
                        fdt_node_offset_by_phandle(oftree_lookup_fdt(tree),
-                                                  phandle);
+                                                  phandle));
 
        return node;
 }
@@ -640,15 +822,27 @@ ofnode ofnode_path(const char *path)
                return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path));
 }
 
-ofnode ofnode_path_root(oftree tree, const char *path)
+ofnode oftree_root(oftree tree)
 {
-       if (of_live_active())
+       if (of_live_active()) {
+               return np_to_ofnode(tree.np);
+       } else {
+               return ofnode_from_tree_offset(tree, 0);
+       }
+}
+
+ofnode oftree_path(oftree tree, const char *path)
+{
+       if (of_live_active()) {
                return np_to_ofnode(of_find_node_opts_by_path(tree.np, path,
                                                              NULL));
-       else if (*path != '/' && tree.fdt != gd->fdt_blob)
+       } else if (*path != '/' && tree.fdt != gd->fdt_blob) {
                return ofnode_null();  /* Aliases only on control FDT */
-       else
-               return offset_to_ofnode(fdt_path_offset(tree.fdt, path));
+       } else {
+               int offset = fdt_path_offset(tree.fdt, path);
+
+               return ofnode_from_tree_offset(tree, offset);
+       }
 }
 
 const void *ofnode_read_chosen_prop(const char *propname, int *sizep)
@@ -1003,12 +1197,12 @@ int ofnode_read_eth_phy_id(ofnode node, u16 *vendor, u16 *device)
        while (list < end) {
                len = strlen(list);
 
-               if (len >= strlen("ethernet-phy-idVVVV,DDDD")) {
+               if (len >= strlen("ethernet-phy-idVVVV.DDDD")) {
                        char *s = strstr(list, "ethernet-phy-id");
 
                        /*
                         * check if the string is something like
-                        * ethernet-phy-idVVVV,DDDD
+                        * ethernet-phy-idVVVV.DDDD
                         */
                        if (s && s[19] == '.') {
                                s += strlen("ethernet-phy-id");
@@ -1194,15 +1388,27 @@ ofnode ofnode_by_prop_value(ofnode from, const char *propname,
 }
 
 int ofnode_write_prop(ofnode node, const char *propname, const void *value,
-                     int len)
+                     int len, bool copy)
 {
-       if (of_live_active())
-               return of_write_prop(ofnode_to_np(node), propname, len, value);
-       else
+       if (of_live_active()) {
+               void *newval;
+               int ret;
+
+               if (copy) {
+                       newval = malloc(len);
+                       if (!newval)
+                               return log_ret(-ENOMEM);
+                       memcpy(newval, value, len);
+                       value = newval;
+               }
+               ret = of_write_prop(ofnode_to_np(node), propname, len, value);
+               if (ret && copy)
+                       free(newval);
+               return ret;
+       } else {
                return fdt_setprop(ofnode_to_fdt(node), ofnode_to_offset(node),
                                   propname, value, len);
-
-       return 0;
+       }
 }
 
 int ofnode_write_string(ofnode node, const char *propname, const char *value)
@@ -1211,7 +1417,8 @@ int ofnode_write_string(ofnode node, const char *propname, const char *value)
 
        debug("%s: %s = %s", __func__, propname, value);
 
-       return ofnode_write_prop(node, propname, value, strlen(value) + 1);
+       return ofnode_write_prop(node, propname, value, strlen(value) + 1,
+                                false);
 }
 
 int ofnode_write_u32(ofnode node, const char *propname, u32 value)
@@ -1226,7 +1433,7 @@ int ofnode_write_u32(ofnode node, const char *propname, u32 value)
                return -ENOMEM;
        *val = cpu_to_fdt32(value);
 
-       return ofnode_write_prop(node, propname, val, sizeof(value));
+       return ofnode_write_prop(node, propname, val, sizeof(value), false);
 }
 
 int ofnode_set_enabled(ofnode node, bool value)
@@ -1350,3 +1557,27 @@ int ofnode_add_subnode(ofnode node, const char *name, ofnode *subnodep)
 
        return ret;     /* 0 or -EEXIST */
 }
+
+int ofnode_copy_props(ofnode src, ofnode dst)
+{
+       struct ofprop prop;
+
+       ofnode_for_each_prop(prop, src) {
+               const char *name;
+               const char *val;
+               int len, ret;
+
+               val = ofprop_get_property(&prop, &name, &len);
+               if (!val) {
+                       log_debug("Cannot read prop (err=%d)\n", len);
+                       return log_msg_ret("get", -EINVAL);
+               }
+               ret = ofnode_write_prop(dst, name, val, len, true);
+               if (ret) {
+                       log_debug("Cannot write prop (err=%d)\n", ret);
+                       return log_msg_ret("wr", -EINVAL);
+               }
+       }
+
+       return 0;
+}