summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/user/booting-linux.rst4
-rw-r--r--commands/Kconfig12
-rw-r--r--commands/Makefile1
-rw-r--r--commands/of_overlay.c89
-rw-r--r--common/blspec.c78
-rw-r--r--common/firmware.c18
-rw-r--r--drivers/of/Kconfig25
-rw-r--r--drivers/of/Makefile1
-rw-r--r--drivers/of/of_firmware.c86
-rw-r--r--drivers/of/overlay.c247
-rw-r--r--drivers/of/resolver.c279
-rw-r--r--include/firmware.h15
-rw-r--r--include/of.h47
-rw-r--r--scripts/Makefile.lib4
14 files changed, 906 insertions, 0 deletions
diff --git a/Documentation/user/booting-linux.rst b/Documentation/user/booting-linux.rst
index 437f4e80ca..12cd505e71 100644
--- a/Documentation/user/booting-linux.rst
+++ b/Documentation/user/booting-linux.rst
@@ -232,6 +232,10 @@ device where the entry is found on. This makes it possible to use the same rootf
image on different devices without having to specify a different root= option each
time.
+Additionally to the options defined in the original spec, Barebox has the
+``devicetree-overlay`` option. This is a string value that refer to overlays
+that will be applied to the device tree before passing it to Linux.
+
Network boot
------------
diff --git a/commands/Kconfig b/commands/Kconfig
index ad65fec409..0189b4715b 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -2106,6 +2106,18 @@ config CMD_OF_FIXUP_STATUS
Register a fixup to enable or disable a device tree node.
Nodes are enabled on default. Disabled with -d.
+config CMD_OF_OVERLAY
+ tristate
+ select OF_OVERLAY
+ prompt "of_overlay"
+ help
+ of_overlay - register device tree overlay as fixup
+
+ Usage: of_overlay [-S path] FILE
+
+ Options:
+ -S path load firmware using this search path
+
config CMD_OFTREE
tristate
select OFTREE
diff --git a/commands/Makefile b/commands/Makefile
index 85adc32769..2f0980185c 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -79,6 +79,7 @@ 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
obj-$(CONFIG_CMD_OF_FIXUP_STATUS) += of_fixup_status.o
+obj-$(CONFIG_CMD_OF_OVERLAY) += of_overlay.o
obj-$(CONFIG_CMD_MAGICVAR) += magicvar.o
obj-$(CONFIG_CMD_IOMEM) += iomemport.o
obj-$(CONFIG_CMD_LINUX_EXEC) += linux_exec.o
diff --git a/commands/of_overlay.c b/commands/of_overlay.c
new file mode 100644
index 0000000000..de3c3dcee5
--- /dev/null
+++ b/commands/of_overlay.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Michael Tretter <m.tretter@pengutronix.de>, Pengutronix
+ *
+ * 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 <command.h>
+#include <common.h>
+#include <environment.h>
+#include <fdt.h>
+#include <firmware.h>
+#include <fs.h>
+#include <getopt.h>
+#include <libfile.h>
+#include <of.h>
+
+static int do_of_overlay(int argc, char *argv[])
+{
+ int opt, ret;
+ struct fdt_header *fdt;
+ struct device_node *overlay;
+ const char *search_path = NULL;
+
+ while ((opt = getopt(argc, argv, "S:")) > 0) {
+ switch (opt) {
+ case 'S':
+ search_path = optarg;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ if (argc != optind + 1)
+ return COMMAND_ERROR_USAGE;
+
+ fdt = read_file(argv[optind], NULL);
+ if (!fdt) {
+ printf("cannot read %s\n", argv[optind]);
+ return 1;
+ }
+
+ overlay = of_unflatten_dtb(fdt);
+ free(fdt);
+ if (IS_ERR(overlay))
+ return PTR_ERR(overlay);
+
+ if (search_path) {
+ ret = of_firmware_load_overlay(overlay, search_path);
+ if (ret)
+ goto err;
+ }
+
+ ret = of_register_overlay(overlay);
+ if (ret) {
+ printf("cannot apply oftree overlay: %s\n", strerror(-ret));
+ goto err;
+ }
+
+ return 0;
+
+err:
+ of_delete_node(overlay);
+ return ret;
+}
+
+BAREBOX_CMD_HELP_START(of_overlay)
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT("-S path", "load firmware using this search path")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(of_overlay)
+ .cmd = do_of_overlay,
+ BAREBOX_CMD_DESC("register device tree overlay as fixup")
+ BAREBOX_CMD_OPTS("[-S path] FILE")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_of_overlay_help)
+BAREBOX_CMD_END
diff --git a/common/blspec.c b/common/blspec.c
index 83b05993dd..7fb62d310f 100644
--- a/common/blspec.c
+++ b/common/blspec.c
@@ -14,6 +14,7 @@
#include <environment.h>
#include <globalvar.h>
+#include <firmware.h>
#include <readkey.h>
#include <common.h>
#include <driver.h>
@@ -42,6 +43,78 @@ int blspec_entry_var_set(struct blspec_entry *entry, const char *name,
val ? strlen(val) + 1 : 0, 1);
}
+static int blspec_apply_oftree_overlay(char *file, const char *abspath,
+ int dryrun)
+{
+ int ret = 0;
+ struct fdt_header *fdt;
+ struct device_node *overlay;
+ char *path;
+ char *firmware_path;
+
+ path = basprintf("%s/%s", abspath, file);
+
+ fdt = read_file(path, NULL);
+ if (!fdt) {
+ pr_warn("unable to read \"%s\"\n", path);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ overlay = of_unflatten_dtb(fdt);
+ free(fdt);
+ if (IS_ERR(overlay)) {
+ ret = PTR_ERR(overlay);
+ goto out;
+ }
+
+ if (dryrun) {
+ pr_info("dry run: skip overlay %s\n", path);
+ of_delete_node(overlay);
+ goto out;
+ }
+
+ /*
+ * Unfortunately the device tree overlay contains only the filename of
+ * the firmware and relies on the firmware search paths to find the
+ * actual file. Use /lib/firmware in the Linux root directory and hope
+ * for the best.
+ */
+ firmware_path = basprintf("%s/%s", abspath, "/lib/firmware");
+ ret = of_firmware_load_overlay(overlay, firmware_path);
+ free(firmware_path);
+ if (ret) {
+ pr_warn("failed to load firmware: skip overlay \"%s\"\n", path);
+ of_delete_node(overlay);
+ goto out;
+ }
+
+ ret = of_register_overlay(overlay);
+ if (ret) {
+ pr_warn("cannot register devicetree overlay \"%s\"\n", path);
+ of_delete_node(overlay);
+ }
+
+out:
+ free(path);
+
+ return ret;
+}
+
+static void blspec_apply_oftree_overlays(const char *overlays,
+ const char *abspath, int dryrun)
+{
+ char *overlay;
+ char *sep, *freep;
+
+ sep = freep = xstrdup(overlays);
+
+ while ((overlay = strsep(&sep, " ")))
+ blspec_apply_oftree_overlay(overlay, abspath, dryrun);
+
+ free(freep);
+}
+
/*
* blspec_boot - boot an entry
*
@@ -54,6 +127,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
struct blspec_entry *entry = container_of(be, struct blspec_entry, entry);
int ret;
const char *abspath, *devicetree, *options, *initrd, *linuximage;
+ const char *overlays;
const char *appendroot;
struct bootm_data data = {
.initrd_address = UIMAGE_INVALID_ADDRESS,
@@ -73,6 +147,7 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
initrd = blspec_entry_var_get(entry, "initrd");
options = blspec_entry_var_get(entry, "options");
linuximage = blspec_entry_var_get(entry, "linux");
+ overlays = blspec_entry_var_get(entry, "devicetree-overlay");
if (entry->rootpath)
abspath = entry->rootpath;
@@ -92,6 +167,9 @@ static int blspec_boot(struct bootentry *be, int verbose, int dryrun)
}
}
+ if (overlays)
+ blspec_apply_oftree_overlays(overlays, abspath, dryrun);
+
if (initrd)
data.initrd_file = basprintf("%s/%s", abspath, initrd);
diff --git a/common/firmware.c b/common/firmware.c
index 9d55d73e7a..609cf11822 100644
--- a/common/firmware.c
+++ b/common/firmware.c
@@ -63,6 +63,24 @@ struct firmware_mgr *firmwaremgr_find(const char *id)
}
/*
+ * firmwaremgr_find_by_node - find a firmware device handler
+ *
+ * Find a firmware device handler using the device node of the firmware
+ * handler. This allows to retrieve the firmware handler with a phandle from
+ * the device tree.
+ */
+struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np)
+{
+ struct firmware_mgr *mgr;
+
+ list_for_each_entry(mgr, &firmwaremgr_list, list)
+ if (mgr->handler->dev->parent->device_node == np)
+ return mgr;
+
+ return NULL;
+}
+
+/*
* firmwaremgr_list_handlers - list registered firmware device handlers
* in pretty format
*/
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 24cf4465a8..7436fc2de1 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -50,3 +50,28 @@ config OF_BAREBOX_ENV_IN_FS
help
Allow the devie tree configuration of the barebox environment path
to specify a file in filesystem, which will be mounted.
+
+config OF_OVERLAY
+ select OFTREE
+ bool "Devicetree overlays"
+ help
+ Overlays allow to patch the devicetree. Unlike Linux, Barebox does
+ not patch the live devicetree, but applies the overlays as fixup to
+ the devicetree. Furthermore, overlays cannot be removed after they
+ have been applied.
+
+config OF_OVERLAY_LIVE
+ depends on OF_OVERLAY
+ bool "Support devicetree overlays on live devicetree"
+ help
+ This option allows to use devicetree overlays with the live
+ devicetree. It is not required to apply overlays to any other
+ devicetree.
+
+ This builds the build-in devicetree with __symbols__, which
+ significantly increases the size of the dtb file.
+
+ Enable this option only if you actually need the live devicetree
+ while applying in the devicetree overlay. This is usually the case if
+ applying the overlay has other side effects than changing the
+ devicetree.
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index ec43870061..b6847752d2 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_OF_OVERLAY) += overlay.o resolver.o of_firmware.o
diff --git a/drivers/of/of_firmware.c b/drivers/of/of_firmware.c
new file mode 100644
index 0000000000..0135631fb8
--- /dev/null
+++ b/drivers/of/of_firmware.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
+ */
+#include <common.h>
+#include <firmware.h>
+#include <of.h>
+
+struct overlay_info {
+ const char *firmware_path;
+};
+
+static struct firmware_mgr *of_node_get_mgr(struct device_node *np)
+{
+ struct device_node *mgr_node;
+
+ do {
+ if (of_device_is_compatible(np, "fpga-region")) {
+ mgr_node = of_parse_phandle(np, "fpga-mgr", 0);
+ if (mgr_node)
+ return firmwaremgr_find_by_node(mgr_node);
+ }
+ } while ((np = of_get_parent(np)) != NULL);
+
+ return NULL;
+}
+
+static int load_firmware(struct device_node *target,
+ struct device_node *fragment, void *data)
+{
+ struct overlay_info *info = data;
+ const char *firmware_name;
+ const char *firmware_path = info->firmware_path;
+ char *firmware;
+ int err;
+ struct firmware_mgr *mgr;
+
+ err = of_property_read_string(fragment,
+ "firmware-name", &firmware_name);
+ /* Nothing to do if property does not exist. */
+ if (err == -EINVAL)
+ return 0;
+ else if (err)
+ return -EINVAL;
+
+ mgr = of_node_get_mgr(target);
+ if (!mgr)
+ return -EINVAL;
+
+ firmware = basprintf("%s/%s", firmware_path, firmware_name);
+ if (!firmware)
+ return -ENOMEM;
+
+ err = firmwaremgr_load_file(mgr, firmware);
+
+ free(firmware);
+
+ return err;
+}
+
+int of_firmware_load_overlay(struct device_node *overlay, const char *path)
+{
+ struct overlay_info info = {
+ .firmware_path = path,
+ };
+ int err;
+ struct device_node *root;
+ struct device_node *resolved;
+ struct device_node *ovl;
+
+ root = of_get_root_node();
+ /*
+ * If we cannot resolve the symbols in the overlay, ensure that the
+ * overlay does depend on firmware to be loaded.
+ */
+ resolved = of_resolve_phandles(root, overlay);
+ ovl = resolved ? resolved : overlay;
+
+ err = of_process_overlay(root, ovl,
+ load_firmware, &info);
+
+ if (resolved)
+ of_delete_node(resolved);
+
+ return err;
+}
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
new file mode 100644
index 0000000000..de79e05cbc
--- /dev/null
+++ b/drivers/of/overlay.c
@@ -0,0 +1,247 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Functions for working with device tree overlays
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
+ */
+#define pr_fmt(fmt) "of_overlay: " fmt
+
+#include <common.h>
+#include <of.h>
+#include <errno.h>
+
+static struct device_node *find_target(struct device_node *root,
+ struct device_node *fragment)
+{
+ struct device_node *node;
+ const char *path;
+ u32 phandle;
+ int ret;
+
+ ret = of_property_read_u32(fragment, "target", &phandle);
+ if (!ret) {
+ node = of_find_node_by_phandle_from(phandle, root);
+ if (!node)
+ pr_err("fragment %pOF: phandle 0x%x not found\n",
+ fragment, phandle);
+ return node;
+ }
+
+ ret = of_property_read_string(fragment, "target-path", &path);
+ if (!ret) {
+ node = of_find_node_by_path_from(root, path);
+ if (!node)
+ pr_err("fragment %pOF: path '%s' not found\n",
+ fragment, path);
+ return node;
+ }
+
+ pr_err("fragment %pOF: no target property\n", fragment);
+
+ return NULL;
+}
+
+static int of_overlay_apply(struct device_node *target,
+ const struct device_node *overlay)
+{
+ struct device_node *child;
+ struct device_node *target_child;
+ struct property *prop;
+ int err;
+
+ if (target == NULL || overlay == NULL)
+ return -EINVAL;
+
+ list_for_each_entry(prop, &overlay->properties, list) {
+ if (of_prop_cmp(prop->name, "name") == 0)
+ continue;
+
+ err = of_set_property(target, prop->name, prop->value,
+ prop->length, true);
+ if (err)
+ return err;
+ }
+
+ for_each_child_of_node(overlay, child) {
+ target_child = of_get_child_by_name(target, child->name);
+ if (!target_child)
+ target_child = of_new_node(target, child->name);
+ if (!target_child)
+ return -ENOMEM;
+
+ err = of_overlay_apply(target_child, child);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static char *of_overlay_fix_path(struct device_node *root,
+ struct device_node *overlay, const char *path)
+{
+ struct device_node *fragment;
+ struct device_node *target;
+ const char *path_tail;
+ const char *prefix;
+
+ fragment = of_find_node_by_path_from(overlay, path);
+ while ((fragment = of_get_parent(fragment)) != NULL) {
+ if (of_get_child_by_name(fragment, "__overlay__"))
+ break;
+ }
+ if (!fragment)
+ return NULL;
+
+ target = find_target(root, fragment);
+ if (!target)
+ return NULL;
+
+ prefix = of_get_child_by_name(fragment, "__overlay__")->full_name;
+ path_tail = path + strlen(prefix);
+
+ return basprintf("%s%s", target->full_name, path_tail);
+}
+
+static int of_overlay_apply_symbols(struct device_node *root,
+ struct device_node *overlay)
+{
+ const char *old_path;
+ char *new_path;
+ struct property *prop;
+ struct device_node *root_symbols;
+ struct device_node *overlay_symbols;
+
+ root_symbols = of_get_child_by_name(root, "__symbols__");
+ if (!root_symbols)
+ return -EINVAL;
+
+ overlay_symbols = of_get_child_by_name(overlay, "__symbols__");
+ if (!overlay_symbols)
+ return -EINVAL;
+
+ list_for_each_entry(prop, &overlay_symbols->properties, list) {
+ if (of_prop_cmp(prop->name, "name") == 0)
+ continue;
+
+ old_path = of_property_get_value(prop);
+ new_path = of_overlay_fix_path(root, overlay, old_path);
+
+ pr_debug("add symbol %s with new path %s\n",
+ prop->name, new_path);
+ of_property_write_string(root_symbols, prop->name, new_path);
+ }
+
+ return 0;
+}
+
+static int of_overlay_apply_fragment(struct device_node *root,
+ struct device_node *fragment)
+{
+ struct device_node *target;
+ struct device_node *overlay;
+
+ overlay = of_get_child_by_name(fragment, "__overlay__");
+ if (!overlay)
+ return 0;
+
+ target = find_target(root, fragment);
+ if (!target)
+ return -EINVAL;
+
+ return of_overlay_apply(target, overlay);
+}
+
+/**
+ * Apply the overlay on the passed devicetree root
+ * @root: the devicetree onto which the overlay will be applied
+ * @overlay: the devicetree to apply as an overlay
+ */
+int of_overlay_apply_tree(struct device_node *root,
+ struct device_node *overlay)
+{
+ struct device_node *resolved;
+ struct device_node *fragment;
+ int err;
+
+ resolved = of_resolve_phandles(root, overlay);
+ if (!resolved)
+ return -EINVAL;
+
+ /* Copy symbols from resolved overlay to base device tree */
+ err = of_overlay_apply_symbols(root, resolved);
+ if (err)
+ pr_warn("failed to copy symbols from overlay");
+
+ /* Copy nodes and properties from resolved overlay to root */
+ for_each_child_of_node(resolved, fragment) {
+ err = of_overlay_apply_fragment(root, fragment);
+ if (err)
+ pr_warn("failed to apply %s", fragment->name);
+ }
+
+ of_delete_node(resolved);
+
+ return err;
+}
+
+static int of_overlay_fixup(struct device_node *root, void *data)
+{
+ struct device_node *overlay = data;
+
+ return of_overlay_apply_tree(root, overlay);
+}
+
+/**
+ * Iterate the overlay and call process for each fragment
+ *
+ * If process() fails for any fragment, the function will stop to process
+ * other fragments and return the error of the failed process() call.
+ */
+int of_process_overlay(struct device_node *root,
+ struct device_node *overlay,
+ int (*process)(struct device_node *target,
+ struct device_node *overlay, void *data),
+ void *data)
+{
+ struct device_node *fragment;
+ int err = 0;
+
+ for_each_child_of_node(overlay, fragment) {
+ struct device_node *ovl;
+ struct device_node *target;
+
+ ovl = of_get_child_by_name(fragment, "__overlay__");
+ if (!ovl)
+ continue;
+
+ target = find_target(root, fragment);
+ if (!target)
+ continue;
+
+ err = process(target, ovl, data);
+ if (err) {
+ pr_warn("failed to process overlay for %s\n",
+ target->name);
+ break;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * Register a devicetree overlay
+ *
+ * The overlay is not applied to the live device tree, but registered as fixup
+ * for the fixed up device tree. Therefore, drivers relying on the overlay
+ * must use the fixed device tree.
+ *
+ * The fixup takes ownership of the overlay.
+ */
+int of_register_overlay(struct device_node *overlay)
+{
+ return of_register_fixup(of_overlay_fixup, overlay);
+}
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
new file mode 100644
index 0000000000..9107c1fbb6
--- /dev/null
+++ b/drivers/of/resolver.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Functions for dealing with DT resolution
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ * Copyright (C) 2019 Pengutronix, Michael Tretter <m.tretter@pengutronix.de>
+ */
+#define pr_fmt(fmt) "of_resolver: " fmt
+
+#include <common.h>
+#include <of.h>
+#include <errno.h>
+
+/**
+ * Recursively update phandles in overlay by adding delta
+ */
+static void adjust_overlay_phandles(struct device_node *overlay, int delta)
+{
+ struct device_node *child;
+ struct property *prop;
+
+ if (overlay->phandle != 0)
+ overlay->phandle += delta;
+
+ list_for_each_entry(prop, &overlay->properties, list) {
+ if (of_prop_cmp(prop->name, "phandle") != 0 &&
+ of_prop_cmp(prop->name, "linux,phandle") != 0)
+ continue;
+ if (prop->length < 4)
+ continue;
+
+ be32_add_cpu(prop->value, delta);
+ }
+
+ for_each_child_of_node(overlay, child)
+ adjust_overlay_phandles(child, delta);
+}
+
+/**
+ * Update all unresolved phandles in the overlay using prop_fixup
+ *
+ * prop_fixup contains a list of tuples of path:property_name:offset, each of
+ * which refers to a property that is phandle to a node in the base
+ * devicetree.
+ */
+static int update_usages_of_a_phandle_reference(struct device_node *overlay,
+ struct property *prop_fixup,
+ phandle phandle)
+{
+ struct device_node *refnode;
+ struct property *prop;
+ char *value, *cur, *end, *node_path, *prop_name, *s;
+ int offset, len;
+ int err = 0;
+
+ pr_debug("resolve references to %s to phandle 0x%x\n",
+ prop_fixup->name, phandle);
+
+ value = kmemdup(prop_fixup->value, prop_fixup->length, GFP_KERNEL);
+ if (!value)
+ return -ENOMEM;
+
+ end = value + prop_fixup->length;
+ for (cur = value; cur < end; cur += len + 1) {
+ len = strlen(cur);
+
+ node_path = cur;
+ s = strchr(cur, ':');
+ if (!s) {
+ err = -EINVAL;
+ goto err_fail;
+ }
+ *s++ = '\0';
+
+ prop_name = s;
+ s = strchr(s, ':');
+ if (!s) {
+ err = -EINVAL;
+ goto err_fail;
+ }
+ *s++ = '\0';
+
+ err = kstrtoint(s, 10, &offset);
+ if (err)
+ goto err_fail;
+
+ refnode = of_find_node_by_path_from(overlay, node_path);
+ if (!refnode)
+ continue;
+
+ prop = of_find_property(refnode, prop_name, NULL);
+ if (!prop) {
+ err = -ENOENT;
+ goto err_fail;
+ }
+
+ if (offset < 0 || offset + sizeof(__be32) > prop->length) {
+ err = -EINVAL;
+ goto err_fail;
+ }
+
+ *(__be32 *)(prop->value + offset) = cpu_to_be32(phandle);
+ }
+
+err_fail:
+ kfree(value);
+
+ if (err)
+ pr_debug("failed to resolve references to %s\n",
+ prop_fixup->name);
+
+ return err;
+}
+
+/*
+ * Adjust the local phandle references by the given phandle delta.
+ *
+ * Subtree @local_fixups, which is overlay node __local_fixups__,
+ * mirrors the fragment node structure at the root of the overlay.
+ *
+ * For each property in the fragments that contains a phandle reference,
+ * @local_fixups has a property of the same name that contains a list
+ * of offsets of the phandle reference(s) within the respective property
+ * value(s). The values at these offsets will be fixed up.
+ */
+static int adjust_local_phandle_references(struct device_node *local_fixups,
+ struct device_node *overlay, int phandle_delta)
+{
+ struct device_node *child, *overlay_child;
+ struct property *prop_fix, *prop;
+ int err, i, count;
+ unsigned int off;
+
+ if (!local_fixups)
+ return 0;
+
+ list_for_each_entry(prop_fix, &local_fixups->properties, list) {
+ if (!of_prop_cmp(prop_fix->name, "name") ||
+ !of_prop_cmp(prop_fix->name, "phandle") ||
+ !of_prop_cmp(prop_fix->name, "linux,phandle"))
+ continue;
+
+ if ((prop_fix->length % sizeof(__be32)) != 0 ||
+ prop_fix->length == 0)
+ return -EINVAL;
+ count = prop_fix->length / sizeof(__be32);
+
+ prop = of_find_property(overlay, prop_fix->name, NULL);
+ if (!prop)
+ return -EINVAL;
+
+ for (i = 0; i < count; i++) {
+ off = be32_to_cpu(((__be32 *)prop_fix->value)[i]);
+ if ((off + sizeof(__be32)) > prop->length)
+ return -EINVAL;
+
+ be32_add_cpu(prop->value + off, phandle_delta);
+ }
+ }
+
+ 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 = adjust_local_phandle_references(child, overlay_child,
+ phandle_delta);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * of_resolve_phandles - Resolve phandles in overlay based on root
+ *
+ * Rename phandles in overlay to avoid conflicts with the base devicetree and
+ * replace all phandles in the overlay with their renamed versions. Resolve
+ * phandles referring to nodes in the base devicetree with the phandle from
+ * the base devicetree.
+ *
+ * Returns a new device_node with resolved phandles which must be deleted by
+ * the caller of this function.
+ */
+struct device_node *of_resolve_phandles(struct device_node *root,
+ const struct device_node *overlay)
+{
+ struct device_node *result;
+ struct device_node *local_fixups;
+ struct device_node *refnode;
+ struct device_node *symbols;
+ struct device_node *overlay_fixups;
+ struct property *prop;
+ const char *refpath;
+ phandle delta;
+ int err;
+
+ result = of_copy_node(NULL, overlay);
+ if (!result)
+ return NULL;
+
+ delta = of_get_tree_max_phandle(root) + 1;
+
+ /*
+ * Rename the phandles in the devicetree overlay to prevent conflicts
+ * with the phandles in the base devicetree.
+ */
+ adjust_overlay_phandles(result, delta);
+
+ /*
+ * __local_fixups__ contains all locations in the overlay that refer
+ * to a phandle defined in the overlay. We must update the references,
+ * because we just adjusted the definitions.
+ */
+ local_fixups = of_find_node_by_name(result, "__local_fixups__");
+ err = adjust_local_phandle_references(local_fixups, result, delta);
+ if (err) {
+ pr_err("failed to fix phandles in overlay\n");
+ goto err;
+ }
+
+ /*
+ * __fixups__ contains all locations in the overlay that refer to a
+ * phandle that is not defined in the overlay and should be defined in
+ * the base device tree. We must update the references, because they
+ * are otherwise undefined.
+ */
+ overlay_fixups = of_find_node_by_name(result, "__fixups__");
+ if (!overlay_fixups) {
+ pr_debug("overlay does not contain phandles to base devicetree\n");
+ goto out;
+ }
+
+ symbols = of_find_node_by_path_from(root, "/__symbols__");
+ if (!symbols) {
+ pr_err("__symbols__ missing from base devicetree\n");
+ goto err;
+ }
+
+ list_for_each_entry(prop, &overlay_fixups->properties, list) {
+ if (!of_prop_cmp(prop->name, "name"))
+ continue;
+
+ err = of_property_read_string(symbols, prop->name, &refpath);
+ if (err) {
+ pr_err("cannot find node %s in base devicetree\n",
+ prop->name);
+ goto err;
+ }
+
+ refnode = of_find_node_by_path_from(root, refpath);
+ if (!refnode) {
+ pr_err("cannot find path %s in base devicetree\n",
+ refpath);
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = update_usages_of_a_phandle_reference(result, prop,
+ refnode->phandle);
+ if (err) {
+ pr_err("failed to update phandles for %s in overlay",
+ prop->name);
+ goto err;
+ }
+ }
+
+out:
+ return result;
+err:
+ of_delete_node(result);
+
+ return NULL;
+
+}
diff --git a/include/firmware.h b/include/firmware.h
index 284e0f9705..7c01a77118 100644
--- a/include/firmware.h
+++ b/include/firmware.h
@@ -34,10 +34,25 @@ struct firmware_mgr;
int firmwaremgr_register(struct firmware_handler *);
struct firmware_mgr *firmwaremgr_find(const char *);
+#ifdef CONFIG_FIRMWARE
+struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np);
+#else
+static inline struct firmware_mgr *firmwaremgr_find_by_node(const struct device_node *np)
+{
+ return NULL;
+}
+#endif
void firmwaremgr_list_handlers(void);
+#ifdef CONFIG_FIRMWARE
int firmwaremgr_load_file(struct firmware_mgr *, const char *path);
+#else
+static inline int firmwaremgr_load_file(struct firmware_mgr *mgr, const char *path)
+{
+ return -ENOSYS;
+}
+#endif
#define get_builtin_firmware(name, start, size) \
{ \
diff --git a/include/of.h b/include/of.h
index c8275e169b..98ddf795cb 100644
--- a/include/of.h
+++ b/include/of.h
@@ -871,4 +871,51 @@ static inline struct device_node *of_find_root_node(struct device_node *node)
return node;
}
+
+#ifdef CONFIG_OF_OVERLAY
+struct device_node *of_resolve_phandles(struct device_node *root,
+ const struct device_node *overlay);
+int of_overlay_apply_tree(struct device_node *root,
+ struct device_node *overlay);
+int of_register_overlay(struct device_node *overlay);
+int of_process_overlay(struct device_node *root,
+ struct device_node *overlay,
+ int (*process)(struct device_node *target,
+ struct device_node *overlay, void *data),
+ void *data);
+
+int of_firmware_load_overlay(struct device_node *overlay, const char *path);
+#else
+static inline struct device_node *of_resolve_phandles(struct device_node *root,
+ const struct device_node *overlay)
+{
+ return NULL;
+}
+
+static inline int of_overlay_apply_tree(struct device_node *root,
+ struct device_node *overlay)
+{
+ return -ENOSYS;
+}
+
+static inline int of_register_overlay(struct device_node *overlay)
+{
+ return -ENOSYS;
+}
+
+static inline int of_process_overlay(struct device_node *root,
+ struct device_node *overlay,
+ int (*process)(struct device_node *target,
+ struct device_node *overlay, void *data),
+ void *data)
+{
+ return -ENOSYS;
+}
+
+static inline int of_firmware_load_overlay(struct device_node *overlay, const char *path)
+{
+ return -ENOSYS;
+}
+#endif
+
#endif /* __OF_H */
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index a0fd71055c..cf9e95c499 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -294,6 +294,10 @@ DTC_FLAGS += -Wno-unit_address_vs_reg \
-Wno-unique_unit_address \
-Wno-pci_device_reg
+ifeq ($(CONFIG_OF_OVERLAY_LIVE), y)
+DTC_FLAGS += -@
+endif
+
# Generate an assembly file to wrap the output of the device tree compiler
quiet_cmd_dt_S_dtb = DTB $@
cmd_dt_S_dtb = $(srctree)/scripts/gen-dtb-s $(subst -,_,$(*F)) $< $(CONFIG_IMD) > $@