* 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
+ * 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
+ * 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
+ * @return node for that offset, with the correct ID
*/
static ofnode ofnode_from_tree_offset(oftree tree, int offset)
{
ofnode node;
- node.of_offset = offset;
+ 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;
}
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;
}
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");
}
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)
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)
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)
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;
+}