1063 lines
29 KiB
Diff
1063 lines
29 KiB
Diff
From c02be471430936674acba23ad18e3a4178c2c45a Mon Sep 17 00:00:00 2001
|
|
From: Pascal Vizeli <pvizeli@syshack.ch>
|
|
Date: Tue, 5 Jun 2018 22:43:53 +0000
|
|
Subject: [PATCH 2/2] drivers: of: implement overlay support
|
|
|
|
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
|
|
|