mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Pascal Vizeli <pvizeli@syshack.ch>
To: barebox@lists.infradead.org
Cc: Pascal Vizeli <pvizeli@syshack.ch>
Subject: [PATCH 2/2] drivers: of: implement overlay support
Date: Tue,  5 Jun 2018 22:50:35 +0000	[thread overview]
Message-ID: <20180605225035.1736-1-pvizeli@syshack.ch> (raw)

Origin patches are from Jan Luebbe.

This is based on Pantelis Antoniou's overlay support for the kernel. It
has been simplified by leaving out transaction support, locking and
other details required by the Linux DT support.

Pascal Vizeli fix some parts of resolver they was wrong ported from
linux source.

Signed-off-by: Jan Luebbe <jlu@pengutronix.de>
---
 Documentation/user/devicetree.rst     | 101 ++++++++-
 arch/sandbox/dts/Makefile             |   5 +-
 arch/sandbox/dts/sandbox-overlay.dtso |  26 +++
 arch/sandbox/dts/sandbox.dts          |   6 +
 commands/Kconfig                      |  10 +
 commands/Makefile                     |   1 +
 commands/of_overlay.c                 | 106 +++++++++
 drivers/of/Kconfig                    |   6 +
 drivers/of/Makefile                   |   1 +
 drivers/of/overlay.c                  | 234 +++++++++++++++++++
 drivers/of/resolver.c                 | 314 ++++++++++++++++++++++++++
 include/of.h                          |  71 ++++++
 scripts/Makefile.lib                  |   5 +-
 13 files changed, 882 insertions(+), 4 deletions(-)
 create mode 100644 arch/sandbox/dts/sandbox-overlay.dtso
 create mode 100644 commands/of_overlay.c
 create mode 100644 drivers/of/overlay.c
 create mode 100644 drivers/of/resolver.c

diff --git a/Documentation/user/devicetree.rst b/Documentation/user/devicetree.rst
index 17934d86e..8b7be4f5b 100644
--- a/Documentation/user/devicetree.rst
+++ b/Documentation/user/devicetree.rst
@@ -21,7 +21,7 @@ The internal devicetree
 -----------------------
 
 The devicetree consulted by barebox plays a special role. It is referred to
-as the "internal devicetree." The barebox devicetree commands work on this
+as the "internal devicetree". The barebox devicetree commands work on this
 devicetree. The devicetree source (DTS) files are kept in sync with the kernel DTS
 files. As the FDT files are meant to be backward compatible, it should always be possible
 to start a kernel with the barebox internal devicetree. However, since the barebox
@@ -83,3 +83,102 @@ you can exchange the internal devicetree during runtime using the
 
    oftree -f
    oftree -l /new/dtb
+
+Devicetree overlays
+-------------------
+
+Since version 3.19, the Linux kernel supports applying "devicetree overlays" to
+its loaded device tree. This can be used to inform the kernel about additional
+non-discoverable devices after the system has booted, which is useful for modular
+boards and FPGAs. The details of the overlay format are specified in the Linux
+`kernel documentation <https://www.kernel.org/doc/Documentation/devicetree/overlay-notes.txt>`_
+and an updated DTC is required to compile the overlays.
+
+The use cases for overlays in barebox are a bit different:
+
+* some of the modular devices are needed to boot Linux to userspace, but barebox
+  can detect which module variant is connected
+* one of several parallel or LVDS displays (which use timing data from devicetree)
+  can be connected to the SoC and should be used for boot messages
+* a generic Linux (distribution) kernel should be booted on a modular
+  system and support additional hardware on modules
+
+barebox supports applying overlays in the internal devicetree was well using the
+:ref:`command_oftree` command with option ``-o``:
+
+.. code-block:: none
+
+   $ ./barebox -d arch/sandbox/dts/sandbox.dtb -i arch/sandbox/dts/sandbox-overlay.dtbo
+   add fd0 backed by file arch/sandbox/dts/sandbox-overlay.dtbo
+
+   barebox 2015.02.0 #26 Wed Mar 4 09:41:19 CET 2015
+   ...
+   barebox@barebox sandbox:/ of_dump
+   ...
+     dummy@0 {
+       status = "disabled";
+       linux,phandle = <0x1>;
+       phandle = <0x1>;
+     };
+     dummy@1 {
+       status = "disabled";
+       linux,phandle = <0x2>;
+       phandle = <0x2>;
+     };
+     __symbols__ {
+       dummy0 = "/dummy@0";
+       dummy1 = "/dummy@1";
+     };
+   ...
+   barebox@barebox sandbox:/ of_dump -f /dev/fd0
+    {
+     fragment@0 {
+       target = <0xdeadbeef>;
+       __overlay__ {
+         status = "okay";
+         child {
+           compatible = "barebox,dummy";
+         };
+       };
+     };
+     fragment@1 {
+       target = <0xdeadbeef>;
+       __overlay__ {
+         status = "okay";
+         child {
+           compatible = "barebox,dummy";
+         };
+       };
+     };
+     __fixups__ {
+       dummy0 = "/fragment@0:target:0";
+       dummy1 = "/fragment@1:target:0";
+     };
+   };
+   barebox@barebox sandbox:/ of_overlay /dev/fd0
+   barebox@barebox sandbox:/ of_dump
+   ...
+     dummy@0 {
+       linux,phandle = <0x1>;
+       phandle = <0x1>;
+       status = "okay";
+       child {
+         compatible = "barebox,dummy";
+       };
+     };
+     dummy@1 {
+       linux,phandle = <0x2>;
+       phandle = <0x2>;
+       status = "okay";
+       child {
+         compatible = "barebox,dummy";
+       };
+     };
+     __symbols__ {
+       dummy0 = "/dummy@0";
+       dummy1 = "/dummy@1";
+     };
+   ...
+
+If you need to use a different base devicetree instead of the one compiled into
+barebox, it needs to be replaced as described in the previous section.
diff --git a/arch/sandbox/dts/Makefile b/arch/sandbox/dts/Makefile
index 6f6838857..ede219e6f 100644
--- a/arch/sandbox/dts/Makefile
+++ b/arch/sandbox/dts/Makefile
@@ -1,6 +1,7 @@
 ifeq ($(CONFIG_OFTREE),y)
 dtb-y += \
-	sandbox.dtb
+	sandbox.dtb \
+	sandbox-overlay.dtbo
 endif
 
 # just to build a built-in.o. Otherwise compilation fails when no devicetree is
@@ -8,4 +9,4 @@ endif
 obj- += dummy.o
 
 always := $(dtb-y)
-clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts
+clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtbo
diff --git a/arch/sandbox/dts/sandbox-overlay.dtso b/arch/sandbox/dts/sandbox-overlay.dtso
new file mode 100644
index 000000000..f9ced04ca
--- /dev/null
+++ b/arch/sandbox/dts/sandbox-overlay.dtso
@@ -0,0 +1,26 @@
+/dts-v1/;
+
+/plugin/;
+
+/ {
+        fragment@0 {
+                target = <&dummy0>;
+                __overlay__ {
+			status = "okay";
+                        child {
+                                compatible = "barebox,dummy";
+                        };
+                };
+        };
+
+        fragment@1 {
+                target = <&dummy1>;
+                __overlay__ {
+			status = "okay";
+                        child {
+                                compatible = "barebox,dummy";
+                        };
+                };
+        };
+};
+
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts
index 2595aa13f..1b99a4960 100644
--- a/arch/sandbox/dts/sandbox.dts
+++ b/arch/sandbox/dts/sandbox.dts
@@ -3,5 +3,11 @@
 #include "skeleton.dtsi"
 
 / {
+	dummy0: dummy@0 {
+		status = "disabled";
+	};
 
+	dummy1: dummy@1 {
+		status = "disabled";
+	};
 };
diff --git a/commands/Kconfig b/commands/Kconfig
index 951a86963..22d131da3 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -2033,6 +2033,16 @@ config CMD_OF_NODE
 		  -c	create a new node
 		  -d	delete a node
 
+config CMD_OF_OVERLAY
+	tristate
+	select OFTREE
+	select OFTREE_OVERLAY
+	prompt "of_overlay"
+	help
+	  Apply a dtb overlay to internal device tree
+
+	  Usage: of_overlay DTBO
+
 config CMD_OF_PROPERTY
 	tristate
 	select OFTREE
diff --git a/commands/Makefile b/commands/Makefile
index eb4796389..f404338fa 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_CMD_USB)		+= usb.o
 obj-$(CONFIG_CMD_TIME)		+= time.o
 obj-$(CONFIG_CMD_OFTREE)	+= oftree.o
 obj-$(CONFIG_CMD_OF_PROPERTY)	+= of_property.o
+obj-$(CONFIG_CMD_OF_OVERLAY)	+= of_overlay.o
 obj-$(CONFIG_CMD_OF_NODE)	+= of_node.o
 obj-$(CONFIG_CMD_OF_DUMP)	+= of_dump.o
 obj-$(CONFIG_CMD_OF_DISPLAY_TIMINGS)	+= of_display_timings.o
diff --git a/commands/of_overlay.c b/commands/of_overlay.c
new file mode 100644
index 000000000..1b3e857bb
--- /dev/null
+++ b/commands/of_overlay.c
@@ -0,0 +1,106 @@
+/*
+ * of_overlay.c - device tree overlay command support
+ *
+ * Copyright (C) 2015 Pengutronix, Jan Luebbe <jlu@pengutronix.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <environment.h>
+#include <fdt.h>
+#include <libfile.h>
+#include <of.h>
+#include <command.h>
+#include <fs.h>
+#include <malloc.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <asm/byteorder.h>
+#include <errno.h>
+#include <getopt.h>
+#include <init.h>
+#include <fcntl.h>
+#include <complete.h>
+
+static int do_of_overlay(int argc, char *argv[])
+{
+	struct fdt_header *fdt = NULL;
+	size_t size;
+	char *filename = NULL;
+	int ret = 0, ovinfo_cnt;
+	struct device_node *overlay = NULL, *root;
+	struct of_overlay_info *ovinfo;
+
+	if (argc != 2)
+		return COMMAND_ERROR_USAGE;
+
+	filename = argv[1];
+
+	root = of_get_root_node();
+	if (!root) {
+		printf("no oftree loaded\n");
+		return 1;
+	}
+
+	fdt = read_file(filename, &size);
+	if (!fdt) {
+		printf("unable to read %s\n", filename);
+		return 1;
+	}
+
+	overlay = of_unflatten_dtb(fdt);
+	free(fdt);
+
+	if (IS_ERR(overlay)) {
+		printf("parse oftree: %s\n", strerror(-ret));
+		return PTR_ERR(overlay);
+	}
+
+	ret = of_resolve(overlay);
+	if (ret != 0) {
+		printf("resolve oftree overlay: %s\n", strerror(-ret));
+		goto out;
+	}
+
+	ret = of_build_overlay_info(overlay, &ovinfo_cnt, &ovinfo);
+	if (ret != 0) {
+		printf("prepare oftree overlay: %s\n", strerror(-ret));
+		goto out;
+	}
+
+	ret = of_overlay(ovinfo_cnt, ovinfo);
+	if (ret != 0) {
+		printf("apply oftree overlay: %s\n", strerror(-ret));
+		goto out;
+	}
+
+	ret = 0;
+
+out:
+	of_delete_node(overlay);
+	return ret;
+}
+
+BAREBOX_CMD_HELP_START(of_overlay)
+BAREBOX_CMD_HELP_TEXT("Apply a dtb overlay to internal device tree")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(of_overlay)
+	.cmd		= do_of_overlay,
+	BAREBOX_CMD_DESC("Apply a dtb overlay")
+	BAREBOX_CMD_OPTS("DTBO")
+	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+	BAREBOX_CMD_HELP(cmd_of_overlay_help)
+BAREBOX_CMD_END
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index a1fac0e61..d90bd9c6b 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -15,6 +15,12 @@ config OFDEVICE
 	select DTC
 	bool "Enable probing of devices from the devicetree"
 
+config OFTREE_OVERLAY
+	bool "Enable support for devicetree overlays"
+	depends on OFTREE
+	help
+	  Allows you to modify the live tree using overlays.
+
 config OF_ADDRESS_PCI
 	bool
 
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index ec4387006..323543461 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -6,3 +6,4 @@ obj-y += partition.o
 obj-y += of_net.o
 obj-$(CONFIG_MTD) += of_mtd.o
 obj-$(CONFIG_OF_BAREBOX_DRIVERS) += barebox.o
+obj-$(CONFIG_OFTREE_OVERLAY) += resolver.o overlay.o
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
new file mode 100644
index 000000000..a711a353f
--- /dev/null
+++ b/drivers/of/overlay.c
@@ -0,0 +1,234 @@
+/*
+ * Functions for working with device tree overlays
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2015 Pengutronix, Jan Luebbe <jlu@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <of.h>
+#include <errno.h>
+#include <malloc.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+
+/**
+ * Apply a single overlay node recursively.
+ *
+ * All the property notifiers are appropriately called.
+ * Note that the in case of an error the target node is left
+ * in a inconsistent state. Error recovery should be performed
+ * by recording the modification using the of notifiers.
+ */
+static int of_overlay_apply_one(struct device_node *target,
+		const struct device_node *overlay)
+{
+	struct device_node *child, *tchild;
+	struct property *prop;
+	int ret;
+
+	/* sanity checks */
+	if (target == NULL || overlay == NULL)
+		return -EINVAL;
+
+	for_each_property_of_node(overlay, prop) {
+		/* don't touch, 'name' */
+		if (of_prop_cmp(prop->name, "name") == 0)
+			continue;
+
+		of_delete_property(of_find_property(target, prop->name, NULL));
+
+		if (of_new_property(target, prop->name, prop->value, prop->length) == NULL)
+			return -ENOMEM;
+	}
+
+	for_each_child_of_node(overlay, child) {
+		tchild = of_get_child_by_name(target, child->name);
+		if (tchild != NULL) {
+			/* apply overlay recursively */
+			ret = of_overlay_apply_one(tchild, child);
+
+			if (ret != 0)
+				return ret;
+		} else {
+			/* create new child */
+			tchild = of_new_node(target, child->name);
+			if (tchild == NULL)
+				return -ENOMEM;
+
+			/* apply the overlay */
+			ret = of_overlay_apply_one(tchild, child);
+			if (ret != 0) {
+				of_delete_node(tchild);
+				return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * of_overlay	- Apply @count overlays pointed at by @ovinfo_tab
+ * @count:	Number of of_overlay_info's
+ * @ovinfo_tab:	Array of overlay_info's to apply
+ *
+ * Applies the overlays given, while handling all error conditions
+ * appropriately. Either the operation succeeds, or if it fails the
+ * live tree is reverted to the state before the attempt.
+ * Returns 0, or an error if the overlay attempt failed.
+ */
+int of_overlay(int count, struct of_overlay_info *ovinfo_tab)
+{
+	struct of_overlay_info *ovinfo;
+	int i, err;
+
+	if (!ovinfo_tab)
+		return -EINVAL;
+
+	for (i = 0; i < count; i++) {
+		ovinfo = &ovinfo_tab[i];
+		err = of_overlay_apply_one(ovinfo->target, ovinfo->overlay);
+		if (err != 0) {
+			pr_err("%s: overlay failed '%s'\n",
+				__func__, ovinfo->target->full_name);
+			goto err_fail;
+		}
+	}
+
+	return 0;
+
+err_fail:
+	return err;
+}
+
+/**
+ * of_fill_overlay_info	- Fill an overlay info structure
+ * @info_node:	Device node containing the overlay
+ * @ovinfo:	Pointer to the overlay info structure to fill
+ *
+ * Fills an overlay info structure with the overlay information
+ * from a device node. This device node must have a target property
+ * which contains a phandle of the overlay target node, and an
+ * __overlay__ child node which has the overlay contents.
+ * Both ovinfo->target & ovinfo->overlay have their references taken.
+ *
+ * Returns 0 on success, or a negative error value.
+ */
+int of_fill_overlay_info(struct device_node *info_node,
+		struct of_overlay_info *ovinfo)
+{
+	const char *target_path;
+	u32 target_handle;
+	int ret;
+
+	if (!info_node || !ovinfo)
+		return -EINVAL;
+
+	ret = of_property_read_u32(info_node, "target", &target_handle);
+	if (ret == 0) {
+		ovinfo->target = of_find_node_by_phandle(target_handle);
+	} else {
+		ret = of_property_read_string(info_node, "target-path", &target_path);
+		if (ret == 0) {
+			ovinfo->target = of_find_node_by_path(target_path);
+		}
+	}
+	if (ovinfo->target == NULL)
+		goto err_fail;
+
+	ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__");
+	if (ovinfo->overlay == NULL)
+		goto err_fail;
+
+	return 0;
+
+err_fail:
+	memset(ovinfo, 0, sizeof(*ovinfo));
+	return -EINVAL;
+}
+
+/**
+ * of_build_overlay_info	- Build an overlay info array
+ * @tree:	Device node containing all the overlays
+ * @cntp:	Pointer to where the overlay info count will be help
+ * @ovinfop:	Pointer to the pointer of an overlay info structure.
+ *
+ * Helper function that given a tree containing overlay information,
+ * allocates and builds an overlay info array containing it, ready
+ * for use using of_overlay.
+ *
+ * Returns 0 on success with the @cntp @ovinfop pointers valid,
+ * while on error a negative error value is returned.
+ */
+int of_build_overlay_info(struct device_node *tree,
+		int *cntp, struct of_overlay_info **ovinfop)
+{
+	struct device_node *node;
+	struct of_overlay_info *ovinfo;
+	int cnt, err;
+
+	if (tree == NULL || cntp == NULL || ovinfop == NULL)
+		return -EINVAL;
+
+	/* worst case; every child is a node */
+	cnt = 0;
+	for_each_child_of_node(tree, node)
+		cnt++;
+
+	ovinfo = kzalloc(cnt * sizeof(*ovinfo), GFP_KERNEL);
+	if (ovinfo == NULL)
+		return -ENOMEM;
+
+	cnt = 0;
+	for_each_child_of_node(tree, node) {
+		memset(&ovinfo[cnt], 0, sizeof(*ovinfo));
+		err = of_fill_overlay_info(node, &ovinfo[cnt]);
+		if (err == 0)
+			cnt++;
+	}
+
+	/* if nothing filled, return error */
+	if (cnt == 0) {
+		kfree(ovinfo);
+		return -ENODEV;
+	}
+
+	*cntp = cnt;
+	*ovinfop = ovinfo;
+
+	return 0;
+}
+
+/**
+ * of_free_overlay_info	- Free an overlay info array
+ * @count:	Number of of_overlay_info's
+ * @ovinfo_tab:	Array of overlay_info's to free
+ *
+ * Releases the memory of a previously allocate ovinfo array
+ * by of_build_overlay_info.
+ * Returns 0, or an error if the arguments are bogus.
+ */
+int of_free_overlay_info(int count, struct of_overlay_info *ovinfo_tab)
+{
+	struct of_overlay_info *ovinfo;
+	int i;
+
+	if (!ovinfo_tab || count < 0)
+		return -EINVAL;
+
+	/* do it in reverse */
+	for (i = count - 1; i >= 0; i--)
+		ovinfo = &ovinfo_tab[i];
+
+	kfree(ovinfo_tab);
+
+	return 0;
+}
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
new file mode 100644
index 000000000..7d159265b
--- /dev/null
+++ b/drivers/of/resolver.c
@@ -0,0 +1,314 @@
+/*
+ * Functions for dealing with DT resolution
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2015 Pengutronix, Jan Luebbe <jlu@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <common.h>
+#include <of.h>
+#include <errno.h>
+#include <malloc.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+/**
+ * Adjust a subtree's phandle values by a given delta.
+ * Makes sure not to just adjust the device node's phandle value,
+ * but modify the phandle properties values as well.
+ */
+static void of_adjust_tree_phandles(struct device_node *node,
+		int phandle_delta)
+{
+	struct device_node *child;
+	struct property *prop;
+	phandle phandle;
+
+	/* first adjust the node's phandle direct value */
+	if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
+		node->phandle += phandle_delta;
+
+	/* now adjust phandle & linux,phandle values */
+	for_each_property_of_node(node, prop) {
+		/* only look for these two */
+		if (of_prop_cmp(prop->name, "phandle") != 0 &&
+		    of_prop_cmp(prop->name, "linux,phandle") != 0)
+			continue;
+
+		/* must be big enough */
+		if (prop->length < 4)
+			continue;
+
+		/* read phandle value */
+		phandle = be32_to_cpu(*(uint32_t *)prop->value);
+		if (phandle == OF_PHANDLE_ILLEGAL)	/* unresolved */
+			continue;
+
+		/* adjust */
+		*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
+	}
+
+	/* now do the children recursively */
+	for_each_child_of_node(node, child)
+		of_adjust_tree_phandles(child, phandle_delta);
+}
+
+/**
+ * Adjust the local phandle references by the given phandle delta.
+ * Assumes the existances of a __local_fixups__ node at the root
+ * of the tree. Does not take any devtree locks so make sure you
+ * call this on a tree which is at the detached state.
+ */
+static int of_adjust_tree_phandle_references(struct device_node *local_fixups,
+		struct device_node *overlay, int phandle_delta)
+{
+	phandle phandle;
+	struct device_node *child, *overlay_child = NULL;
+	struct property *fixprop, *prop = NULL;
+	int i, count, err;
+	unsigned int off;
+
+	if (!local_fixups)
+		return 0;
+
+	for_each_property_of_node(local_fixups, fixprop) {
+		/* skip properties added automatically */
+		if (!of_prop_cmp(fixprop->name, "name") ||
+		    !of_prop_cmp(fixprop->name, "phandle") ||
+		    !of_prop_cmp(fixprop->name, "linux,phandle"))
+			continue;
+
+		if ((fixprop->length % 4) != 0 || fixprop->length == 0)
+			return -EINVAL;
+		count = fixprop->length / sizeof(uint32_t);
+
+		for_each_property_of_node(overlay, prop) {
+			if (!of_prop_cmp(fixprop->name, prop->name))
+				break;
+		}
+
+		if (!prop)
+			return -EINVAL;
+
+		for (i=0; i < count; i++) {
+			off = be32_to_cpu(((uint32_t *)fixprop->value)[i]);
+			if ((off + 4) > prop->length)
+				return -EINVAL;
+
+			phandle = be32_to_cpu(*(uint32_t *)(prop->value + off));
+			phandle += phandle_delta;
+			*(uint32_t *)(prop->value + off) = cpu_to_be32(phandle);
+		}
+	}
+
+	/*
+	 * These nested loops recurse down two subtrees in parallel, where the
+	 * node names in the two subtrees match.
+	 *
+	 * The roots of the subtrees are the overlay's __local_fixups__ node
+	 * and the overlay's root node.
+	 */
+	for_each_child_of_node(local_fixups, child) {
+
+		for_each_child_of_node(overlay, overlay_child)
+			if (!of_node_cmp(child->name, overlay_child->name))
+				break;
+
+		if (!overlay_child)
+			return -EINVAL;
+
+		err = of_adjust_tree_phandle_references(child, overlay_child,
+				phandle_delta);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+/**
+ * of_resolve	- Resolve the given node against the live tree.
+ *
+ * @resolve:	Node to resolve
+ *
+ * Perform dynamic Device Tree resolution against the live tree
+ * to the given node to resolve. This depends on the live tree
+ * having a __symbols__ node, and the resolve node the __fixups__ &
+ * __local_fixups__ nodes (if needed).
+ * The result of the operation is a resolve node that it's contents
+ * are fit to be inserted or operate upon the live tree.
+ * Returns 0 on success or a negative error value on error.
+ */
+int of_resolve(struct device_node *resolve)
+{
+	struct device_node *child, *refnode, *local_fixups = NULL;
+	struct device_node *root_sym, *resolve_sym, *resolve_fix;
+	struct property *rprop, *sprop;
+	const char *refpath;
+	char *propval, *propcur, *propend, *nodestr, *propstr, *s;
+	int offset, propcurlen;
+	phandle phandle, phandle_delta;
+	int err;
+	bool found = false;
+
+	/* the resolve node must exist */
+	if (resolve == NULL) {
+		return -EINVAL;
+	}
+
+	/* first we need to adjust the phandles */
+	phandle_delta = of_get_tree_max_phandle(NULL) + 1;
+	of_adjust_tree_phandles(resolve, phandle_delta);
+
+	/* second we need lookup local fixups of phandles */
+	local_fixups = of_find_node_by_name(resolve, "__local_fixups__");
+	err = of_adjust_tree_phandle_references(local_fixups, resolve,
+			phandle_delta);
+
+	if (err != 0)
+		return err;
+
+	root_sym = NULL;
+	resolve_sym = NULL;
+	resolve_fix = NULL;
+
+	/* this may fail (if no fixups are required) */
+	root_sym = of_find_node_by_path("/__symbols__");
+
+	/* locate the symbols & fixups nodes on resolve */
+	for_each_child_of_node(resolve, child) {
+		if (resolve_sym == NULL &&
+				of_node_cmp(child->name, "__symbols__") == 0)
+			resolve_sym = child;
+
+		if (resolve_fix == NULL &&
+				of_node_cmp(child->name, "__fixups__") == 0)
+			resolve_fix = child;
+
+		/* both found, don't bother anymore */
+		if (resolve_sym != NULL && resolve_fix != NULL)
+			break;
+	}
+
+	/* we do allow for the case where no fixups are needed */
+	if (resolve_fix == NULL)
+		goto merge_sym;
+
+	/* we need to fixup, but no root symbols... */
+	if (root_sym == NULL)
+		return -EINVAL;
+
+	for_each_property_of_node(resolve_fix, rprop) {
+		/* skip properties added automatically */
+		if (of_prop_cmp(rprop->name, "name") == 0)
+			continue;
+
+		err = of_property_read_string(root_sym,
+				rprop->name, &refpath);
+		if (err != 0) {
+			pr_err("%s: Could not find symbol '%s'\n",
+					__func__, rprop->name);
+			goto err_fail;
+		}
+
+		refnode = of_find_node_by_path(refpath);
+		if (refnode == NULL) {
+			pr_err("%s: Could not find node by path '%s'\n",
+					__func__, refpath);
+			err = -ENOENT;
+			goto err_fail;
+		}
+
+		phandle = refnode->phandle;
+
+		pr_debug("%s: %s phandle is 0x%08x\n",
+				__func__, rprop->name, phandle);
+
+		/* make a copy */
+		propval = kmalloc(rprop->length, GFP_KERNEL);
+		if (propval == NULL) {
+			pr_err("%s: Could not copy value of '%s'\n",
+					__func__, rprop->name);
+			err = -ENOMEM;
+			goto err_fail;
+		}
+
+		memcpy(propval, rprop->value, rprop->length);
+
+		propend = propval + rprop->length;
+		for (propcur = propval; propcur < propend;
+				propcur += propcurlen + 1) {
+			propcurlen = strlen(propcur);
+
+			nodestr = propcur;
+			s = strchr(propcur, ':');
+			if (s == NULL) {
+				pr_err("%s: Illegal symbol "
+					"entry '%s' (1)\n",
+					__func__, (char *)rprop->value);
+				kfree(propval);
+				err = -EINVAL;
+				goto err_fail;
+			}
+			*s++ = '\0';
+
+			propstr = s;
+			s = strchr(s, ':');
+			if (s == NULL) {
+				pr_err("%s: Illegal symbol "
+					"entry '%s' (2)\n",
+					__func__, (char *)rprop->value);
+				kfree(propval);
+				err = -EINVAL;
+				goto err_fail;
+			}
+
+			*s++ = '\0';
+			offset = simple_strtoul(s, NULL, 10);
+
+			/* look into the resolve node for the full path */
+			refnode = of_find_node_by_path_from(resolve, nodestr);
+			if (refnode == NULL) {
+				pr_err("%s: Could not find refnode '%s'\n",
+					__func__, (char *)rprop->value);
+				kfree(propval);
+				err = -ENOENT;
+				goto err_fail;
+			}
+
+			/* now find the property */
+			found = false;
+			for_each_property_of_node(refnode, sprop)
+				if (of_prop_cmp(sprop->name, propstr) == 0) {
+					found = true;
+					break;
+				}
+
+			if (!found) {
+				pr_err("%s: Could not find property '%s'\n",
+					__func__, (char *)rprop->value);
+				kfree(propval);
+				err = -ENOENT;
+				goto err_fail;
+			}
+
+			*(uint32_t *)(sprop->value + offset) =
+				cpu_to_be32(phandle);
+		}
+
+		kfree(propval);
+	}
+
+merge_sym:
+	return 0;
+
+err_fail:
+	return err;
+}
diff --git a/include/of.h b/include/of.h
index fec51bb94..91476ae47 100644
--- a/include/of.h
+++ b/include/of.h
@@ -730,6 +730,8 @@ static inline struct device_node *of_find_matching_node(
 #define for_each_available_child_of_node(parent, child) \
 	for (child = of_get_next_available_child(parent, NULL); child != NULL; \
 	     child = of_get_next_available_child(parent, child))
+#define for_each_property_of_node(dn, pp) \
+	list_for_each_entry(pp, &dn->properties, list)
 
 /**
  * of_property_read_bool - Findfrom a property
@@ -850,4 +852,73 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
 
 	return node;
 }
+
+/* illegal phandle value (set when unresolved) */
+#define OF_PHANDLE_ILLEGAL      0xdeadbeef
+
+#ifdef CONFIG_OFTREE_OVERLAY
+
+extern int of_resolve(struct device_node *resolve);
+
+#else
+
+static inline int of_resolve(struct device_node *resolve)
+{
+	return -ENOSYS;
+}
+
+#endif
+
+/**
+ * Overlay support
+ */
+
+/**
+ * struct of_overlay_info       - Holds a single overlay info
+ * @target:     target of the overlay operation
+ * @overlay:    pointer to the overlay contents node
+ *
+ * Holds a single overlay state.
+ */
+struct of_overlay_info {
+	struct device_node *target;
+	struct device_node *overlay;
+};
+
+#ifdef CONFIG_OFTREE_OVERLAY
+
+extern int of_overlay(int count, struct of_overlay_info *ovinfo_tab);
+
+extern int of_fill_overlay_info(struct device_node *info_node,
+		struct of_overlay_info *ovinfo);
+extern int of_build_overlay_info(struct device_node *tree,
+		int *cntp, struct of_overlay_info **ovinfop);
+extern int of_free_overlay_info(int cnt, struct of_overlay_info *ovinfo);
+
+#else
+
+static inline int of_overlay(int count, struct of_overlay_info *ovinfo_tab)
+{
+	return -ENOSYS;
+}
+
+static inline int of_fill_overlay_info(struct device_node *info_node,
+		struct of_overlay_info *ovinfo)
+{
+	return -ENOSYS;
+}
+
+static inline int of_build_overlay_info(struct device_node *tree,
+		int *cntp, struct of_overlay_info **ovinfop)
+{
+	return -ENOSYS;
+}
+
+static inline int of_free_overlay_info(int cnt, struct of_overlay_info *ovinfo)
+{
+	return -ENOSYS;
+}
+
+#endif
+
 #endif /* __OF_H */
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 272b5981e..e7cea55c4 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -261,7 +261,7 @@ $(obj)/%.dtb.S: $(obj)/%.dtb $(srctree)/scripts/gen-dtb-s FORCE
 
 quiet_cmd_dtc = DTC     $@
 cmd_dtc = $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
-	$(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 \
+	$(objtree)/scripts/dtc/dtc -@ -O dtb -o $@ -b 0 \
 		-i $(srctree)/arch/$(SRCARCH)/dts $(DTC_FLAGS) \
 		-i $(srctree)/dts/src/$(SRCARCH) \
 		-d $(depfile).dtc $(dtc-tmp) ; \
@@ -270,6 +270,9 @@ cmd_dtc = $(CPP) $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
 $(obj)/%.dtb: $(src)/%.dts FORCE
 	$(call if_changed_dep,dtc)
 
+$(obj)/%.dtbo: $(src)/%.dtso FORCE
+	$(call if_changed_dep,dtc)
+
 dtc-tmp = $(subst $(comma),_,$(dot-target).dts)
 
 obj-y += $(patsubst %,%.bbenv$(DEFAULT_COMPRESSION_SUFFIX).o,$(bbenv-y))
-- 
2.17.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

                 reply	other threads:[~2018-06-05 22:50 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180605225035.1736-1-pvizeli@syshack.ch \
    --to=pvizeli@syshack.ch \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox