diff --git a/Documentation/user/booting-linux.rst b/Documentation/user/booting-linux.rst index 437f4e8..12cd505 100644 --- a/Documentation/user/booting-linux.rst +++ b/Documentation/user/booting-linux.rst @@ -232,6 +232,10 @@ 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 ad65fec..0189b47 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -2106,6 +2106,18 @@ 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 85adc32..2f09801 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -79,6 +79,7 @@ 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 0000000..de3c3dc --- /dev/null +++ b/commands/of_overlay.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Michael Tretter , 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 83b0599..7fb62d3 100644 --- a/common/blspec.c +++ b/common/blspec.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,78 @@ 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 @@ 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 @@ 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 @@ } } + 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 9d55d73..609cf11 100644 --- a/common/firmware.c +++ b/common/firmware.c @@ -63,6 +63,24 @@ } /* + * 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 24cf446..7436fc2 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -50,3 +50,28 @@ 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 ec43870..b684775 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -6,3 +6,4 @@ 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 0000000..0135631 --- /dev/null +++ b/drivers/of/of_firmware.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Pengutronix, Michael Tretter + */ +#include +#include +#include + +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 0000000..de79e05 --- /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 + * Copyright (C) 2012 Texas Instruments Inc. + * Copyright (C) 2019 Pengutronix, Michael Tretter + */ +#define pr_fmt(fmt) "of_overlay: " fmt + +#include +#include +#include + +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 0000000..9107c1f --- /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 + * Copyright (C) 2012 Texas Instruments Inc. + * Copyright (C) 2019 Pengutronix, Michael Tretter + */ +#define pr_fmt(fmt) "of_resolver: " fmt + +#include +#include +#include + +/** + * 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 284e0f9..7c01a77 100644 --- a/include/firmware.h +++ b/include/firmware.h @@ -34,10 +34,25 @@ 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 c8275e1..98ddf79 100644 --- a/include/of.h +++ b/include/of.h @@ -871,4 +871,51 @@ 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 a0fd710..cf9e95c 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -294,6 +294,10 @@ -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) > $@