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 dda6c76..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;
+       size_t len;
+
+       assert(ofnode_valid(node));
+
+       node_name = ofnode_get_name(node);
+       len = strchrnul(node_name, '@') - node_name;
+
+       return (strlen(name) == len) && !strncmp(node_name, name, len);
+}
+
+int ofnode_read_u8(ofnode node, const char *propname, u8 *outp)
+{
+       const u8 *cell;
+       int len;
+
+       assert(ofnode_valid(node));
+       debug("%s: %s: ", __func__, propname);
+
+       if (ofnode_is_np(node))
+               return of_read_u8(ofnode_to_np(node), propname, outp);
+
+       cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
+                          &len);
+       if (!cell || len < sizeof(*cell)) {
+               debug("(not found)\n");
+               return -EINVAL;
+       }
+       *outp = *cell;
+       debug("%#x (%d)\n", *outp, *outp);
+
+       return 0;
+}
+
+u8 ofnode_read_u8_default(ofnode node, const char *propname, u8 def)
+{
+       assert(ofnode_valid(node));
+       ofnode_read_u8(node, propname, &def);
+
+       return def;
+}
+
+int ofnode_read_u16(ofnode node, const char *propname, u16 *outp)
+{
+       const fdt16_t *cell;
+       int len;
+
+       assert(ofnode_valid(node));
+       debug("%s: %s: ", __func__, propname);
+
+       if (ofnode_is_np(node))
+               return of_read_u16(ofnode_to_np(node), propname, outp);
+
+       cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
+                          &len);
+       if (!cell || len < sizeof(*cell)) {
+               debug("(not found)\n");
+               return -EINVAL;
+       }
+       *outp = be16_to_cpup(cell);
+       debug("%#x (%d)\n", *outp, *outp);
+
+       return 0;
+}
+
+u16 ofnode_read_u16_default(ofnode node, const char *propname, u16 def)
+{
+       assert(ofnode_valid(node));
+       ofnode_read_u16(node, propname, &def);
+
+       return def;
+}
+
 int ofnode_read_u32(ofnode node, const char *propname, u32 *outp)
 {
        return ofnode_read_u32_index(node, propname, 0, outp);
@@ -44,8 +301,8 @@ int ofnode_read_u32_index(ofnode node, const char *propname, int index,
                return of_read_u32_index(ofnode_to_np(node), propname, index,
                                         outp);
 
-       cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
-                          &len);
+       cell = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node),
+                          propname, &len);
        if (!cell) {
                debug("(not found)\n");
                return -EINVAL;
@@ -90,8 +347,8 @@ int ofnode_read_u64(ofnode node, const char *propname, u64 *outp)
        if (ofnode_is_np(node))
                return of_read_u64(ofnode_to_np(node), propname, outp);
 
-       cell = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node), propname,
-                          &len);
+       cell = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node),
+                          propname, &len);
        if (!cell || len < sizeof(*cell)) {
                debug("(not found)\n");
                return -EINVAL;
@@ -142,7 +399,7 @@ const void *ofnode_read_prop(ofnode node, const char *propname, int *sizep)
                        len = prop->length;
                }
        } else {
-               val = fdt_getprop(gd->fdt_blob, ofnode_to_offset(node),
+               val = fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node),
                                  propname, &len);
        }
        if (!val) {
@@ -193,7 +450,7 @@ ofnode ofnode_find_subnode(ofnode node, const char *subnode_name)
        debug("%s: %s: ", __func__, subnode_name);
 
        if (ofnode_is_np(node)) {
-               const struct device_node *np = ofnode_to_np(node);
+               struct device_node *np = ofnode_to_np(node);
 
                for (np = np->child; np; np = np->sibling) {
                        if (!strcmp(subnode_name, np->name))
@@ -201,9 +458,9 @@ ofnode ofnode_find_subnode(ofnode node, const char *subnode_name)
                }
                subnode = np_to_ofnode(np);
        } else {
-               int ooffset = fdt_subnode_offset(gd->fdt_blob,
+               int ooffset = fdt_subnode_offset(ofnode_to_fdt(node),
                                ofnode_to_offset(node), subnode_name);
-               subnode = offset_to_ofnode(ooffset);
+               subnode = noffset_to_ofnode(node, ooffset);
        }
        debug("%s\n", ofnode_valid(subnode) ?
              ofnode_get_name(subnode) : "<none>");
@@ -221,9 +478,20 @@ int ofnode_read_u32_array(ofnode node, const char *propname,
                return of_read_u32_array(ofnode_to_np(node), propname,
                                         out_values, sz);
        } else {
-               return fdtdec_get_int_array(gd->fdt_blob,
-                                           ofnode_to_offset(node), propname,
-                                           out_values, sz);
+               int ret;
+
+               ret = fdtdec_get_int_array(ofnode_to_fdt(node),
+                                          ofnode_to_offset(node), propname,
+                                          out_values, sz);
+
+               /* get the error right, but space is more important in SPL */
+               if (!IS_ENABLED(CONFIG_SPL_BUILD)) {
+                       if (ret == -FDT_ERR_NOTFOUND)
+                               return -EINVAL;
+                       else if (ret == -FDT_ERR_BADLAYOUT)
+                               return -EOVERFLOW;
+               }
+               return ret;
        }
 }
 
@@ -233,7 +501,7 @@ bool ofnode_is_enabled(ofnode node)
        if (ofnode_is_np(node)) {
                return of_device_is_available(ofnode_to_np(node));
        } else {
-               return fdtdec_get_is_enabled(gd->fdt_blob,
+               return fdtdec_get_is_enabled(ofnode_to_fdt(node),
                                             ofnode_to_offset(node));
        }
 }
@@ -244,8 +512,8 @@ ofnode ofnode_first_subnode(ofnode node)
        if (ofnode_is_np(node))
                return np_to_ofnode(node.np->child);
 
-       return offset_to_ofnode(
-               fdt_first_subnode(gd->fdt_blob, ofnode_to_offset(node)));
+       return noffset_to_ofnode(node,
+               fdt_first_subnode(ofnode_to_fdt(node), ofnode_to_offset(node)));
 }
 
 ofnode ofnode_next_subnode(ofnode node)
@@ -254,8 +522,8 @@ ofnode ofnode_next_subnode(ofnode node)
        if (ofnode_is_np(node))
                return np_to_ofnode(node.np->sibling);
 
-       return offset_to_ofnode(
-               fdt_next_subnode(gd->fdt_blob, ofnode_to_offset(node)));
+       return noffset_to_ofnode(node,
+               fdt_next_subnode(ofnode_to_fdt(node), ofnode_to_offset(node)));
 }
 #endif /* !DM_INLINE_OFNODE */
 
@@ -267,7 +535,7 @@ ofnode ofnode_get_parent(ofnode node)
        if (ofnode_is_np(node))
                parent = np_to_ofnode(of_get_parent(ofnode_to_np(node)));
        else
-               parent.of_offset = fdt_parent_offset(gd->fdt_blob,
+               parent.of_offset = fdt_parent_offset(ofnode_to_fdt(node),
                                                     ofnode_to_offset(node));
 
        return parent;
@@ -281,9 +549,9 @@ const char *ofnode_get_name(ofnode node)
        }
 
        if (ofnode_is_np(node))
-               return strrchr(node.np->full_name, '/') + 1;
+               return node.np->name;
 
-       return fdt_get_name(gd->fdt_blob, ofnode_to_offset(node), NULL);
+       return fdt_get_name(ofnode_to_fdt(node), ofnode_to_offset(node), NULL);
 }
 
 int ofnode_get_path(ofnode node, char *buf, int buflen)
@@ -300,7 +568,7 @@ int ofnode_get_path(ofnode node, char *buf, int buflen)
        } else {
                int res;
 
-               res = fdt_get_path(gd->fdt_blob, ofnode_to_offset(node), buf,
+               res = fdt_get_path(ofnode_to_fdt(node), ofnode_to_offset(node), buf,
                                   buflen);
                if (!res)
                        return res;
@@ -316,7 +584,7 @@ ofnode ofnode_get_by_phandle(uint phandle)
        ofnode node;
 
        if (of_live_active())
-               node = np_to_ofnode(of_find_node_by_phandle(phandle));
+               node = np_to_ofnode(of_find_node_by_phandle(NULL, phandle));
        else
                node.of_offset = fdt_node_offset_by_phandle(gd->fdt_blob,
                                                            phandle);
@@ -324,6 +592,20 @@ ofnode ofnode_get_by_phandle(uint phandle)
        return node;
 }
 
+ofnode oftree_get_by_phandle(oftree tree, uint phandle)
+{
+       ofnode node;
+
+       if (of_live_active())
+               node = np_to_ofnode(of_find_node_by_phandle(tree.np, phandle));
+       else
+               node = ofnode_from_tree_offset(tree,
+                       fdt_node_offset_by_phandle(oftree_lookup_fdt(tree),
+                                                  phandle));
+
+       return node;
+}
+
 static fdt_addr_t __ofnode_get_addr_size_index(ofnode node, int index,
                                               fdt_size_t *size, bool translate)
 {
@@ -356,7 +638,7 @@ static fdt_addr_t __ofnode_get_addr_size_index(ofnode node, int index,
        } else {
                na = ofnode_read_simple_addr_cells(ofnode_get_parent(node));
                ns = ofnode_read_simple_size_cells(ofnode_get_parent(node));
-               return fdtdec_get_addr_size_fixed(gd->fdt_blob,
+               return fdtdec_get_addr_size_fixed(ofnode_to_fdt(node),
                                                  ofnode_to_offset(node), "reg",
                                                  index, na, ns, size,
                                                  translate);
@@ -404,7 +686,7 @@ int ofnode_stringlist_search(ofnode node, const char *property,
        } else {
                int ret;
 
-               ret = fdt_stringlist_search(gd->fdt_blob,
+               ret = fdt_stringlist_search(ofnode_to_fdt(node),
                                            ofnode_to_offset(node), property,
                                            string);
                if (ret == -FDT_ERR_NOTFOUND)
@@ -425,7 +707,8 @@ int ofnode_read_string_index(ofnode node, const char *property, int index,
        } else {
                int len;
 
-               *outp = fdt_stringlist_get(gd->fdt_blob, ofnode_to_offset(node),
+               *outp = fdt_stringlist_get(ofnode_to_fdt(node),
+                                          ofnode_to_offset(node),
                                           property, index, &len);
                if (len < 0)
                        return -EINVAL;
@@ -438,11 +721,37 @@ int ofnode_read_string_count(ofnode node, const char *property)
        if (ofnode_is_np(node)) {
                return of_property_count_strings(ofnode_to_np(node), property);
        } else {
-               return fdt_stringlist_count(gd->fdt_blob,
+               return fdt_stringlist_count(ofnode_to_fdt(node),
                                            ofnode_to_offset(node), property);
        }
 }
 
+int ofnode_read_string_list(ofnode node, const char *property,
+                           const char ***listp)
+{
+       const char **prop;
+       int count;
+       int i;
+
+       *listp = NULL;
+       count = ofnode_read_string_count(node, property);
+       if (count < 0)
+               return count;
+       if (!count)
+               return 0;
+
+       prop = calloc(count + 1, sizeof(char *));
+       if (!prop)
+               return -ENOMEM;
+
+       for (i = 0; i < count; i++)
+               ofnode_read_string_index(node, property, i, &prop[i]);
+       prop[count] = NULL;
+       *listp = prop;
+
+       return count;
+}
+
 static void ofnode_from_fdtdec_phandle_args(struct fdtdec_phandle_args *in,
                                            struct ofnode_phandle_args *out)
 {
@@ -481,7 +790,7 @@ int ofnode_parse_phandle_with_args(ofnode node, const char *list_name,
                struct fdtdec_phandle_args args;
                int ret;
 
-               ret = fdtdec_parse_phandle_with_args(gd->fdt_blob,
+               ret = fdtdec_parse_phandle_with_args(ofnode_to_fdt(node),
                                                     ofnode_to_offset(node),
                                                     list_name, cells_name,
                                                     cell_count, index, &args);
@@ -500,7 +809,7 @@ int ofnode_count_phandle_with_args(ofnode node, const char *list_name,
                return of_count_phandle_with_args(ofnode_to_np(node),
                                list_name, cells_name, cell_count);
        else
-               return fdtdec_parse_phandle_with_args(gd->fdt_blob,
+               return fdtdec_parse_phandle_with_args(ofnode_to_fdt(node),
                                ofnode_to_offset(node), list_name, cells_name,
                                cell_count, -1, NULL);
 }
@@ -513,6 +822,29 @@ ofnode ofnode_path(const char *path)
                return offset_to_ofnode(fdt_path_offset(gd->fdt_blob, path));
 }
 
+ofnode oftree_root(oftree tree)
+{
+       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) {
+               return ofnode_null();  /* Aliases only on control FDT */
+       } 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)
 {
        ofnode chosen_node;
@@ -664,11 +996,11 @@ const void *ofnode_get_property(ofnode node, const char *propname, int *lenp)
        if (ofnode_is_np(node))
                return of_get_property(ofnode_to_np(node), propname, lenp);
        else
-               return fdt_getprop(gd->fdt_blob, ofnode_to_offset(node),
+               return fdt_getprop(ofnode_to_fdt(node), ofnode_to_offset(node),
                                   propname, lenp);
 }
 
-int ofnode_get_first_property(ofnode node, struct ofprop *prop)
+int ofnode_first_property(ofnode node, struct ofprop *prop)
 {
        prop->node = node;
 
@@ -678,7 +1010,7 @@ int ofnode_get_first_property(ofnode node, struct ofprop *prop)
                        return -FDT_ERR_NOTFOUND;
        } else {
                prop->offset =
-                       fdt_first_property_offset(gd->fdt_blob,
+                       fdt_first_property_offset(ofnode_to_fdt(node),
                                                  ofnode_to_offset(prop->node));
                if (prop->offset < 0)
                        return prop->offset;
@@ -687,7 +1019,7 @@ int ofnode_get_first_property(ofnode node, struct ofprop *prop)
        return 0;
 }
 
-int ofnode_get_next_property(struct ofprop *prop)
+int ofnode_next_property(struct ofprop *prop)
 {
        if (ofnode_is_np(prop->node)) {
                prop->prop = of_get_next_property(ofnode_to_np(prop->node),
@@ -695,8 +1027,9 @@ int ofnode_get_next_property(struct ofprop *prop)
                if (!prop->prop)
                        return -FDT_ERR_NOTFOUND;
        } else {
-               prop->offset = fdt_next_property_offset(gd->fdt_blob,
-                                                       prop->offset);
+               prop->offset =
+                       fdt_next_property_offset(ofnode_to_fdt(prop->node),
+                                                prop->offset);
                if (prop->offset  < 0)
                        return prop->offset;
        }
@@ -704,27 +1037,18 @@ int ofnode_get_next_property(struct ofprop *prop)
        return 0;
 }
 
-const void *ofnode_get_property_by_prop(const struct ofprop *prop,
-                                       const char **propname, int *lenp)
+const void *ofprop_get_property(const struct ofprop *prop,
+                               const char **propname, int *lenp)
 {
        if (ofnode_is_np(prop->node))
                return of_get_property_by_prop(ofnode_to_np(prop->node),
                                               prop->prop, propname, lenp);
        else
-               return fdt_getprop_by_offset(gd->fdt_blob,
+               return fdt_getprop_by_offset(ofnode_to_fdt(prop->node),
                                             prop->offset,
                                             propname, lenp);
 }
 
-bool ofnode_is_available(ofnode node)
-{
-       if (ofnode_is_np(node))
-               return of_device_is_available(ofnode_to_np(node));
-       else
-               return fdtdec_get_is_enabled(gd->fdt_blob,
-                                            ofnode_to_offset(node));
-}
-
 fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property,
                                fdt_size_t *sizep)
 {
@@ -745,7 +1069,7 @@ fdt_addr_t ofnode_get_addr_size(ofnode node, const char *property,
                else
                        return of_read_number(prop, na);
        } else {
-               return fdtdec_get_addr_size(gd->fdt_blob,
+               return fdtdec_get_addr_size(ofnode_to_fdt(node),
                                            ofnode_to_offset(node), property,
                                            sizep);
        }
@@ -764,7 +1088,7 @@ const uint8_t *ofnode_read_u8_array_ptr(ofnode node, const char *propname,
                return (uint8_t *)prop;
 
        } else {
-               return fdtdec_locate_byte_array(gd->fdt_blob,
+               return fdtdec_locate_byte_array(ofnode_to_fdt(node),
                                ofnode_to_offset(node), propname, sz);
        }
 }
@@ -859,15 +1183,51 @@ int ofnode_read_pci_vendev(ofnode node, u16 *vendor, u16 *device)
        return -ENOENT;
 }
 
+int ofnode_read_eth_phy_id(ofnode node, u16 *vendor, u16 *device)
+{
+       const char *list, *end;
+       int len;
+
+       list = ofnode_get_property(node, "compatible", &len);
+
+       if (!list)
+               return -ENOENT;
+
+       end = list + len;
+       while (list < end) {
+               len = strlen(list);
+
+               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
+                        */
+                       if (s && s[19] == '.') {
+                               s += strlen("ethernet-phy-id");
+                               *vendor = simple_strtol(s, NULL, 16);
+                               s += 5;
+                               *device = simple_strtol(s, NULL, 16);
+
+                               return 0;
+                       }
+               }
+               list += (len + 1);
+       }
+
+       return -ENOENT;
+}
+
 int ofnode_read_addr_cells(ofnode node)
 {
        if (ofnode_is_np(node)) {
                return of_n_addr_cells(ofnode_to_np(node));
        } else {
-               int parent = fdt_parent_offset(gd->fdt_blob,
+               int parent = fdt_parent_offset(ofnode_to_fdt(node),
                                               ofnode_to_offset(node));
 
-               return fdt_address_cells(gd->fdt_blob, parent);
+               return fdt_address_cells(ofnode_to_fdt(node), parent);
        }
 }
 
@@ -876,10 +1236,10 @@ int ofnode_read_size_cells(ofnode node)
        if (ofnode_is_np(node)) {
                return of_n_size_cells(ofnode_to_np(node));
        } else {
-               int parent = fdt_parent_offset(gd->fdt_blob,
+               int parent = fdt_parent_offset(ofnode_to_fdt(node),
                                               ofnode_to_offset(node));
 
-               return fdt_size_cells(gd->fdt_blob, parent);
+               return fdt_size_cells(ofnode_to_fdt(node), parent);
        }
 }
 
@@ -888,7 +1248,8 @@ int ofnode_read_simple_addr_cells(ofnode node)
        if (ofnode_is_np(node))
                return of_simple_addr_cells(ofnode_to_np(node));
        else
-               return fdt_address_cells(gd->fdt_blob, ofnode_to_offset(node));
+               return fdt_address_cells(ofnode_to_fdt(node),
+                                        ofnode_to_offset(node));
 }
 
 int ofnode_read_simple_size_cells(ofnode node)
@@ -896,7 +1257,8 @@ int ofnode_read_simple_size_cells(ofnode node)
        if (ofnode_is_np(node))
                return of_simple_size_cells(ofnode_to_np(node));
        else
-               return fdt_size_cells(gd->fdt_blob, ofnode_to_offset(node));
+               return fdt_size_cells(ofnode_to_fdt(node),
+                                     ofnode_to_offset(node));
 }
 
 bool ofnode_pre_reloc(ofnode node)
@@ -933,7 +1295,8 @@ int ofnode_read_resource(ofnode node, uint index, struct resource *res)
                struct fdt_resource fres;
                int ret;
 
-               ret = fdt_get_resource(gd->fdt_blob, ofnode_to_offset(node),
+               ret = fdt_get_resource(ofnode_to_fdt(node),
+                                      ofnode_to_offset(node),
                                       "reg", index, &fres);
                if (ret < 0)
                        return -EINVAL;
@@ -962,7 +1325,8 @@ u64 ofnode_translate_address(ofnode node, const fdt32_t *in_addr)
        if (ofnode_is_np(node))
                return of_translate_address(ofnode_to_np(node), in_addr);
        else
-               return fdt_translate_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
+               return fdt_translate_address(ofnode_to_fdt(node),
+                                            ofnode_to_offset(node), in_addr);
 }
 
 u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr)
@@ -970,7 +1334,8 @@ u64 ofnode_translate_dma_address(ofnode node, const fdt32_t *in_addr)
        if (ofnode_is_np(node))
                return of_translate_dma_address(ofnode_to_np(node), in_addr);
        else
-               return fdt_translate_dma_address(gd->fdt_blob, ofnode_to_offset(node), in_addr);
+               return fdt_translate_dma_address(ofnode_to_fdt(node),
+                                                ofnode_to_offset(node), in_addr);
 }
 
 int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus, u64 *size)
@@ -978,7 +1343,8 @@ int ofnode_get_dma_range(ofnode node, phys_addr_t *cpu, dma_addr_t *bus, u64 *si
        if (ofnode_is_np(node))
                return of_get_dma_range(ofnode_to_np(node), cpu, bus, size);
        else
-               return fdt_get_dma_range(gd->fdt_blob, ofnode_to_offset(node),
+               return fdt_get_dma_range(ofnode_to_fdt(node),
+                                        ofnode_to_offset(node),
                                         cpu, bus, size);
 }
 
@@ -988,7 +1354,7 @@ int ofnode_device_is_compatible(ofnode node, const char *compat)
                return of_device_is_compatible(ofnode_to_np(node), compat,
                                               NULL, NULL);
        else
-               return !fdt_node_check_compatible(gd->fdt_blob,
+               return !fdt_node_check_compatible(ofnode_to_fdt(node),
                                                  ofnode_to_offset(node),
                                                  compat);
 }
@@ -1000,8 +1366,9 @@ ofnode ofnode_by_compatible(ofnode from, const char *compat)
                        (struct device_node *)ofnode_to_np(from), NULL,
                        compat));
        } else {
-               return offset_to_ofnode(fdt_node_offset_by_compatible(
-                               gd->fdt_blob, ofnode_to_offset(from), compat));
+               return noffset_to_ofnode(from,
+                       fdt_node_offset_by_compatible(ofnode_to_fdt(from),
+                                       ofnode_to_offset(from), compat));
        }
 }
 
@@ -1013,76 +1380,64 @@ ofnode ofnode_by_prop_value(ofnode from, const char *propname,
                        (struct device_node *)ofnode_to_np(from), propname,
                        propval, proplen));
        } else {
-               return offset_to_ofnode(fdt_node_offset_by_prop_value(
-                               gd->fdt_blob, ofnode_to_offset(from),
-                               propname, propval, proplen));
+               return noffset_to_ofnode(from,
+                        fdt_node_offset_by_prop_value(ofnode_to_fdt(from),
+                               ofnode_to_offset(from), propname, propval,
+                               proplen));
        }
 }
 
-int ofnode_write_prop(ofnode node, const char *propname, int len,
-                     const void *value)
+int ofnode_write_prop(ofnode node, const char *propname, const void *value,
+                     int len, bool copy)
 {
-       const struct device_node *np = ofnode_to_np(node);
-       struct property *pp;
-       struct property *pp_last = NULL;
-       struct property *new;
-
-       if (!of_live_active())
-               return -ENOSYS;
-
-       if (!np)
-               return -EINVAL;
+       if (of_live_active()) {
+               void *newval;
+               int ret;
 
-       for (pp = np->properties; pp; pp = pp->next) {
-               if (strcmp(pp->name, propname) == 0) {
-                       /* Property exists -> change value */
-                       pp->value = (void *)value;
-                       pp->length = len;
-                       return 0;
+               if (copy) {
+                       newval = malloc(len);
+                       if (!newval)
+                               return log_ret(-ENOMEM);
+                       memcpy(newval, value, len);
+                       value = newval;
                }
-               pp_last = pp;
-       }
-
-       if (!pp_last)
-               return -ENOENT;
-
-       /* Property does not exist -> append new property */
-       new = malloc(sizeof(struct property));
-       if (!new)
-               return -ENOMEM;
-
-       new->name = strdup(propname);
-       if (!new->name) {
-               free(new);
-               return -ENOMEM;
+               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);
        }
+}
 
-       new->value = (void *)value;
-       new->length = len;
-       new->next = NULL;
+int ofnode_write_string(ofnode node, const char *propname, const char *value)
+{
+       assert(ofnode_valid(node));
 
-       pp_last->next = new;
+       debug("%s: %s = %s", __func__, propname, value);
 
-       return 0;
+       return ofnode_write_prop(node, propname, value, strlen(value) + 1,
+                                false);
 }
 
-int ofnode_write_string(ofnode node, const char *propname, const char *value)
+int ofnode_write_u32(ofnode node, const char *propname, u32 value)
 {
-       if (!of_live_active())
-               return -ENOSYS;
+       fdt32_t *val;
 
        assert(ofnode_valid(node));
 
-       debug("%s: %s = %s", __func__, propname, value);
+       log_debug("%s = %x", propname, value);
+       val = malloc(sizeof(*val));
+       if (!val)
+               return -ENOMEM;
+       *val = cpu_to_fdt32(value);
 
-       return ofnode_write_prop(node, propname, strlen(value) + 1, value);
+       return ofnode_write_prop(node, propname, val, sizeof(value), false);
 }
 
 int ofnode_set_enabled(ofnode node, bool value)
 {
-       if (!of_live_active())
-               return -ENOSYS;
-
        assert(ofnode_valid(node));
 
        if (value)
@@ -1090,3 +1445,139 @@ int ofnode_set_enabled(ofnode node, bool value)
        else
                return ofnode_write_string(node, "status", "disabled");
 }
+
+bool ofnode_conf_read_bool(const char *prop_name)
+{
+       ofnode node;
+
+       node = ofnode_path("/config");
+       if (!ofnode_valid(node))
+               return false;
+
+       return ofnode_read_bool(node, prop_name);
+}
+
+int ofnode_conf_read_int(const char *prop_name, int default_val)
+{
+       ofnode node;
+
+       node = ofnode_path("/config");
+       if (!ofnode_valid(node))
+               return default_val;
+
+       return ofnode_read_u32_default(node, prop_name, default_val);
+}
+
+const char *ofnode_conf_read_str(const char *prop_name)
+{
+       ofnode node;
+
+       node = ofnode_path("/config");
+       if (!ofnode_valid(node))
+               return NULL;
+
+       return ofnode_read_string(node, prop_name);
+}
+
+ofnode ofnode_get_phy_node(ofnode node)
+{
+       /* DT node properties that reference a PHY node */
+       static const char * const phy_handle_str[] = {
+               "phy-handle", "phy", "phy-device",
+       };
+       struct ofnode_phandle_args args = {
+               .node = ofnode_null()
+       };
+       int i;
+
+       assert(ofnode_valid(node));
+
+       for (i = 0; i < ARRAY_SIZE(phy_handle_str); i++)
+               if (!ofnode_parse_phandle_with_args(node, phy_handle_str[i],
+                                                   NULL, 0, 0, &args))
+                       break;
+
+       return args.node;
+}
+
+phy_interface_t ofnode_read_phy_mode(ofnode node)
+{
+       const char *mode;
+       int i;
+
+       assert(ofnode_valid(node));
+
+       mode = ofnode_read_string(node, "phy-mode");
+       if (!mode)
+               mode = ofnode_read_string(node, "phy-connection-type");
+
+       if (!mode)
+               return PHY_INTERFACE_MODE_NA;
+
+       for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++)
+               if (!strcmp(mode, phy_interface_strings[i]))
+                       return i;
+
+       debug("%s: Invalid PHY interface '%s'\n", __func__, mode);
+
+       return PHY_INTERFACE_MODE_NA;
+}
+
+int ofnode_add_subnode(ofnode node, const char *name, ofnode *subnodep)
+{
+       ofnode subnode;
+       int ret = 0;
+
+       assert(ofnode_valid(node));
+
+       if (ofnode_is_np(node)) {
+               struct device_node *np, *child;
+
+               np = (struct device_node *)ofnode_to_np(node);
+               ret = of_add_subnode(np, name, -1, &child);
+               if (ret && ret != -EEXIST)
+                       return ret;
+               subnode = np_to_ofnode(child);
+       } else {
+               void *fdt = ofnode_to_fdt(node);
+               int poffset = ofnode_to_offset(node);
+               int offset;
+
+               offset = fdt_add_subnode(fdt, poffset, name);
+               if (offset == -FDT_ERR_EXISTS) {
+                       offset = fdt_subnode_offset(fdt, poffset, name);
+                       ret = -EEXIST;
+               }
+               if (offset < 0)
+                       return -EINVAL;
+               subnode = noffset_to_ofnode(node, offset);
+       }
+
+       *subnodep = subnode;
+
+       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;
+}