aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Shi <alex.shi@linaro.org>2015-12-16 15:46:04 +0800
committerAlex Shi <alex.shi@linaro.org>2015-12-16 15:46:04 +0800
commit08671464b7991cdbc2928243fbde2458357b2ce8 (patch)
tree9df6728d1d3dc885e3dba5031c22408cb59a3eec
parentabacc500b7cddb62f7efc04d7c21d56375f42646 (diff)
parent37e8e4f1465886675b03c37e9221658568ceb8f7 (diff)
Merge branch 'v3.18/topic/of-overlay' into linux-linaro-lsk-v3.18lsk-v3.18-15.12
-rw-r--r--Documentation/devicetree/bindings/unittest.txt14
-rw-r--r--Documentation/devicetree/of_selftest.txt20
-rw-r--r--Documentation/devicetree/overlay-notes.txt133
-rw-r--r--Documentation/devicetree/todo.txt1
-rw-r--r--arch/powerpc/mm/numa.c3
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-cpu.c7
-rw-r--r--arch/powerpc/platforms/pseries/hotplug-memory.c15
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c5
-rw-r--r--arch/powerpc/platforms/pseries/setup.c5
-rw-r--r--drivers/base/platform.c1
-rw-r--r--drivers/crypto/nx/nx-842.c4
-rw-r--r--drivers/mfd/vexpress-sysreg.c2
-rw-r--r--drivers/of/Kconfig11
-rw-r--r--drivers/of/Makefile5
-rw-r--r--drivers/of/address.c2
-rw-r--r--drivers/of/base.c110
-rw-r--r--drivers/of/dynamic.c218
-rw-r--r--drivers/of/fdt.c113
-rw-r--r--drivers/of/irq.c1
-rw-r--r--drivers/of/of_pci.c1
-rw-r--r--drivers/of/of_private.h2
-rw-r--r--drivers/of/of_reserved_mem.c2
-rw-r--r--drivers/of/overlay.c552
-rw-r--r--drivers/of/pdt.c27
-rw-r--r--drivers/of/platform.c70
-rw-r--r--drivers/of/resolver.c128
-rw-r--r--drivers/of/testcase-data/testcases.dts50
-rw-r--r--drivers/of/unittest-data/testcases.dts79
-rw-r--r--drivers/of/unittest-data/tests-interrupts.dtsi (renamed from drivers/of/testcase-data/tests-interrupts.dtsi)0
-rw-r--r--drivers/of/unittest-data/tests-match.dtsi (renamed from drivers/of/testcase-data/tests-match.dtsi)0
-rw-r--r--drivers/of/unittest-data/tests-overlay.dtsi235
-rw-r--r--drivers/of/unittest-data/tests-phandle.dtsi (renamed from drivers/of/testcase-data/tests-phandle.dtsi)0
-rw-r--r--drivers/of/unittest-data/tests-platform.dtsi (renamed from drivers/of/testcase-data/tests-platform.dtsi)0
-rw-r--r--drivers/of/unittest.c (renamed from drivers/of/selftest.c)734
-rw-r--r--include/linux/of.h124
-rw-r--r--include/linux/of_address.h4
-rw-r--r--include/linux/of_pdt.h3
-rw-r--r--include/linux/of_platform.h6
38 files changed, 2274 insertions, 413 deletions
diff --git a/Documentation/devicetree/bindings/unittest.txt b/Documentation/devicetree/bindings/unittest.txt
new file mode 100644
index 000000000000..0f92a22fddfa
--- /dev/null
+++ b/Documentation/devicetree/bindings/unittest.txt
@@ -0,0 +1,14 @@
+* OF selftest platform device
+
+** selftest
+
+Required properties:
+- compatible: must be "selftest"
+
+All other properties are optional.
+
+Example:
+ selftest {
+ compatible = "selftest";
+ status = "okay";
+ };
diff --git a/Documentation/devicetree/of_selftest.txt b/Documentation/devicetree/of_selftest.txt
index 1e3d5c92b5e3..57a808b588bf 100644
--- a/Documentation/devicetree/of_selftest.txt
+++ b/Documentation/devicetree/of_selftest.txt
@@ -63,7 +63,6 @@ struct device_node {
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
- struct device_node *allnext; /* next in list of all nodes */
...
};
@@ -99,12 +98,6 @@ child11 -> sibling12 -> sibling13 -> sibling14 -> null
Figure 1: Generic structure of un-flattened device tree
-*allnext: it is used to link all the nodes of DT into a list. So, for the
- above tree the list would be as follows:
-
-root->child1->child11->sibling12->sibling13->child131->sibling14->sibling2->
-child21->sibling22->sibling23->sibling3->child31->sibling32->sibling4->null
-
Before executing OF selftest, it is required to attach the test data to
machine's device tree (if present). So, when selftest_data_add() is called,
at first it reads the flattened device tree data linked into the kernel image
@@ -131,11 +124,6 @@ root ('/')
test-child01 null null null
-allnext list:
-
-root->testcase-data->test-child0->test-child01->test-sibling1->test-sibling2
-->test-sibling3->null
-
Figure 2: Example test data tree to be attached to live tree.
According to the scenario above, the live tree is already present so it isn't
@@ -204,8 +192,6 @@ detached and then moving up the parent nodes are removed, and eventually the
whole tree). selftest_data_remove() calls detach_node_and_children() that uses
of_detach_node() to detach the nodes from the live device tree.
-To detach a node, of_detach_node() first updates all_next linked list, by
-attaching the previous node's allnext to current node's allnext pointer. And
-then, it either updates the child pointer of given node's parent to its
-sibling or attaches the previous sibling to the given node's sibling, as
-appropriate. That is it :)
+To detach a node, of_detach_node() either updates the child pointer of given
+node's parent to its sibling or attaches the previous sibling to the given
+node's sibling, as appropriate. That is it :)
diff --git a/Documentation/devicetree/overlay-notes.txt b/Documentation/devicetree/overlay-notes.txt
new file mode 100644
index 000000000000..30ae758e3eef
--- /dev/null
+++ b/Documentation/devicetree/overlay-notes.txt
@@ -0,0 +1,133 @@
+Device Tree Overlay Notes
+-------------------------
+
+This document describes the implementation of the in-kernel
+device tree overlay functionality residing in drivers/of/overlay.c and is a
+companion document to Documentation/devicetree/dt-object-internal.txt[1] &
+Documentation/devicetree/dynamic-resolution-notes.txt[2]
+
+How overlays work
+-----------------
+
+A Device Tree's overlay purpose is to modify the kernel's live tree, and
+have the modification affecting the state of the the kernel in a way that
+is reflecting the changes.
+Since the kernel mainly deals with devices, any new device node that result
+in an active device should have it created while if the device node is either
+disabled or removed all together, the affected device should be deregistered.
+
+Lets take an example where we have a foo board with the following base tree
+which is taken from [1].
+
+---- foo.dts -----------------------------------------------------------------
+ /* FOO platform */
+ / {
+ compatible = "corp,foo";
+
+ /* shared resources */
+ res: res {
+ };
+
+ /* On chip peripherals */
+ ocp: ocp {
+ /* peripherals that are always instantiated */
+ peripheral1 { ... };
+ }
+ };
+---- foo.dts -----------------------------------------------------------------
+
+The overlay bar.dts, when loaded (and resolved as described in [2]) should
+
+---- bar.dts -----------------------------------------------------------------
+/plugin/; /* allow undefined label references and record them */
+/ {
+ .... /* various properties for loader use; i.e. part id etc. */
+ fragment@0 {
+ target = <&ocp>;
+ __overlay__ {
+ /* bar peripheral */
+ bar {
+ compatible = "corp,bar";
+ ... /* various properties and child nodes */
+ }
+ };
+ };
+};
+---- bar.dts -----------------------------------------------------------------
+
+result in foo+bar.dts
+
+---- foo+bar.dts -------------------------------------------------------------
+ /* FOO platform + bar peripheral */
+ / {
+ compatible = "corp,foo";
+
+ /* shared resources */
+ res: res {
+ };
+
+ /* On chip peripherals */
+ ocp: ocp {
+ /* peripherals that are always instantiated */
+ peripheral1 { ... };
+
+ /* bar peripheral */
+ bar {
+ compatible = "corp,bar";
+ ... /* various properties and child nodes */
+ }
+ }
+ };
+---- foo+bar.dts -------------------------------------------------------------
+
+As a result of the the overlay, a new device node (bar) has been created
+so a bar platform device will be registered and if a matching device driver
+is loaded the device will be created as expected.
+
+Overlay in-kernel API
+--------------------------------
+
+The API is quite easy to use.
+
+1. Call of_overlay_create() to create and apply an overlay. The return value
+is a cookie identifying this overlay.
+
+2. Call of_overlay_destroy() to remove and cleanup the overlay previously
+created via the call to of_overlay_create(). Removal of an overlay that
+is stacked by another will not be permitted.
+
+Finally, if you need to remove all overlays in one-go, just call
+of_overlay_destroy_all() which will remove every single one in the correct
+order.
+
+Overlay DTS Format
+------------------
+
+The DTS of an overlay should have the following format:
+
+{
+ /* ignored properties by the overlay */
+
+ fragment@0 { /* first child node */
+
+ target=<phandle>; /* phandle target of the overlay */
+ or
+ target-path="/path"; /* target path of the overlay */
+
+ __overlay__ {
+ property-a; /* add property-a to the target */
+ node-a { /* add to an existing, or create a node-a */
+ ...
+ };
+ };
+ }
+ fragment@1 { /* second child node */
+ ...
+ };
+ /* more fragments follow */
+}
+
+Using the non-phandle based target method allows one to use a base DT which does
+not contain a __symbols__ node, i.e. it was not compiled with the -@ option.
+The __symbols__ node is only required for the target=<phandle> method, since it
+contains the information required to map from a phandle to a tree location.
diff --git a/Documentation/devicetree/todo.txt b/Documentation/devicetree/todo.txt
index c3cf0659bd19..b5139d1de811 100644
--- a/Documentation/devicetree/todo.txt
+++ b/Documentation/devicetree/todo.txt
@@ -2,7 +2,6 @@ Todo list for devicetree:
=== General structure ===
- Switch from custom lists to (h)list_head for nodes and properties structure
-- Remove of_allnodes list and iterate using list of child nodes alone
=== CONFIG_OF_DYNAMIC ===
- Switch to RCU for tree updates and get rid of global spinlock
diff --git a/arch/powerpc/mm/numa.c b/arch/powerpc/mm/numa.c
index b9d1dfdbe5bb..9fe6002c1d5a 100644
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -1711,12 +1711,11 @@ static void stage_topology_update(int core_id)
static int dt_update_callback(struct notifier_block *nb,
unsigned long action, void *data)
{
- struct of_prop_reconfig *update;
+ struct of_reconfig_data *update = data;
int rc = NOTIFY_DONE;
switch (action) {
case OF_RECONFIG_UPDATE_PROPERTY:
- update = (struct of_prop_reconfig *)data;
if (!of_prop_cmp(update->dn->type, "cpu") &&
!of_prop_cmp(update->prop->name, "ibm,associativity")) {
u32 core_id;
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index 5c375f93c669..f30cf4d136a4 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -340,16 +340,17 @@ static void pseries_remove_processor(struct device_node *np)
}
static int pseries_smp_notifier(struct notifier_block *nb,
- unsigned long action, void *node)
+ unsigned long action, void *data)
{
+ struct of_reconfig_data *rd = data;
int err = 0;
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
- err = pseries_add_processor(node);
+ err = pseries_add_processor(rd->dn);
break;
case OF_RECONFIG_DETACH_NODE:
- pseries_remove_processor(node);
+ pseries_remove_processor(rd->dn);
break;
}
return notifier_from_errno(err);
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
index 3c4c0dcd90d3..1bbb78fab530 100644
--- a/arch/powerpc/platforms/pseries/hotplug-memory.c
+++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
@@ -183,7 +183,7 @@ static int pseries_add_mem_node(struct device_node *np)
return (ret < 0) ? -EINVAL : 0;
}
-static int pseries_update_drconf_memory(struct of_prop_reconfig *pr)
+static int pseries_update_drconf_memory(struct of_reconfig_data *pr)
{
struct of_drconf_cell *new_drmem, *old_drmem;
unsigned long memblock_size;
@@ -232,22 +232,21 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr)
}
static int pseries_memory_notifier(struct notifier_block *nb,
- unsigned long action, void *node)
+ unsigned long action, void *data)
{
- struct of_prop_reconfig *pr;
+ struct of_reconfig_data *rd = data;
int err = 0;
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
- err = pseries_add_mem_node(node);
+ err = pseries_add_mem_node(rd->dn);
break;
case OF_RECONFIG_DETACH_NODE:
- err = pseries_remove_mem_node(node);
+ err = pseries_remove_mem_node(rd->dn);
break;
case OF_RECONFIG_UPDATE_PROPERTY:
- pr = (struct of_prop_reconfig *)node;
- if (!strcmp(pr->prop->name, "ibm,dynamic-memory"))
- err = pseries_update_drconf_memory(pr);
+ if (!strcmp(rd->prop->name, "ibm,dynamic-memory"))
+ err = pseries_update_drconf_memory(rd);
break;
}
return notifier_from_errno(err);
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index e32e00976a94..3e5bfdafee63 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -1251,10 +1251,11 @@ static struct notifier_block iommu_mem_nb = {
.notifier_call = iommu_mem_notifier,
};
-static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
+static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *data)
{
int err = NOTIFY_OK;
- struct device_node *np = node;
+ struct of_reconfig_data *rd = data;
+ struct device_node *np = rd->dn;
struct pci_dn *pci = PCI_DN(np);
struct direct_window *window;
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 125c589eeef5..ed8a90022a3d 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -251,9 +251,10 @@ static void __init pseries_discover_pic(void)
" interrupt-controller\n");
}
-static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *node)
+static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *data)
{
- struct device_node *np = node;
+ struct of_reconfig_data *rd = data;
+ struct device_node *np = rd->dn;
struct pci_dn *pci = NULL;
int err = NOTIFY_OK;
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 317e0e491ea0..b387fb91885e 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -1011,6 +1011,7 @@ int __init platform_bus_init(void)
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
+ of_platform_register_reconfig_notifier();
return error;
}
diff --git a/drivers/crypto/nx/nx-842.c b/drivers/crypto/nx/nx-842.c
index 061407d59520..887196e9b50c 100644
--- a/drivers/crypto/nx/nx-842.c
+++ b/drivers/crypto/nx/nx-842.c
@@ -1009,9 +1009,9 @@ error_out:
* notifier_to_errno() to decode this value
*/
static int nx842_OF_notifier(struct notifier_block *np, unsigned long action,
- void *update)
+ void *data)
{
- struct of_prop_reconfig *upd = update;
+ struct of_reconfig_data *upd = data;
struct nx842_devdata *local_devdata;
struct device_node *node = NULL;
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index 9e21e4fc9599..8f43ab8fd2d6 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -223,7 +223,7 @@ static int vexpress_sysreg_probe(struct platform_device *pdev)
vexpress_config_set_master(vexpress_sysreg_get_master());
/* Confirm board type against DT property, if available */
- if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) {
+ if (of_property_read_u32(of_root, "arm,hbi", &dt_hbi) == 0) {
u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER);
u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 1a13f5b722c5..7bcaeec876c0 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -7,10 +7,9 @@ config OF
menu "Device Tree and Open Firmware support"
depends on OF
-config OF_SELFTEST
- bool "Device Tree Runtime self tests"
+config OF_UNITTEST
+ bool "Device Tree runtime unit tests"
depends on OF_IRQ && OF_EARLY_FLATTREE
- select OF_DYNAMIC
select OF_RESOLVE
help
This option builds in test cases for the device tree infrastructure
@@ -23,6 +22,7 @@ config OF_FLATTREE
bool
select DTC
select LIBFDT
+ select CRC32
config OF_EARLY_FLATTREE
bool
@@ -83,4 +83,9 @@ config OF_RESERVED_MEM
config OF_RESOLVE
bool
+config OF_OVERLAY
+ bool "Device Tree overlays"
+ select OF_DYNAMIC
+ select OF_RESOLVE
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index ca9209ce50cd..7563f36c71db 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -6,14 +6,15 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o
obj-$(CONFIG_OF_ADDRESS) += address.o
obj-$(CONFIG_OF_IRQ) += irq.o
obj-$(CONFIG_OF_NET) += of_net.o
-obj-$(CONFIG_OF_SELFTEST) += of_selftest.o
-of_selftest-objs := selftest.o testcase-data/testcases.dtb.o
+obj-$(CONFIG_OF_UNITTEST) += of_unittest.o
+of_unittest-objs := unittest.o unittest-data/testcases.dtb.o
obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o
+obj-$(CONFIG_OF_OVERLAY) += overlay.o
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 9e77614391a0..d5f3b07177f9 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -889,7 +889,7 @@ EXPORT_SYMBOL(of_iomap);
* return PTR_ERR(base);
*/
void __iomem *of_io_request_and_map(struct device_node *np, int index,
- char *name)
+ const char *name)
{
struct resource res;
void __iomem *mem;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 469d2b7f47eb..df9626747afb 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -32,11 +32,12 @@
LIST_HEAD(aliases_lookup);
-struct device_node *of_allnodes;
-EXPORT_SYMBOL(of_allnodes);
+struct device_node *of_root;
+EXPORT_SYMBOL(of_root);
struct device_node *of_chosen;
struct device_node *of_aliases;
struct device_node *of_stdout;
+static const char *of_stdout_options;
struct kset *of_kset;
@@ -48,7 +49,7 @@ struct kset *of_kset;
*/
DEFINE_MUTEX(of_mutex);
-/* use when traversing tree through the allnext, child, sibling,
+/* use when traversing tree through the child, sibling,
* or parent members of struct device_node.
*/
DEFINE_RAW_SPINLOCK(devtree_lock);
@@ -204,7 +205,7 @@ static int __init of_init(void)
mutex_unlock(&of_mutex);
/* Symlink in /proc as required by userspace ABI */
- if (of_allnodes)
+ if (of_root)
proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
return 0;
@@ -245,6 +246,23 @@ struct property *of_find_property(const struct device_node *np,
}
EXPORT_SYMBOL(of_find_property);
+struct device_node *__of_find_all_nodes(struct device_node *prev)
+{
+ struct device_node *np;
+ if (!prev) {
+ np = of_root;
+ } else if (prev->child) {
+ np = prev->child;
+ } else {
+ /* Walk back up looking for a sibling, or the end of the structure */
+ np = prev;
+ while (np->parent && !np->sibling)
+ np = np->parent;
+ np = np->sibling; /* Might be null at the end of the tree */
+ }
+ return np;
+}
+
/**
* of_find_all_nodes - Get next node in global list
* @prev: Previous node or NULL to start iteration
@@ -259,10 +277,8 @@ struct device_node *of_find_all_nodes(struct device_node *prev)
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
- np = prev ? prev->allnext : of_allnodes;
- for (; np != NULL; np = np->allnext)
- if (of_node_get(np))
- break;
+ np = __of_find_all_nodes(prev);
+ of_node_get(np);
of_node_put(prev);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
@@ -507,27 +523,27 @@ EXPORT_SYMBOL(of_machine_is_compatible);
*
* @device: Node to check for availability, with locks already held
*
- * Returns 1 if the status property is absent or set to "okay" or "ok",
- * 0 otherwise
+ * Returns true if the status property is absent or set to "okay" or "ok",
+ * false otherwise
*/
-static int __of_device_is_available(const struct device_node *device)
+static bool __of_device_is_available(const struct device_node *device)
{
const char *status;
int statlen;
if (!device)
- return 0;
+ return false;
status = __of_get_property(device, "status", &statlen);
if (status == NULL)
- return 1;
+ return true;
if (statlen > 0) {
if (!strcmp(status, "okay") || !strcmp(status, "ok"))
- return 1;
+ return true;
}
- return 0;
+ return false;
}
/**
@@ -535,13 +551,13 @@ static int __of_device_is_available(const struct device_node *device)
*
* @device: Node to check for availability
*
- * Returns 1 if the status property is absent or set to "okay" or "ok",
- * 0 otherwise
+ * Returns true if the status property is absent or set to "okay" or "ok",
+ * false otherwise
*/
-int of_device_is_available(const struct device_node *device)
+bool of_device_is_available(const struct device_node *device)
{
unsigned long flags;
- int res;
+ bool res;
raw_spin_lock_irqsave(&devtree_lock, flags);
res = __of_device_is_available(device);
@@ -698,8 +714,9 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
const char *path)
{
struct device_node *child;
- int len = strchrnul(path, '/') - path;
+ int len;
+ len = strcspn(path, "/:");
if (!len)
return NULL;
@@ -715,11 +732,14 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
}
/**
- * of_find_node_by_path - Find a node matching a full OF path
+ * of_find_node_opts_by_path - Find a node matching a full OF path
* @path: Either the full path to match, or if the path does not
* start with '/', the name of a property of the /aliases
* node (an alias). In the case of an alias, the node
* matching the alias' value will be returned.
+ * @opts: Address of a pointer into which to store the start of
+ * an options string appended to the end of the path with
+ * a ':' separator.
*
* Valid paths:
* /foo/bar Full path
@@ -729,19 +749,27 @@ static struct device_node *__of_find_node_by_path(struct device_node *parent,
* Returns a node pointer with refcount incremented, use
* of_node_put() on it when done.
*/
-struct device_node *of_find_node_by_path(const char *path)
+struct device_node *of_find_node_opts_by_path(const char *path, const char **opts)
{
struct device_node *np = NULL;
struct property *pp;
unsigned long flags;
+ const char *separator = strchr(path, ':');
+
+ if (opts)
+ *opts = separator ? separator + 1 : NULL;
if (strcmp(path, "/") == 0)
- return of_node_get(of_allnodes);
+ return of_node_get(of_root);
/* The path could begin with an alias */
if (*path != '/') {
- char *p = strchrnul(path, '/');
- int len = p - path;
+ int len;
+ const char *p = separator;
+
+ if (!p)
+ p = strchrnul(path, '/');
+ len = p - path;
/* of_aliases must not be NULL */
if (!of_aliases)
@@ -761,16 +789,18 @@ struct device_node *of_find_node_by_path(const char *path)
/* Step down the tree matching path components */
raw_spin_lock_irqsave(&devtree_lock, flags);
if (!np)
- np = of_node_get(of_allnodes);
+ np = of_node_get(of_root);
while (np && *path == '/') {
path++; /* Increment past '/' delimiter */
np = __of_find_node_by_path(np, path);
path = strchrnul(path, '/');
+ if (separator && separator < path)
+ break;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
-EXPORT_SYMBOL(of_find_node_by_path);
+EXPORT_SYMBOL(of_find_node_opts_by_path);
/**
* of_find_node_by_name - Find a node by its "name" property
@@ -790,8 +820,7 @@ struct device_node *of_find_node_by_name(struct device_node *from,
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
- np = from ? from->allnext : of_allnodes;
- for (; np; np = np->allnext)
+ for_each_of_allnodes_from(from, np)
if (np->name && (of_node_cmp(np->name, name) == 0)
&& of_node_get(np))
break;
@@ -820,8 +849,7 @@ struct device_node *of_find_node_by_type(struct device_node *from,
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
- np = from ? from->allnext : of_allnodes;
- for (; np; np = np->allnext)
+ for_each_of_allnodes_from(from, np)
if (np->type && (of_node_cmp(np->type, type) == 0)
&& of_node_get(np))
break;
@@ -852,12 +880,10 @@ struct device_node *of_find_compatible_node(struct device_node *from,
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
- np = from ? from->allnext : of_allnodes;
- for (; np; np = np->allnext) {
+ for_each_of_allnodes_from(from, np)
if (__of_device_is_compatible(np, compatible, type, NULL) &&
of_node_get(np))
break;
- }
of_node_put(from);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
@@ -884,8 +910,7 @@ struct device_node *of_find_node_with_property(struct device_node *from,
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
- np = from ? from->allnext : of_allnodes;
- for (; np; np = np->allnext) {
+ for_each_of_allnodes_from(from, np) {
for (pp = np->properties; pp; pp = pp->next) {
if (of_prop_cmp(pp->name, prop_name) == 0) {
of_node_get(np);
@@ -967,8 +992,7 @@ struct device_node *of_find_matching_node_and_match(struct device_node *from,
*match = NULL;
raw_spin_lock_irqsave(&devtree_lock, flags);
- np = from ? from->allnext : of_allnodes;
- for (; np; np = np->allnext) {
+ for_each_of_allnodes_from(from, np) {
m = __of_match_node(matches, np);
if (m && of_node_get(np)) {
if (match)
@@ -1025,7 +1049,7 @@ struct device_node *of_find_node_by_phandle(phandle handle)
return NULL;
raw_spin_lock_irqsave(&devtree_lock, flags);
- for (np = of_allnodes; np; np = np->allnext)
+ for_each_of_allnodes(np)
if (np->phandle == handle)
break;
of_node_get(np);
@@ -1281,6 +1305,7 @@ int of_property_read_u64_array(const struct device_node *np,
}
return 0;
}
+EXPORT_SYMBOL_GPL(of_property_read_u64_array);
/**
* of_property_read_string - Find and read a string from a property
@@ -1350,7 +1375,7 @@ int of_property_match_string(struct device_node *np, const char *propname,
EXPORT_SYMBOL_GPL(of_property_match_string);
/**
- * of_property_read_string_util() - Utility helper for parsing string properties
+ * of_property_read_string_helper() - Utility helper for parsing string properties
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @out_strs: output array of string pointers.
@@ -1864,7 +1889,7 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
if (IS_ENABLED(CONFIG_PPC) && !name)
name = of_get_property(of_aliases, "stdout", NULL);
if (name)
- of_stdout = of_find_node_by_path(name);
+ of_stdout = of_find_node_opts_by_path(name, &of_stdout_options);
}
if (!of_aliases)
@@ -1990,7 +2015,8 @@ bool of_console_check(struct device_node *dn, char *name, int index)
{
if (!dn || dn != of_stdout || console_set_on_cmdline)
return false;
- return !add_preferred_console(name, index, NULL);
+ return !add_preferred_console(name, index,
+ kstrdup(of_stdout_options, GFP_KERNEL));
}
EXPORT_SYMBOL_GPL(of_console_check);
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index d4994177dec2..3351ef408125 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -77,18 +77,132 @@ int of_reconfig_notifier_unregister(struct notifier_block *nb)
}
EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
-int of_reconfig_notify(unsigned long action, void *p)
+#ifdef DEBUG
+const char *action_names[] = {
+ [OF_RECONFIG_ATTACH_NODE] = "ATTACH_NODE",
+ [OF_RECONFIG_DETACH_NODE] = "DETACH_NODE",
+ [OF_RECONFIG_ADD_PROPERTY] = "ADD_PROPERTY",
+ [OF_RECONFIG_REMOVE_PROPERTY] = "REMOVE_PROPERTY",
+ [OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY",
+};
+#endif
+
+int of_reconfig_notify(unsigned long action, struct of_reconfig_data *p)
{
int rc;
+#ifdef DEBUG
+ struct of_reconfig_data *pr = p;
+
+ switch (action) {
+ case OF_RECONFIG_ATTACH_NODE:
+ case OF_RECONFIG_DETACH_NODE:
+ pr_debug("of/notify %-15s %s\n", action_names[action],
+ pr->dn->full_name);
+ break;
+ case OF_RECONFIG_ADD_PROPERTY:
+ case OF_RECONFIG_REMOVE_PROPERTY:
+ case OF_RECONFIG_UPDATE_PROPERTY:
+ pr_debug("of/notify %-15s %s:%s\n", action_names[action],
+ pr->dn->full_name, pr->prop->name);
+ break;
+ }
+#endif
rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
return notifier_to_errno(rc);
}
+/*
+ * of_reconfig_get_state_change() - Returns new state of device
+ * @action - action of the of notifier
+ * @arg - argument of the of notifier
+ *
+ * Returns the new state of a device based on the notifier used.
+ * Returns 0 on device going from enabled to disabled, 1 on device
+ * going from disabled to enabled and -1 on no change.
+ */
+int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data *pr)
+{
+ struct property *prop, *old_prop = NULL;
+ int is_status, status_state, old_status_state, prev_state, new_state;
+
+ /* figure out if a device should be created or destroyed */
+ switch (action) {
+ case OF_RECONFIG_ATTACH_NODE:
+ case OF_RECONFIG_DETACH_NODE:
+ prop = of_find_property(pr->dn, "status", NULL);
+ break;
+ case OF_RECONFIG_ADD_PROPERTY:
+ case OF_RECONFIG_REMOVE_PROPERTY:
+ prop = pr->prop;
+ break;
+ case OF_RECONFIG_UPDATE_PROPERTY:
+ prop = pr->prop;
+ old_prop = pr->old_prop;
+ break;
+ default:
+ return OF_RECONFIG_NO_CHANGE;
+ }
+
+ is_status = 0;
+ status_state = -1;
+ old_status_state = -1;
+ prev_state = -1;
+ new_state = -1;
+
+ if (prop && !strcmp(prop->name, "status")) {
+ is_status = 1;
+ status_state = !strcmp(prop->value, "okay") ||
+ !strcmp(prop->value, "ok");
+ if (old_prop)
+ old_status_state = !strcmp(old_prop->value, "okay") ||
+ !strcmp(old_prop->value, "ok");
+ }
+
+ switch (action) {
+ case OF_RECONFIG_ATTACH_NODE:
+ prev_state = 0;
+ /* -1 & 0 status either missing or okay */
+ new_state = status_state != 0;
+ break;
+ case OF_RECONFIG_DETACH_NODE:
+ /* -1 & 0 status either missing or okay */
+ prev_state = status_state != 0;
+ new_state = 0;
+ break;
+ case OF_RECONFIG_ADD_PROPERTY:
+ if (is_status) {
+ /* no status property -> enabled (legacy) */
+ prev_state = 1;
+ new_state = status_state;
+ }
+ break;
+ case OF_RECONFIG_REMOVE_PROPERTY:
+ if (is_status) {
+ prev_state = status_state;
+ /* no status property -> enabled (legacy) */
+ new_state = 1;
+ }
+ break;
+ case OF_RECONFIG_UPDATE_PROPERTY:
+ if (is_status) {
+ prev_state = old_status_state != 0;
+ new_state = status_state != 0;
+ }
+ break;
+ }
+
+ if (prev_state == new_state)
+ return OF_RECONFIG_NO_CHANGE;
+
+ return new_state ? OF_RECONFIG_CHANGE_ADD : OF_RECONFIG_CHANGE_REMOVE;
+}
+EXPORT_SYMBOL_GPL(of_reconfig_get_state_change);
+
int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *oldprop)
{
- struct of_prop_reconfig pr;
+ struct of_reconfig_data pr;
/* only call notifiers if the node is attached */
if (!of_node_is_attached(np))
@@ -117,8 +231,6 @@ void __of_attach_node(struct device_node *np)
np->child = NULL;
np->sibling = np->parent->child;
- np->allnext = np->parent->allnext;
- np->parent->allnext = np;
np->parent->child = np;
of_node_clear_flag(np, OF_DETACHED);
}
@@ -128,8 +240,12 @@ void __of_attach_node(struct device_node *np)
*/
int of_attach_node(struct device_node *np)
{
+ struct of_reconfig_data rd;
unsigned long flags;
+ memset(&rd, 0, sizeof(rd));
+ rd.dn = np;
+
mutex_lock(&of_mutex);
raw_spin_lock_irqsave(&devtree_lock, flags);
__of_attach_node(np);
@@ -138,7 +254,7 @@ int of_attach_node(struct device_node *np)
__of_attach_node_sysfs(np);
mutex_unlock(&of_mutex);
- of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
+ of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd);
return 0;
}
@@ -154,17 +270,6 @@ void __of_detach_node(struct device_node *np)
if (WARN_ON(!parent))
return;
- if (of_allnodes == np)
- of_allnodes = np->allnext;
- else {
- struct device_node *prev;
- for (prev = of_allnodes;
- prev->allnext != np;
- prev = prev->allnext)
- ;
- prev->allnext = np->allnext;
- }
-
if (parent->child == np)
parent->child = np->sibling;
else {
@@ -187,9 +292,13 @@ void __of_detach_node(struct device_node *np)
*/
int of_detach_node(struct device_node *np)
{
+ struct of_reconfig_data rd;
unsigned long flags;
int rc = 0;
+ memset(&rd, 0, sizeof(rd));
+ rd.dn = np;
+
mutex_lock(&of_mutex);
raw_spin_lock_irqsave(&devtree_lock, flags);
__of_detach_node(np);
@@ -198,7 +307,7 @@ int of_detach_node(struct device_node *np)
__of_detach_node_sysfs(np);
mutex_unlock(&of_mutex);
- of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
+ of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd);
return rc;
}
@@ -285,36 +394,54 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
}
/**
- * __of_node_alloc() - Create an empty device node dynamically.
- * @full_name: Full name of the new device node
- * @allocflags: Allocation flags (typically pass GFP_KERNEL)
+ * __of_node_dup() - Duplicate or create an empty device node dynamically.
+ * @fmt: Format string (plus vargs) for new full name of the device node
*
- * Create an empty device tree node, suitable for further modification.
- * The node data are dynamically allocated and all the node flags
- * have the OF_DYNAMIC & OF_DETACHED bits set.
- * Returns the newly allocated node or NULL on out of memory error.
+ * Create an device tree node, either by duplicating an empty node or by allocating
+ * an empty one suitable for further modification. The node data are
+ * dynamically allocated and all the node flags have the OF_DYNAMIC &
+ * OF_DETACHED bits set. Returns the newly allocated node or NULL on out of
+ * memory error.
*/
-struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags)
+struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...)
{
+ va_list vargs;
struct device_node *node;
- node = kzalloc(sizeof(*node), allocflags);
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return NULL;
+ va_start(vargs, fmt);
+ node->full_name = kvasprintf(GFP_KERNEL, fmt, vargs);
+ va_end(vargs);
+ if (!node->full_name) {
+ kfree(node);
+ return NULL;
+ }
- node->full_name = kstrdup(full_name, allocflags);
of_node_set_flag(node, OF_DYNAMIC);
of_node_set_flag(node, OF_DETACHED);
- if (!node->full_name)
- goto err_free;
-
of_node_init(node);
+ /* Iterate over and duplicate all properties */
+ if (np) {
+ struct property *pp, *new_pp;
+ for_each_property_of_node(np, pp) {
+ new_pp = __of_prop_dup(pp, GFP_KERNEL);
+ if (!new_pp)
+ goto err_prop;
+ if (__of_add_property(node, new_pp)) {
+ kfree(new_pp->name);
+ kfree(new_pp->value);
+ kfree(new_pp);
+ goto err_prop;
+ }
+ }
+ }
return node;
- err_free:
- kfree(node->full_name);
- kfree(node);
+ err_prop:
+ of_node_put(node); /* Frees the node and properties */
return NULL;
}
@@ -330,27 +457,15 @@ static void __of_changeset_entry_dump(struct of_changeset_entry *ce)
{
switch (ce->action) {
case OF_RECONFIG_ADD_PROPERTY:
- pr_debug("%p: %s %s/%s\n",
- ce, "ADD_PROPERTY ", ce->np->full_name,
- ce->prop->name);
- break;
case OF_RECONFIG_REMOVE_PROPERTY:
- pr_debug("%p: %s %s/%s\n",
- ce, "REMOVE_PROPERTY", ce->np->full_name,
- ce->prop->name);
- break;
case OF_RECONFIG_UPDATE_PROPERTY:
- pr_debug("%p: %s %s/%s\n",
- ce, "UPDATE_PROPERTY", ce->np->full_name,
- ce->prop->name);
+ pr_debug("of/cset<%p> %-15s %s/%s\n", ce, action_names[ce->action],
+ ce->np->full_name, ce->prop->name);
break;
case OF_RECONFIG_ATTACH_NODE:
- pr_debug("%p: %s %s\n",
- ce, "ATTACH_NODE ", ce->np->full_name);
- break;
case OF_RECONFIG_DETACH_NODE:
- pr_debug("%p: %s %s\n",
- ce, "DETACH_NODE ", ce->np->full_name);
+ pr_debug("of/cset<%p> %-15s %s\n", ce, action_names[ce->action],
+ ce->np->full_name);
break;
}
}
@@ -388,6 +503,7 @@ static void __of_changeset_entry_invert(struct of_changeset_entry *ce,
static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert)
{
+ struct of_reconfig_data rd;
struct of_changeset_entry ce_inverted;
int ret;
@@ -399,7 +515,9 @@ static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool reve
switch (ce->action) {
case OF_RECONFIG_ATTACH_NODE:
case OF_RECONFIG_DETACH_NODE:
- ret = of_reconfig_notify(ce->action, ce->np);
+ memset(&rd, 0, sizeof(rd));
+ rd.dn = ce->np;
+ ret = of_reconfig_notify(ce->action, &rd);
break;
case OF_RECONFIG_ADD_PROPERTY:
case OF_RECONFIG_REMOVE_PROPERTY:
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index d134710de96d..3a896c9aeb74 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -9,6 +9,7 @@
* version 2 as published by the Free Software Foundation.
*/
+#include <linux/crc32.h>
#include <linux/kernel.h>
#include <linux/initrd.h>
#include <linux/memblock.h>
@@ -22,6 +23,7 @@
#include <linux/libfdt.h>
#include <linux/debugfs.h>
#include <linux/serial_core.h>
+#include <linux/sysfs.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
#include <asm/page.h>
@@ -145,15 +147,15 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
* @mem: Memory chunk to use for allocating device nodes and properties
* @p: pointer to node in flat tree
* @dad: Parent struct device_node
- * @allnextpp: pointer to ->allnext from last allocated device_node
* @fpsize: Size of the node path up at the current depth.
*/
static void * unflatten_dt_node(void *blob,
void *mem,
int *poffset,
struct device_node *dad,
- struct device_node ***allnextpp,
- unsigned long fpsize)
+ struct device_node **nodepp,
+ unsigned long fpsize,
+ bool dryrun)
{
const __be32 *p;
struct device_node *np;
@@ -200,7 +202,7 @@ static void * unflatten_dt_node(void *blob,
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
- if (allnextpp) {
+ if (!dryrun) {
char *fn;
of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
@@ -222,16 +224,10 @@ static void * unflatten_dt_node(void *blob,
memcpy(fn, pathp, l);
prev_pp = &np->properties;
- **allnextpp = np;
- *allnextpp = &np->allnext;
if (dad != NULL) {
np->parent = dad;
- /* we temporarily use the next field as `last_child'*/
- if (dad->next == NULL)
- dad->child = np;
- else
- dad->next->sibling = np;
- dad->next = np;
+ np->sibling = dad->child;
+ dad->child = np;
}
}
/* process properties */
@@ -254,7 +250,7 @@ static void * unflatten_dt_node(void *blob,
has_name = 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property));
- if (allnextpp) {
+ if (!dryrun) {
/* We accept flattened tree phandles either in
* ePAPR-style "phandle" properties, or the
* legacy "linux,phandle" properties. If both
@@ -296,7 +292,7 @@ static void * unflatten_dt_node(void *blob,
sz = (pa - ps) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,
__alignof__(struct property));
- if (allnextpp) {
+ if (!dryrun) {
pp->name = "name";
pp->length = sz;
pp->value = pp + 1;
@@ -308,7 +304,7 @@ static void * unflatten_dt_node(void *blob,
(char *)pp->value);
}
}
- if (allnextpp) {
+ if (!dryrun) {
*prev_pp = NULL;
np->name = of_get_property(np, "name", NULL);
np->type = of_get_property(np, "device_type", NULL);
@@ -324,12 +320,30 @@ static void * unflatten_dt_node(void *blob,
if (depth < 0)
depth = 0;
while (*poffset > 0 && depth > old_depth)
- mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
- fpsize);
+ mem = unflatten_dt_node(blob, mem, poffset, np, NULL,
+ fpsize, dryrun);
if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
pr_err("unflatten: error %d processing FDT\n", *poffset);
+ /*
+ * Reverse the child list. Some drivers assumes node order matches .dts
+ * node order
+ */
+ if (!dryrun && np->child) {
+ struct device_node *child = np->child;
+ np->child = NULL;
+ while (child) {
+ struct device_node *next = child->sibling;
+ child->sibling = np->child;
+ np->child = child;
+ child = next;
+ }
+ }
+
+ if (nodepp)
+ *nodepp = np;
+
return mem;
}
@@ -352,7 +366,6 @@ static void __unflatten_device_tree(void *blob,
unsigned long size;
int start;
void *mem;
- struct device_node **allnextp = mynodes;
pr_debug(" -> unflatten_device_tree()\n");
@@ -373,7 +386,7 @@ static void __unflatten_device_tree(void *blob,
/* First pass, scan for size */
start = 0;
- size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0);
+ size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
size = ALIGN(size, 4);
pr_debug(" size is %lx, allocating...\n", size);
@@ -388,11 +401,10 @@ static void __unflatten_device_tree(void *blob,
/* Second pass, do actual unflattening */
start = 0;
- unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
+ unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
if (be32_to_cpup(mem + size) != 0xdeadbeef)
pr_warning("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size));
- *allnextp = NULL;
pr_debug(" <- unflatten_device_tree()\n");
}
@@ -425,6 +437,8 @@ void *initial_boot_params;
#ifdef CONFIG_OF_EARLY_FLATTREE
+static u32 of_fdt_crc32;
+
/**
* res_mem_reserve_reg() - reserve all memory described in 'reg' property
*/
@@ -748,7 +762,7 @@ static inline void early_init_dt_check_for_initrd(unsigned long node)
#ifdef CONFIG_SERIAL_EARLYCON
extern struct of_device_id __earlycon_of_table[];
-int __init early_init_dt_scan_chosen_serial(void)
+static int __init early_init_dt_scan_chosen_serial(void)
{
int offset;
const char *p;
@@ -930,6 +944,11 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
const u64 phys_offset = __pa(PAGE_OFFSET);
if (!PAGE_ALIGNED(base)) {
+ if (size < PAGE_SIZE - (base & ~PAGE_MASK)) {
+ pr_warn("Ignoring memory block 0x%llx - 0x%llx\n",
+ base, base + size);
+ return;
+ }
size -= PAGE_SIZE - (base & ~PAGE_MASK);
base = PAGE_ALIGN(base);
}
@@ -992,15 +1011,14 @@ bool __init early_init_dt_verify(void *params)
if (!params)
return false;
- /* Setup flat device-tree pointer */
- initial_boot_params = params;
-
/* check device tree validity */
- if (fdt_check_header(params)) {
- initial_boot_params = NULL;
+ if (fdt_check_header(params))
return false;
- }
+ /* Setup flat device-tree pointer */
+ initial_boot_params = params;
+ of_fdt_crc32 = crc32_be(~0, initial_boot_params,
+ fdt_totalsize(initial_boot_params));
return true;
}
@@ -1039,7 +1057,7 @@ bool __init early_init_dt_scan(void *params)
*/
void __init unflatten_device_tree(void)
{
- __unflatten_device_tree(initial_boot_params, &of_allnodes,
+ __unflatten_device_tree(initial_boot_params, &of_root,
early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
@@ -1078,27 +1096,32 @@ void __init unflatten_and_copy_device_tree(void)
unflatten_device_tree();
}
-#if defined(CONFIG_DEBUG_FS) && defined(DEBUG)
-static struct debugfs_blob_wrapper flat_dt_blob;
-
-static int __init of_flat_dt_debugfs_export_fdt(void)
+#ifdef CONFIG_SYSFS
+static ssize_t of_fdt_raw_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
{
- struct dentry *d = debugfs_create_dir("device-tree", NULL);
-
- if (!d)
- return -ENOENT;
+ memcpy(buf, initial_boot_params + off, count);
+ return count;
+}
- flat_dt_blob.data = initial_boot_params;
- flat_dt_blob.size = fdt_totalsize(initial_boot_params);
+static int __init of_fdt_raw_init(void)
+{
+ static struct bin_attribute of_fdt_raw_attr =
+ __BIN_ATTR(fdt, S_IRUSR, of_fdt_raw_read, NULL, 0);
- d = debugfs_create_blob("flat-device-tree", S_IFREG | S_IRUSR,
- d, &flat_dt_blob);
- if (!d)
- return -ENOENT;
+ if (!initial_boot_params)
+ return 0;
- return 0;
+ if (of_fdt_crc32 != crc32_be(~0, initial_boot_params,
+ fdt_totalsize(initial_boot_params))) {
+ pr_warn("fdt: not creating '/sys/firmware/fdt': CRC check failed\n");
+ return 0;
+ }
+ of_fdt_raw_attr.size = fdt_totalsize(initial_boot_params);
+ return sysfs_create_bin_file(firmware_kobj, &of_fdt_raw_attr);
}
-module_init(of_flat_dt_debugfs_export_fdt);
+late_initcall(of_fdt_raw_init);
#endif
#endif /* CONFIG_OF_EARLY_FLATTREE */
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 4419e62143ed..c21e364dcd96 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -410,6 +410,7 @@ int of_irq_get(struct device_node *dev, int index)
return irq_create_of_mapping(&oirq);
}
+EXPORT_SYMBOL_GPL(of_irq_get);
/**
* of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c
index af0143983d27..b249fdf5ecf8 100644
--- a/drivers/of/of_pci.c
+++ b/drivers/of/of_pci.c
@@ -250,7 +250,6 @@ parse_failed:
list_for_each_entry(window, resources, list)
kfree(window->res);
pci_free_resource_list(resources);
- kfree(bus_range);
return err;
}
EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources);
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 858e0a5d9a11..8e882e706cd8 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -61,7 +61,7 @@ static inline int of_property_notify(int action, struct device_node *np,
* own the devtree lock or work on detached trees only.
*/
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags);
-struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags);
+__printf(2, 3) struct device_node *__of_node_dup(const struct device_node *np, const char *fmt, ...);
extern const void *__of_get_property(const struct device_node *np,
const char *name, int *lenp);
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index dc566b38645f..726ebe792813 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -265,6 +265,7 @@ int of_reserved_mem_device_init(struct device *dev)
return ret;
}
+EXPORT_SYMBOL_GPL(of_reserved_mem_device_init);
/**
* of_reserved_mem_device_release() - release reserved memory device structures
@@ -289,3 +290,4 @@ void of_reserved_mem_device_release(struct device *dev)
rmem->ops->device_release(rmem, dev);
}
+EXPORT_SYMBOL_GPL(of_reserved_mem_device_release);
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
new file mode 100644
index 000000000000..dee9270ba547
--- /dev/null
+++ b/drivers/of/overlay.c
@@ -0,0 +1,552 @@
+/*
+ * Functions for working with device tree overlays
+ *
+ * Copyright (C) 2012 Pantelis Antoniou <panto@antoniou-consulting.com>
+ * Copyright (C) 2012 Texas Instruments Inc.
+ *
+ * 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.
+ */
+#undef DEBUG
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+
+#include "of_private.h"
+
+/**
+ * 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, including all the overlay logs &
+ * records.
+ */
+struct of_overlay_info {
+ struct device_node *target;
+ struct device_node *overlay;
+};
+
+/**
+ * struct of_overlay - Holds a complete overlay transaction
+ * @node: List on which we are located
+ * @count: Count of ovinfo structures
+ * @ovinfo_tab: Overlay info table (count sized)
+ * @cset: Changeset to be used
+ *
+ * Holds a complete overlay transaction
+ */
+struct of_overlay {
+ int id;
+ struct list_head node;
+ int count;
+ struct of_overlay_info *ovinfo_tab;
+ struct of_changeset cset;
+};
+
+static int of_overlay_apply_one(struct of_overlay *ov,
+ struct device_node *target, const struct device_node *overlay);
+
+static int of_overlay_apply_single_property(struct of_overlay *ov,
+ struct device_node *target, struct property *prop)
+{
+ struct property *propn, *tprop;
+
+ /* NOTE: Multiple changes of single properties not supported */
+ tprop = of_find_property(target, prop->name, NULL);
+
+ /* special properties are not meant to be updated (silent NOP) */
+ if (of_prop_cmp(prop->name, "name") == 0 ||
+ of_prop_cmp(prop->name, "phandle") == 0 ||
+ of_prop_cmp(prop->name, "linux,phandle") == 0)
+ return 0;
+
+ propn = __of_prop_dup(prop, GFP_KERNEL);
+ if (propn == NULL)
+ return -ENOMEM;
+
+ /* not found? add */
+ if (tprop == NULL)
+ return of_changeset_add_property(&ov->cset, target, propn);
+
+ /* found? update */
+ return of_changeset_update_property(&ov->cset, target, propn);
+}
+
+static int of_overlay_apply_single_device_node(struct of_overlay *ov,
+ struct device_node *target, struct device_node *child)
+{
+ const char *cname;
+ struct device_node *tchild;
+ int ret = 0;
+
+ cname = kbasename(child->full_name);
+ if (cname == NULL)
+ return -ENOMEM;
+
+ /* NOTE: Multiple mods of created nodes not supported */
+ tchild = of_get_child_by_name(target, cname);
+ if (tchild != NULL) {
+ /* apply overlay recursively */
+ ret = of_overlay_apply_one(ov, tchild, child);
+ of_node_put(tchild);
+ } else {
+ /* create empty tree as a target */
+ tchild = __of_node_dup(child, "%s/%s", target->full_name, cname);
+ if (!tchild)
+ return -ENOMEM;
+
+ /* point to parent */
+ tchild->parent = target;
+
+ ret = of_changeset_attach_node(&ov->cset, tchild);
+ if (ret)
+ return ret;
+
+ ret = of_overlay_apply_one(ov, tchild, child);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * Apply a single overlay node recursively.
+ *
+ * Note that the in case of an error the target node is left
+ * in a inconsistent state. Error recovery should be performed
+ * by using the changeset.
+ */
+static int of_overlay_apply_one(struct of_overlay *ov,
+ struct device_node *target, const struct device_node *overlay)
+{
+ struct device_node *child;
+ struct property *prop;
+ int ret;
+
+ for_each_property_of_node(overlay, prop) {
+ ret = of_overlay_apply_single_property(ov, target, prop);
+ if (ret) {
+ pr_err("%s: Failed to apply prop @%s/%s\n",
+ __func__, target->full_name, prop->name);
+ return ret;
+ }
+ }
+
+ for_each_child_of_node(overlay, child) {
+ ret = of_overlay_apply_single_device_node(ov, target, child);
+ if (ret != 0) {
+ pr_err("%s: Failed to apply single node @%s/%s\n",
+ __func__, target->full_name,
+ child->name);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * of_overlay_apply() - Apply @count overlays pointed at by @ovinfo_tab
+ * @ov: Overlay 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.
+ */
+static int of_overlay_apply(struct of_overlay *ov)
+{
+ int i, err;
+
+ /* first we apply the overlays atomically */
+ for (i = 0; i < ov->count; i++) {
+ struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i];
+
+ err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay);
+ if (err != 0) {
+ pr_err("%s: overlay failed '%s'\n",
+ __func__, ovinfo->target->full_name);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Find the target node using a number of different strategies
+ * in order of preference
+ *
+ * "target" property containing the phandle of the target
+ * "target-path" property containing the path of the target
+ */
+static struct device_node *find_target_node(struct device_node *info_node)
+{
+ const char *path;
+ u32 val;
+ int ret;
+
+ /* first try to go by using the target as a phandle */
+ ret = of_property_read_u32(info_node, "target", &val);
+ if (ret == 0)
+ return of_find_node_by_phandle(val);
+
+ /* now try to locate by path */
+ ret = of_property_read_string(info_node, "target-path", &path);
+ if (ret == 0)
+ return of_find_node_by_path(path);
+
+ pr_err("%s: Failed to find target for node %p (%s)\n", __func__,
+ info_node, info_node->name);
+
+ return NULL;
+}
+
+/**
+ * of_fill_overlay_info() - Fill an overlay info structure
+ * @ov Overlay to fill
+ * @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.
+ */
+static int of_fill_overlay_info(struct of_overlay *ov,
+ struct device_node *info_node, struct of_overlay_info *ovinfo)
+{
+ ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__");
+ if (ovinfo->overlay == NULL)
+ goto err_fail;
+
+ ovinfo->target = find_target_node(info_node);
+ if (ovinfo->target == NULL)
+ goto err_fail;
+
+ return 0;
+
+err_fail:
+ of_node_put(ovinfo->target);
+ of_node_put(ovinfo->overlay);
+
+ memset(ovinfo, 0, sizeof(*ovinfo));
+ return -EINVAL;
+}
+
+/**
+ * of_build_overlay_info() - Build an overlay info array
+ * @ov Overlay to build
+ * @tree: Device node containing all the overlays
+ *
+ * Helper function that given a tree containing overlay information,
+ * allocates and builds an overlay info array containing it, ready
+ * for use using of_overlay_apply.
+ *
+ * Returns 0 on success with the @cntp @ovinfop pointers valid,
+ * while on error a negative error value is returned.
+ */
+static int of_build_overlay_info(struct of_overlay *ov,
+ struct device_node *tree)
+{
+ struct device_node *node;
+ struct of_overlay_info *ovinfo;
+ int cnt, err;
+
+ /* worst case; every child is a node */
+ cnt = 0;
+ for_each_child_of_node(tree, node)
+ cnt++;
+
+ ovinfo = kcalloc(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(ov, node, &ovinfo[cnt]);
+ if (err == 0)
+ cnt++;
+ }
+
+ /* if nothing filled, return error */
+ if (cnt == 0) {
+ kfree(ovinfo);
+ return -ENODEV;
+ }
+
+ ov->count = cnt;
+ ov->ovinfo_tab = ovinfo;
+
+ return 0;
+}
+
+/**
+ * of_free_overlay_info() - Free an overlay info array
+ * @ov Overlay to free the overlay info from
+ * @ovinfo_tab: Array of overlay_info's to free
+ *
+ * Releases the memory of a previously allocated ovinfo array
+ * by of_build_overlay_info.
+ * Returns 0, or an error if the arguments are bogus.
+ */
+static int of_free_overlay_info(struct of_overlay *ov)
+{
+ struct of_overlay_info *ovinfo;
+ int i;
+
+ /* do it in reverse */
+ for (i = ov->count - 1; i >= 0; i--) {
+ ovinfo = &ov->ovinfo_tab[i];
+
+ of_node_put(ovinfo->target);
+ of_node_put(ovinfo->overlay);
+ }
+ kfree(ov->ovinfo_tab);
+
+ return 0;
+}
+
+static LIST_HEAD(ov_list);
+static DEFINE_IDR(ov_idr);
+
+/**
+ * of_overlay_create() - Create and apply an overlay
+ * @tree: Device node containing all the overlays
+ *
+ * Creates and applies an overlay while also keeping track
+ * of the overlay in a list. This list can be used to prevent
+ * illegal overlay removals.
+ *
+ * Returns the id of the created overlay, or an negative error number
+ */
+int of_overlay_create(struct device_node *tree)
+{
+ struct of_overlay *ov;
+ int err, id;
+
+ /* allocate the overlay structure */
+ ov = kzalloc(sizeof(*ov), GFP_KERNEL);
+ if (ov == NULL)
+ return -ENOMEM;
+ ov->id = -1;
+
+ INIT_LIST_HEAD(&ov->node);
+
+ of_changeset_init(&ov->cset);
+
+ mutex_lock(&of_mutex);
+
+ id = idr_alloc(&ov_idr, ov, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ pr_err("%s: idr_alloc() failed for tree@%s\n",
+ __func__, tree->full_name);
+ err = id;
+ goto err_destroy_trans;
+ }
+ ov->id = id;
+
+ /* build the overlay info structures */
+ err = of_build_overlay_info(ov, tree);
+ if (err) {
+ pr_err("%s: of_build_overlay_info() failed for tree@%s\n",
+ __func__, tree->full_name);
+ goto err_free_idr;
+ }
+
+ /* apply the overlay */
+ err = of_overlay_apply(ov);
+ if (err) {
+ pr_err("%s: of_overlay_apply() failed for tree@%s\n",
+ __func__, tree->full_name);
+ goto err_abort_trans;
+ }
+
+ /* apply the changeset */
+ err = of_changeset_apply(&ov->cset);
+ if (err) {
+ pr_err("%s: of_changeset_apply() failed for tree@%s\n",
+ __func__, tree->full_name);
+ goto err_revert_overlay;
+ }
+
+ /* add to the tail of the overlay list */
+ list_add_tail(&ov->node, &ov_list);
+
+ mutex_unlock(&of_mutex);
+
+ return id;
+
+err_revert_overlay:
+err_abort_trans:
+ of_free_overlay_info(ov);
+err_free_idr:
+ idr_remove(&ov_idr, ov->id);
+err_destroy_trans:
+ of_changeset_destroy(&ov->cset);
+ kfree(ov);
+ mutex_unlock(&of_mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(of_overlay_create);
+
+/* check whether the given node, lies under the given tree */
+static int overlay_subtree_check(struct device_node *tree,
+ struct device_node *dn)
+{
+ struct device_node *child;
+
+ /* match? */
+ if (tree == dn)
+ return 1;
+
+ for_each_child_of_node(tree, child) {
+ if (overlay_subtree_check(child, dn))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* check whether this overlay is the topmost */
+static int overlay_is_topmost(struct of_overlay *ov, struct device_node *dn)
+{
+ struct of_overlay *ovt;
+ struct of_changeset_entry *ce;
+
+ list_for_each_entry_reverse(ovt, &ov_list, node) {
+ /* if we hit ourselves, we're done */
+ if (ovt == ov)
+ break;
+
+ /* check against each subtree affected by this overlay */
+ list_for_each_entry(ce, &ovt->cset.entries, node) {
+ if (overlay_subtree_check(ce->np, dn)) {
+ pr_err("%s: #%d clashes #%d @%s\n",
+ __func__, ov->id, ovt->id,
+ dn->full_name);
+ return 0;
+ }
+ }
+ }
+
+ /* overlay is topmost */
+ return 1;
+}
+
+/*
+ * We can safely remove the overlay only if it's the top-most one.
+ * Newly applied overlays are inserted at the tail of the overlay list,
+ * so a top most overlay is the one that is closest to the tail.
+ *
+ * The topmost check is done by exploiting this property. For each
+ * affected device node in the log list we check if this overlay is
+ * the one closest to the tail. If another overlay has affected this
+ * device node and is closest to the tail, then removal is not permited.
+ */
+static int overlay_removal_is_ok(struct of_overlay *ov)
+{
+ struct of_changeset_entry *ce;
+
+ list_for_each_entry(ce, &ov->cset.entries, node) {
+ if (!overlay_is_topmost(ov, ce->np)) {
+ pr_err("%s: overlay #%d is not topmost\n",
+ __func__, ov->id);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * of_overlay_destroy() - Removes an overlay
+ * @id: Overlay id number returned by a previous call to of_overlay_create
+ *
+ * Removes an overlay if it is permissible.
+ *
+ * Returns 0 on success, or an negative error number
+ */
+int of_overlay_destroy(int id)
+{
+ struct of_overlay *ov;
+ int err;
+
+ mutex_lock(&of_mutex);
+
+ ov = idr_find(&ov_idr, id);
+ if (ov == NULL) {
+ err = -ENODEV;
+ pr_err("%s: Could not find overlay #%d\n",
+ __func__, id);
+ goto out;
+ }
+
+ /* check whether the overlay is safe to remove */
+ if (!overlay_removal_is_ok(ov)) {
+ err = -EBUSY;
+ pr_err("%s: removal check failed for overlay #%d\n",
+ __func__, id);
+ goto out;
+ }
+
+
+ list_del(&ov->node);
+ of_changeset_revert(&ov->cset);
+ of_free_overlay_info(ov);
+ idr_remove(&ov_idr, id);
+ of_changeset_destroy(&ov->cset);
+ kfree(ov);
+
+ err = 0;
+
+out:
+ mutex_unlock(&of_mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(of_overlay_destroy);
+
+/**
+ * of_overlay_destroy_all() - Removes all overlays from the system
+ *
+ * Removes all overlays from the system in the correct order.
+ *
+ * Returns 0 on success, or an negative error number
+ */
+int of_overlay_destroy_all(void)
+{
+ struct of_overlay *ov, *ovn;
+
+ mutex_lock(&of_mutex);
+
+ /* the tail of list is guaranteed to be safe to remove */
+ list_for_each_entry_safe_reverse(ov, ovn, &ov_list, node) {
+ list_del(&ov->node);
+ of_changeset_revert(&ov->cset);
+ of_free_overlay_info(ov);
+ idr_remove(&ov_idr, ov->id);
+ kfree(ov);
+ }
+
+ mutex_unlock(&of_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_overlay_destroy_all);
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c
index 36b4035881b0..d2acae825af9 100644
--- a/drivers/of/pdt.c
+++ b/drivers/of/pdt.c
@@ -25,8 +25,7 @@
static struct of_pdt_ops *of_pdt_prom_ops __initdata;
-void __initdata (*of_pdt_build_more)(struct device_node *dp,
- struct device_node ***nextp);
+void __initdata (*of_pdt_build_more)(struct device_node *dp);
#if defined(CONFIG_SPARC)
unsigned int of_pdt_unique_id __initdata;
@@ -192,8 +191,7 @@ static struct device_node * __init of_pdt_create_node(phandle node,
}
static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
- phandle node,
- struct device_node ***nextp)
+ phandle node)
{
struct device_node *ret = NULL, *prev_sibling = NULL;
struct device_node *dp;
@@ -210,16 +208,12 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent,
ret = dp;
prev_sibling = dp;
- *(*nextp) = dp;
- *nextp = &dp->allnext;
-
dp->full_name = of_pdt_build_full_name(dp);
- dp->child = of_pdt_build_tree(dp,
- of_pdt_prom_ops->getchild(node), nextp);
+ dp->child = of_pdt_build_tree(dp, of_pdt_prom_ops->getchild(node));
if (of_pdt_build_more)
- of_pdt_build_more(dp, nextp);
+ of_pdt_build_more(dp);
node = of_pdt_prom_ops->getsibling(node);
}
@@ -234,20 +228,17 @@ static void * __init kernel_tree_alloc(u64 size, u64 align)
void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops)
{
- struct device_node **nextp;
-
BUG_ON(!ops);
of_pdt_prom_ops = ops;
- of_allnodes = of_pdt_create_node(root_node, NULL);
+ of_root = of_pdt_create_node(root_node, NULL);
#if defined(CONFIG_SPARC)
- of_allnodes->path_component_name = "";
+ of_root->path_component_name = "";
#endif
- of_allnodes->full_name = "/";
+ of_root->full_name = "/";
- nextp = &of_allnodes->allnext;
- of_allnodes->child = of_pdt_build_tree(of_allnodes,
- of_pdt_prom_ops->getchild(of_allnodes->phandle), &nextp);
+ of_root->child = of_pdt_build_tree(of_root,
+ of_pdt_prom_ops->getchild(of_root->phandle));
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
of_alias_scan(kernel_tree_alloc);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 02b96d6b7e3a..8a002d6151f2 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -450,6 +450,7 @@ int of_platform_populate(struct device_node *root,
if (rc)
break;
}
+ of_node_set_flag(root, OF_POPULATED_BUS);
of_node_put(root);
return rc;
@@ -493,8 +494,75 @@ static int of_platform_device_destroy(struct device *dev, void *data)
*/
void of_platform_depopulate(struct device *parent)
{
- device_for_each_child(parent, NULL, of_platform_device_destroy);
+ if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) {
+ device_for_each_child(parent, NULL, of_platform_device_destroy);
+ of_node_clear_flag(parent->of_node, OF_POPULATED_BUS);
+ }
}
EXPORT_SYMBOL_GPL(of_platform_depopulate);
+#ifdef CONFIG_OF_DYNAMIC
+static int of_platform_notify(struct notifier_block *nb,
+ unsigned long action, void *arg)
+{
+ struct of_reconfig_data *rd = arg;
+ struct platform_device *pdev_parent, *pdev;
+ bool children_left;
+
+ switch (of_reconfig_get_state_change(action, rd)) {
+ case OF_RECONFIG_CHANGE_ADD:
+ /* verify that the parent is a bus */
+ if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS))
+ return NOTIFY_OK; /* not for us */
+
+ /* already populated? (driver using of_populate manually) */
+ if (of_node_check_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
+ /* pdev_parent may be NULL when no bus platform device */
+ pdev_parent = of_find_device_by_node(rd->dn->parent);
+ pdev = of_platform_device_create(rd->dn, NULL,
+ pdev_parent ? &pdev_parent->dev : NULL);
+ of_dev_put(pdev_parent);
+
+ if (pdev == NULL) {
+ pr_err("%s: failed to create for '%s'\n",
+ __func__, rd->dn->full_name);
+ /* of_platform_device_create tosses the error code */
+ return notifier_from_errno(-EINVAL);
+ }
+ break;
+
+ case OF_RECONFIG_CHANGE_REMOVE:
+
+ /* already depopulated? */
+ if (!of_node_check_flag(rd->dn, OF_POPULATED))
+ return NOTIFY_OK;
+
+ /* find our device by node */
+ pdev = of_find_device_by_node(rd->dn);
+ if (pdev == NULL)
+ return NOTIFY_OK; /* no? not meant for us */
+
+ /* unregister takes one ref away */
+ of_platform_device_destroy(&pdev->dev, &children_left);
+
+ /* and put the reference of the find */
+ of_dev_put(pdev);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block platform_of_notifier = {
+ .notifier_call = of_platform_notify,
+};
+
+void of_platform_register_reconfig_notifier(void)
+{
+ WARN_ON(of_reconfig_notifier_register(&platform_of_notifier));
+}
+#endif /* CONFIG_OF_DYNAMIC */
+
#endif /* CONFIG_OF_ADDRESS */
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
index aed7959f800d..640eb4cb46e3 100644
--- a/drivers/of/resolver.c
+++ b/drivers/of/resolver.c
@@ -111,7 +111,8 @@ static void __of_adjust_tree_phandles(struct device_node *node,
__of_adjust_tree_phandles(child, phandle_delta);
}
-static int __of_adjust_phandle_ref(struct device_node *node, struct property *rprop, int value, bool is_delta)
+static int __of_adjust_phandle_ref(struct device_node *node,
+ struct property *rprop, int value)
{
phandle phandle;
struct device_node *refnode;
@@ -181,7 +182,7 @@ static int __of_adjust_phandle_ref(struct device_node *node, struct property *rp
goto err_fail;
}
- phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value;
+ phandle = value;
*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
}
@@ -190,36 +191,97 @@ err_fail:
return err;
}
+/* compare nodes taking into account that 'name' strips out the @ part */
+static int __of_node_name_cmp(const struct device_node *dn1,
+ const struct device_node *dn2)
+{
+ const char *n1 = strrchr(dn1->full_name, '/') ? : "/";
+ const char *n2 = strrchr(dn2->full_name, '/') ? : "/";
+
+ return of_node_cmp(n1, n2);
+}
+
/*
* 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.
+ * Assumes the existances of a __local_fixups__ node at the root.
+ * Assumes that __of_verify_tree_phandle_references has been called.
+ * 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 *node,
- int phandle_delta)
+ struct device_node *target, int phandle_delta)
{
- struct device_node *child;
- struct property *rprop;
- int err;
-
- /* locate the symbols & fixups nodes on resolve */
- for_each_child_of_node(node, child)
- if (of_node_cmp(child->name, "__local_fixups__") == 0)
- break;
+ struct device_node *child, *childtarget;
+ struct property *rprop, *sprop;
+ int err, i, count;
+ unsigned int off;
+ phandle phandle;
- /* no local fixups */
- if (!child)
+ if (node == NULL)
return 0;
- /* find the local fixups property */
- for_each_property_of_node(child, rprop) {
+ for_each_property_of_node(node, rprop) {
+
/* skip properties added automatically */
- if (of_prop_cmp(rprop->name, "name") == 0)
+ if (of_prop_cmp(rprop->name, "name") == 0 ||
+ of_prop_cmp(rprop->name, "phandle") == 0 ||
+ of_prop_cmp(rprop->name, "linux,phandle") == 0)
continue;
- err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true);
- if (err)
+ if ((rprop->length % 4) != 0 || rprop->length == 0) {
+ pr_err("%s: Illegal property (size) '%s' @%s\n",
+ __func__, rprop->name, node->full_name);
+ return -EINVAL;
+ }
+ count = rprop->length / sizeof(__be32);
+
+ /* now find the target property */
+ for_each_property_of_node(target, sprop) {
+ if (of_prop_cmp(sprop->name, rprop->name) == 0)
+ break;
+ }
+
+ if (sprop == NULL) {
+ pr_err("%s: Could not find target property '%s' @%s\n",
+ __func__, rprop->name, node->full_name);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < count; i++) {
+ off = be32_to_cpu(((__be32 *)rprop->value)[i]);
+ /* make sure the offset doesn't overstep (even wrap) */
+ if (off >= sprop->length ||
+ (off + 4) > sprop->length) {
+ pr_err("%s: Illegal property '%s' @%s\n",
+ __func__, rprop->name,
+ node->full_name);
+ return -EINVAL;
+ }
+
+ if (phandle_delta) {
+ /* adjust */
+ phandle = be32_to_cpu(*(__be32 *)(sprop->value + off));
+ phandle += phandle_delta;
+ *(__be32 *)(sprop->value + off) = cpu_to_be32(phandle);
+ }
+ }
+ }
+
+ for_each_child_of_node(node, child) {
+
+ for_each_child_of_node(target, childtarget)
+ if (__of_node_name_cmp(child, childtarget) == 0)
+ break;
+
+ if (!childtarget) {
+ pr_err("%s: Could not find target child '%s' @%s\n",
+ __func__, child->name, node->full_name);
+ return -EINVAL;
+ }
+
+ err = __of_adjust_tree_phandle_references(child, childtarget,
+ phandle_delta);
+ if (err != 0)
return err;
}
@@ -241,7 +303,7 @@ static int __of_adjust_tree_phandle_references(struct device_node *node,
*/
int of_resolve_phandles(struct device_node *resolve)
{
- struct device_node *child, *refnode;
+ struct device_node *child, *childroot, *refnode;
struct device_node *root_sym, *resolve_sym, *resolve_fix;
struct property *rprop;
const char *refpath;
@@ -255,9 +317,23 @@ int of_resolve_phandles(struct device_node *resolve)
/* first we need to adjust the phandles */
phandle_delta = of_get_tree_max_phandle() + 1;
__of_adjust_tree_phandles(resolve, phandle_delta);
- err = __of_adjust_tree_phandle_references(resolve, phandle_delta);
- if (err != 0)
- return err;
+
+ /* locate the local fixups */
+ childroot = NULL;
+ for_each_child_of_node(resolve, childroot)
+ if (of_node_cmp(childroot->name, "__local_fixups__") == 0)
+ break;
+
+ if (childroot != NULL) {
+ /* resolve root is guaranteed to be the '/' */
+ err = __of_adjust_tree_phandle_references(childroot,
+ resolve, 0);
+ if (err != 0)
+ return err;
+
+ BUG_ON(__of_adjust_tree_phandle_references(childroot,
+ resolve, phandle_delta));
+ }
root_sym = NULL;
resolve_sym = NULL;
@@ -322,7 +398,7 @@ int of_resolve_phandles(struct device_node *resolve)
pr_debug("%s: %s phandle is 0x%08x\n",
__func__, rprop->name, phandle);
- err = __of_adjust_phandle_ref(resolve, rprop, phandle, false);
+ err = __of_adjust_phandle_ref(resolve, rprop, phandle);
if (err)
break;
}
diff --git a/drivers/of/testcase-data/testcases.dts b/drivers/of/testcase-data/testcases.dts
deleted file mode 100644
index 6994e15c24bf..000000000000
--- a/drivers/of/testcase-data/testcases.dts
+++ /dev/null
@@ -1,50 +0,0 @@
-/dts-v1/;
-/ {
- testcase-data {
- changeset {
- prop-update = "hello";
- prop-remove = "world";
- node-remove {
- };
- };
- };
-};
-#include "tests-phandle.dtsi"
-#include "tests-interrupts.dtsi"
-#include "tests-match.dtsi"
-#include "tests-platform.dtsi"
-
-/*
- * phandle fixup data - generated by dtc patches that aren't upstream.
- * This data must be regenerated whenever phandle references are modified in
- * the testdata tree.
- *
- * The format of this data may be subject to change. For the time being consider
- * this a kernel-internal data format.
- */
-/ { __local_fixups__ {
- fixup = "/testcase-data/testcase-device2:interrupt-parent:0",
- "/testcase-data/testcase-device1:interrupt-parent:0",
- "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:60",
- "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:52",
- "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:44",
- "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:36",
- "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:24",
- "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:8",
- "/testcase-data/interrupts/interrupts-extended0:interrupts-extended:0",
- "/testcase-data/interrupts/interrupts1:interrupt-parent:0",
- "/testcase-data/interrupts/interrupts0:interrupt-parent:0",
- "/testcase-data/interrupts/intmap1:interrupt-map:12",
- "/testcase-data/interrupts/intmap0:interrupt-map:52",
- "/testcase-data/interrupts/intmap0:interrupt-map:36",
- "/testcase-data/interrupts/intmap0:interrupt-map:16",
- "/testcase-data/interrupts/intmap0:interrupt-map:4",
- "/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:12",
- "/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:0",
- "/testcase-data/phandle-tests/consumer-a:phandle-list:56",
- "/testcase-data/phandle-tests/consumer-a:phandle-list:52",
- "/testcase-data/phandle-tests/consumer-a:phandle-list:40",
- "/testcase-data/phandle-tests/consumer-a:phandle-list:24",
- "/testcase-data/phandle-tests/consumer-a:phandle-list:8",
- "/testcase-data/phandle-tests/consumer-a:phandle-list:0";
-}; };
diff --git a/drivers/of/unittest-data/testcases.dts b/drivers/of/unittest-data/testcases.dts
new file mode 100644
index 000000000000..12f7c3d649c8
--- /dev/null
+++ b/drivers/of/unittest-data/testcases.dts
@@ -0,0 +1,79 @@
+/dts-v1/;
+/ {
+ testcase-data {
+ changeset {
+ prop-update = "hello";
+ prop-remove = "world";
+ node-remove {
+ };
+ };
+ };
+};
+#include "tests-phandle.dtsi"
+#include "tests-interrupts.dtsi"
+#include "tests-match.dtsi"
+#include "tests-platform.dtsi"
+#include "tests-overlay.dtsi"
+
+/*
+ * phandle fixup data - generated by dtc patches that aren't upstream.
+ * This data must be regenerated whenever phandle references are modified in
+ * the testdata tree.
+ *
+ * The format of this data may be subject to change. For the time being consider
+ * this a kernel-internal data format.
+ */
+/ { __local_fixups__ {
+ testcase-data {
+ phandle-tests {
+ consumer-a {
+ phandle-list = <0x00000000 0x00000008
+ 0x00000018 0x00000028
+ 0x00000034 0x00000038>;
+ phandle-list-bad-args = <0x00000000 0x0000000c>;
+ };
+ };
+ interrupts {
+ intmap0 {
+ interrupt-map = <0x00000004 0x00000010
+ 0x00000024 0x00000034>;
+ };
+ intmap1 {
+ interrupt-map = <0x0000000c>;
+ };
+ interrupts0 {
+ interrupt-parent = <0x00000000>;
+ };
+ interrupts1 {
+ interrupt-parent = <0x00000000>;
+ };
+ interrupts-extended0 {
+ interrupts-extended = <0x00000000 0x00000008
+ 0x00000018 0x00000024
+ 0x0000002c 0x00000034
+ 0x0000003c>;
+ };
+ };
+ testcase-device1 {
+ interrupt-parent = <0x00000000>;
+ };
+ testcase-device2 {
+ interrupt-parent = <0x00000000>;
+ };
+ overlay2 {
+ fragment@0 {
+ target = <0x00000000>;
+ };
+ };
+ overlay3 {
+ fragment@0 {
+ target = <0x00000000>;
+ };
+ };
+ overlay4 {
+ fragment@0 {
+ target = <0x00000000>;
+ };
+ };
+ };
+}; };
diff --git a/drivers/of/testcase-data/tests-interrupts.dtsi b/drivers/of/unittest-data/tests-interrupts.dtsi
index da4695f60351..da4695f60351 100644
--- a/drivers/of/testcase-data/tests-interrupts.dtsi
+++ b/drivers/of/unittest-data/tests-interrupts.dtsi
diff --git a/drivers/of/testcase-data/tests-match.dtsi b/drivers/of/unittest-data/tests-match.dtsi
index c9e541129534..c9e541129534 100644
--- a/drivers/of/testcase-data/tests-match.dtsi
+++ b/drivers/of/unittest-data/tests-match.dtsi
diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi
new file mode 100644
index 000000000000..a2b687d5f324
--- /dev/null
+++ b/drivers/of/unittest-data/tests-overlay.dtsi
@@ -0,0 +1,235 @@
+
+/ {
+ testcase-data {
+ overlay-node {
+
+ /* test bus */
+ selftestbus: test-bus {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ selftest100: test-selftest100 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <100>;
+ };
+
+ selftest101: test-selftest101 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <101>;
+ };
+
+ selftest0: test-selftest0 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <0>;
+ };
+
+ selftest1: test-selftest1 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <1>;
+ };
+
+ selftest2: test-selftest2 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <2>;
+ };
+
+ selftest3: test-selftest3 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <3>;
+ };
+
+ selftest5: test-selftest5 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <5>;
+ };
+
+ selftest6: test-selftest6 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <6>;
+ };
+
+ selftest7: test-selftest7 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <7>;
+ };
+
+ selftest8: test-selftest8 {
+ compatible = "selftest";
+ status = "disabled";
+ reg = <8>;
+ };
+ };
+ };
+
+ /* test enable using absolute target path */
+ overlay0 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest0";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test disable using absolute target path */
+ overlay1 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest1";
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+ };
+
+ /* test enable using label */
+ overlay2 {
+ fragment@0 {
+ target = <&selftest2>;
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test disable using label */
+ overlay3 {
+ fragment@0 {
+ target = <&selftest3>;
+ __overlay__ {
+ status = "disabled";
+ };
+ };
+ };
+
+ /* test insertion of a full node */
+ overlay4 {
+ fragment@0 {
+ target = <&selftestbus>;
+ __overlay__ {
+
+ /* suppress DTC warning */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ test-selftest4 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <4>;
+ };
+ };
+ };
+ };
+
+ /* test overlay apply revert */
+ overlay5 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest5";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test overlays application and removal in sequence */
+ overlay6 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest6";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+ overlay7 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest7";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+
+ /* test overlays application and removal in bad sequence */
+ overlay8 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
+ __overlay__ {
+ status = "okay";
+ };
+ };
+ };
+ overlay9 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus/test-selftest8";
+ __overlay__ {
+ property-foo = "bar";
+ };
+ };
+ };
+
+ overlay10 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus";
+ __overlay__ {
+
+ /* suppress DTC warning */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ test-selftest10 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <10>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ test-selftest101 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <1>;
+ };
+
+ };
+ };
+ };
+ };
+
+ overlay11 {
+ fragment@0 {
+ target-path = "/testcase-data/overlay-node/test-bus";
+ __overlay__ {
+
+ /* suppress DTC warning */
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ test-selftest11 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <11>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ test-selftest111 {
+ compatible = "selftest";
+ status = "okay";
+ reg = <1>;
+ };
+
+ };
+ };
+ };
+ };
+ };
+};
diff --git a/drivers/of/testcase-data/tests-phandle.dtsi b/drivers/of/unittest-data/tests-phandle.dtsi
index 5b1527e8a7fb..5b1527e8a7fb 100644
--- a/drivers/of/testcase-data/tests-phandle.dtsi
+++ b/drivers/of/unittest-data/tests-phandle.dtsi
diff --git a/drivers/of/testcase-data/tests-platform.dtsi b/drivers/of/unittest-data/tests-platform.dtsi
index eb20eeb2b062..eb20eeb2b062 100644
--- a/drivers/of/testcase-data/tests-platform.dtsi
+++ b/drivers/of/unittest-data/tests-platform.dtsi
diff --git a/drivers/of/selftest.c b/drivers/of/unittest.c
index e2d79afa9dc6..e5fa3e91f82f 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/unittest.c
@@ -17,6 +17,8 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
#include "of_private.h"
@@ -25,24 +27,22 @@ static struct selftest_results {
int failed;
} selftest_results;
-#define NO_OF_NODES 3
-static struct device_node *nodes[NO_OF_NODES];
-static int last_node_index;
-static bool selftest_live_tree;
-
-#define selftest(result, fmt, ...) { \
- if (!(result)) { \
+#define selftest(result, fmt, ...) ({ \
+ bool failed = !(result); \
+ if (failed) { \
selftest_results.failed++; \
pr_err("FAIL %s():%i " fmt, __func__, __LINE__, ##__VA_ARGS__); \
} else { \
selftest_results.passed++; \
pr_debug("pass %s():%i\n", __func__, __LINE__); \
} \
-}
+ failed; \
+})
static void __init of_selftest_find_node_by_name(void)
{
struct device_node *np;
+ const char *options;
np = of_find_node_by_path("/testcase-data");
selftest(np && !strcmp("/testcase-data", np->full_name),
@@ -83,6 +83,51 @@ static void __init of_selftest_find_node_by_name(void)
np = of_find_node_by_path("testcase-alias/missing-path");
selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name);
of_node_put(np);
+
+ np = of_find_node_opts_by_path("/testcase-data:testoption", &options);
+ selftest(np && !strcmp("testoption", options),
+ "option path test failed\n");
+ of_node_put(np);
+
+ np = of_find_node_opts_by_path("/testcase-data:test/option", &options);
+ selftest(np && !strcmp("test/option", options),
+ "option path test, subcase #1 failed\n");
+ of_node_put(np);
+
+ np = of_find_node_opts_by_path("/testcase-data/testcase-device1:test/option", &options);
+ selftest(np && !strcmp("test/option", options),
+ "option path test, subcase #2 failed\n");
+ of_node_put(np);
+
+ np = of_find_node_opts_by_path("/testcase-data:testoption", NULL);
+ selftest(np, "NULL option path test failed\n");
+ of_node_put(np);
+
+ np = of_find_node_opts_by_path("testcase-alias:testaliasoption",
+ &options);
+ selftest(np && !strcmp("testaliasoption", options),
+ "option alias path test failed\n");
+ of_node_put(np);
+
+ np = of_find_node_opts_by_path("testcase-alias:test/alias/option",
+ &options);
+ selftest(np && !strcmp("test/alias/option", options),
+ "option alias path test, subcase #1 failed\n");
+ of_node_put(np);
+
+ np = of_find_node_opts_by_path("testcase-alias:testaliasoption", NULL);
+ selftest(np, "NULL option alias path test failed\n");
+ of_node_put(np);
+
+ options = "testoption";
+ np = of_find_node_opts_by_path("testcase-alias", &options);
+ selftest(np && !options, "option clearing test failed\n");
+ of_node_put(np);
+
+ options = "testoption";
+ np = of_find_node_opts_by_path("/", &options);
+ selftest(np && !options, "option clearing root node test failed\n");
+ of_node_put(np);
}
static void __init of_selftest_dynamic(void)
@@ -148,7 +193,7 @@ static void __init of_selftest_dynamic(void)
static int __init of_selftest_check_node_linkage(struct device_node *np)
{
- struct device_node *child, *allnext_index = np;
+ struct device_node *child;
int count = 0, rc;
for_each_child_of_node(np, child) {
@@ -158,14 +203,6 @@ static int __init of_selftest_check_node_linkage(struct device_node *np)
return -EINVAL;
}
- while (allnext_index && allnext_index != child)
- allnext_index = allnext_index->allnext;
- if (allnext_index != child) {
- pr_err("Node %s is ordered differently in sibling and allnode lists\n",
- child->name);
- return -EINVAL;
- }
-
rc = of_selftest_check_node_linkage(child);
if (rc < 0)
return rc;
@@ -180,12 +217,12 @@ static void __init of_selftest_check_tree_linkage(void)
struct device_node *np;
int allnode_count = 0, child_count;
- if (!of_allnodes)
+ if (!of_root)
return;
for_each_of_allnodes(np)
allnode_count++;
- child_count = of_selftest_check_node_linkage(of_allnodes);
+ child_count = of_selftest_check_node_linkage(of_root);
selftest(child_count > 0, "Device node data structure is corrupted\n");
selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match"
@@ -354,9 +391,9 @@ static void __init of_selftest_property_string(void)
rc = of_property_match_string(np, "phandle-list-names", "first");
selftest(rc == 0, "first expected:0 got:%i\n", rc);
rc = of_property_match_string(np, "phandle-list-names", "second");
- selftest(rc == 1, "second expected:0 got:%i\n", rc);
+ selftest(rc == 1, "second expected:1 got:%i\n", rc);
rc = of_property_match_string(np, "phandle-list-names", "third");
- selftest(rc == 2, "third expected:0 got:%i\n", rc);
+ selftest(rc == 2, "third expected:2 got:%i\n", rc);
rc = of_property_match_string(np, "phandle-list-names", "fourth");
selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
rc = of_property_match_string(np, "missing-property", "blah");
@@ -451,15 +488,14 @@ static void __init of_selftest_changeset(void)
struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" };
struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
struct property *ppremove;
- struct device_node *n1, *n2, *n21, *nremove, *parent;
+ struct device_node *n1, *n2, *n21, *nremove, *parent, *np;
struct of_changeset chgset;
- of_changeset_init(&chgset);
- n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL);
+ n1 = __of_node_dup(NULL, "/testcase-data/changeset/n1");
selftest(n1, "testcase setup failure\n");
- n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL);
+ n2 = __of_node_dup(NULL, "/testcase-data/changeset/n2");
selftest(n2, "testcase setup failure\n");
- n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL);
+ n21 = __of_node_dup(NULL, "%s/%s", "/testcase-data/changeset/n2", "n21");
selftest(n21, "testcase setup failure %p\n", n21);
nremove = of_find_node_by_path("/testcase-data/changeset/node-remove");
selftest(nremove, "testcase setup failure\n");
@@ -487,6 +523,11 @@ static void __init of_selftest_changeset(void)
selftest(!of_changeset_apply(&chgset), "apply failed\n");
mutex_unlock(&of_mutex);
+ /* Make sure node names are constructed correctly */
+ selftest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
+ "'%s' not added\n", n21->full_name);
+ of_node_put(np);
+
mutex_lock(&of_mutex);
selftest(!of_changeset_revert(&chgset), "revert failed\n");
mutex_unlock(&of_mutex);
@@ -702,10 +743,13 @@ static void __init of_selftest_match_node(void)
}
}
+struct device test_bus = {
+ .init_name = "unittest-bus",
+};
static void __init of_selftest_platform_populate(void)
{
- int irq;
- struct device_node *np, *child;
+ int irq, rc;
+ struct device_node *np, *child, *grandchild;
struct platform_device *pdev;
struct of_device_id match[] = {
{ .compatible = "test-device", },
@@ -730,20 +774,32 @@ static void __init of_selftest_platform_populate(void)
irq = platform_get_irq(pdev, 0);
selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq);
- np = of_find_node_by_path("/testcase-data/platform-tests");
- if (!np) {
- pr_err("No testcase data in device tree\n");
+ if (selftest(np = of_find_node_by_path("/testcase-data/platform-tests"),
+ "No testcase data in device tree\n"));
+ return;
+
+ if (selftest(!(rc = device_register(&test_bus)),
+ "testbus registration failed; rc=%i\n", rc));
return;
- }
for_each_child_of_node(np, child) {
- struct device_node *grandchild;
- of_platform_populate(child, match, NULL, NULL);
+ of_platform_populate(child, match, NULL, &test_bus);
for_each_child_of_node(child, grandchild)
selftest(of_find_device_by_node(grandchild),
"Could not create device for node '%s'\n",
grandchild->name);
}
+
+ of_platform_depopulate(&test_bus);
+ for_each_child_of_node(np, child) {
+ for_each_child_of_node(child, grandchild)
+ selftest(!of_find_device_by_node(grandchild),
+ "device didn't get destroyed '%s'\n",
+ grandchild->name);
+ }
+
+ device_unregister(&test_bus);
+ of_node_put(np);
}
/**
@@ -775,33 +831,32 @@ static void update_node_properties(struct device_node *np,
*/
static int attach_node_and_children(struct device_node *np)
{
- struct device_node *next, *root = np, *dup;
+ struct device_node *next, *dup, *child;
+ unsigned long flags;
- /* skip root node */
- np = np->child;
- /* storing a copy in temporary node */
- dup = np;
-
- while (dup) {
- if (WARN_ON(last_node_index >= NO_OF_NODES))
- return -EINVAL;
- nodes[last_node_index++] = dup;
- dup = dup->sibling;
+ dup = of_find_node_by_path(np->full_name);
+ if (dup) {
+ update_node_properties(np, dup);
+ return 0;
}
- dup = NULL;
- while (np) {
- next = np->allnext;
- dup = of_find_node_by_path(np->full_name);
- if (dup)
- update_node_properties(np, dup);
- else {
- np->child = NULL;
- if (np->parent == root)
- np->parent = of_allnodes;
- of_attach_node(np);
- }
- np = next;
+ child = np->child;
+ np->child = NULL;
+
+ mutex_lock(&of_mutex);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ np->sibling = np->parent->child;
+ np->parent->child = np;
+ of_node_clear_flag(np, OF_DETACHED);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ __of_attach_node_sysfs(np);
+ mutex_unlock(&of_mutex);
+
+ while (child) {
+ next = child->sibling;
+ attach_node_and_children(child);
+ child = next;
}
return 0;
@@ -846,11 +901,8 @@ static int __init selftest_data_add(void)
return -EINVAL;
}
- if (!of_allnodes) {
- /* enabling flag for removing nodes */
- selftest_live_tree = true;
- of_allnodes = selftest_data_node;
-
+ if (!of_root) {
+ of_root = selftest_data_node;
for_each_of_allnodes(np)
__of_attach_node_sysfs(np);
of_aliases = of_find_node_by_path("/aliases");
@@ -859,62 +911,532 @@ static int __init selftest_data_add(void)
}
/* attach the sub-tree to live tree */
- return attach_node_and_children(selftest_data_node);
+ np = selftest_data_node->child;
+ while (np) {
+ struct device_node *next = np->sibling;
+ np->parent = of_root;
+ attach_node_and_children(np);
+ np = next;
+ }
+ return 0;
}
-/**
- * detach_node_and_children - detaches node
- * and its children from live tree
- *
- * @np: Node to detach from live tree
- */
-static void detach_node_and_children(struct device_node *np)
+#ifdef CONFIG_OF_OVERLAY
+
+static int selftest_probe(struct platform_device *pdev)
{
- while (np->child)
- detach_node_and_children(np->child);
- of_detach_node(np);
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ if (np == NULL) {
+ dev_err(dev, "No OF data for device\n");
+ return -EINVAL;
+
+ }
+
+ dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
+
+ of_platform_populate(np, NULL, NULL, &pdev->dev);
+
+ return 0;
}
-/**
- * selftest_data_remove - removes the selftest data
- * nodes from the live tree
- */
-static void selftest_data_remove(void)
+static int selftest_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
+ return 0;
+}
+
+static struct of_device_id selftest_match[] = {
+ { .compatible = "selftest", },
+ {},
+};
+
+static struct platform_driver selftest_driver = {
+ .probe = selftest_probe,
+ .remove = selftest_remove,
+ .driver = {
+ .name = "selftest",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(selftest_match),
+ },
+};
+
+/* get the platform device instantiated at the path */
+static struct platform_device *of_path_to_platform_device(const char *path)
{
struct device_node *np;
- struct property *prop;
+ struct platform_device *pdev;
+
+ np = of_find_node_by_path(path);
+ if (np == NULL)
+ return NULL;
+
+ pdev = of_find_device_by_node(np);
+ of_node_put(np);
+
+ return pdev;
+}
+
+/* find out if a platform device exists at that path */
+static int of_path_platform_device_exists(const char *path)
+{
+ struct platform_device *pdev;
+
+ pdev = of_path_to_platform_device(path);
+ platform_device_put(pdev);
+ return pdev != NULL;
+}
+
+static const char *selftest_path(int nr)
+{
+ static char buf[256];
+
+ snprintf(buf, sizeof(buf) - 1,
+ "/testcase-data/overlay-node/test-bus/test-selftest%d", nr);
+ buf[sizeof(buf) - 1] = '\0';
+
+ return buf;
+}
+
+static const char *overlay_path(int nr)
+{
+ static char buf[256];
+
+ snprintf(buf, sizeof(buf) - 1,
+ "/testcase-data/overlay%d", nr);
+ buf[sizeof(buf) - 1] = '\0';
+
+ return buf;
+}
+
+static const char *bus_path = "/testcase-data/overlay-node/test-bus";
+
+static int of_selftest_apply_overlay(int selftest_nr, int overlay_nr,
+ int *overlay_id)
+{
+ struct device_node *np = NULL;
+ int ret, id = -1;
+
+ np = of_find_node_by_path(overlay_path(overlay_nr));
+ if (np == NULL) {
+ selftest(0, "could not find overlay node @\"%s\"\n",
+ overlay_path(overlay_nr));
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = of_overlay_create(np);
+ if (ret < 0) {
+ selftest(0, "could not create overlay from \"%s\"\n",
+ overlay_path(overlay_nr));
+ goto out;
+ }
+ id = ret;
+
+ ret = 0;
+
+out:
+ of_node_put(np);
+
+ if (overlay_id)
+ *overlay_id = id;
- if (selftest_live_tree) {
- of_node_put(of_aliases);
- of_node_put(of_chosen);
- of_aliases = NULL;
- of_chosen = NULL;
- for_each_child_of_node(of_allnodes, np)
- detach_node_and_children(np);
- __of_detach_node_sysfs(of_allnodes);
- of_allnodes = NULL;
+ return ret;
+}
+
+/* apply an overlay while checking before and after states */
+static int of_selftest_apply_overlay_check(int overlay_nr, int selftest_nr,
+ int before, int after)
+{
+ int ret;
+
+ /* selftest device must not be in before state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !before ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, NULL);
+ if (ret != 0) {
+ /* of_selftest_apply_overlay already called selftest() */
+ return ret;
+ }
+
+ /* selftest device must be to set to after state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != after) {
+ selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !after ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* apply an overlay and then revert it while checking before, after states */
+static int of_selftest_apply_revert_overlay_check(int overlay_nr,
+ int selftest_nr, int before, int after)
+{
+ int ret, ov_id;
+
+ /* selftest device must be in before state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !before ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ /* apply the overlay */
+ ret = of_selftest_apply_overlay(overlay_nr, selftest_nr, &ov_id);
+ if (ret != 0) {
+ /* of_selftest_apply_overlay already called selftest() */
+ return ret;
+ }
+
+ /* selftest device must be in after state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != after) {
+ selftest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !after ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ ret = of_overlay_destroy(ov_id);
+ if (ret != 0) {
+ selftest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr));
+ return ret;
+ }
+
+ /* selftest device must be again in before state */
+ if (of_path_platform_device_exists(selftest_path(selftest_nr))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr),
+ selftest_path(selftest_nr),
+ !before ? "enabled" : "disabled");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* test activation of device */
+static void of_selftest_overlay_0(void)
+{
+ int ret;
+
+ /* device should enable */
+ ret = of_selftest_apply_overlay_check(0, 0, 0, 1);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 0);
+}
+
+/* test deactivation of device */
+static void of_selftest_overlay_1(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_overlay_check(1, 1, 1, 0);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 1);
+}
+
+/* test activation of device */
+static void of_selftest_overlay_2(void)
+{
+ int ret;
+
+ /* device should enable */
+ ret = of_selftest_apply_overlay_check(2, 2, 0, 1);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 2);
+}
+
+/* test deactivation of device */
+static void of_selftest_overlay_3(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_overlay_check(3, 3, 1, 0);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 3);
+}
+
+/* test activation of a full device node */
+static void of_selftest_overlay_4(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_overlay_check(4, 4, 0, 1);
+ if (ret != 0)
+ return;
+
+ selftest(1, "overlay test %d passed\n", 4);
+}
+
+/* test overlay apply/revert sequence */
+static void of_selftest_overlay_5(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_revert_overlay_check(5, 5, 0, 1);
+ if (ret != 0)
return;
+
+ selftest(1, "overlay test %d passed\n", 5);
+}
+
+/* test overlay application in sequence */
+static void of_selftest_overlay_6(void)
+{
+ struct device_node *np;
+ int ret, i, ov_id[2];
+ int overlay_nr = 6, selftest_nr = 6;
+ int before = 0, after = 1;
+
+ /* selftest device must be in before state */
+ for (i = 0; i < 2; i++) {
+ if (of_path_platform_device_exists(
+ selftest_path(selftest_nr + i))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i),
+ !before ? "enabled" : "disabled");
+ return;
+ }
}
- while (last_node_index-- > 0) {
- if (nodes[last_node_index]) {
- np = of_find_node_by_path(nodes[last_node_index]->full_name);
- if (np == nodes[last_node_index]) {
- if (of_aliases == np) {
- of_node_put(of_aliases);
- of_aliases = NULL;
- }
- detach_node_and_children(np);
- } else {
- for_each_property_of_node(np, prop) {
- if (strcmp(prop->name, "testcase-alias") == 0)
- of_remove_property(np, prop);
- }
- }
+ /* apply the overlays */
+ for (i = 0; i < 2; i++) {
+
+ np = of_find_node_by_path(overlay_path(overlay_nr + i));
+ if (np == NULL) {
+ selftest(0, "could not find overlay node @\"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+
+ ret = of_overlay_create(np);
+ if (ret < 0) {
+ selftest(0, "could not create overlay from \"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+ ov_id[i] = ret;
+ }
+
+ for (i = 0; i < 2; i++) {
+ /* selftest device must be in after state */
+ if (of_path_platform_device_exists(
+ selftest_path(selftest_nr + i))
+ != after) {
+ selftest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i),
+ !after ? "enabled" : "disabled");
+ return;
}
}
+
+ for (i = 1; i >= 0; i--) {
+ ret = of_overlay_destroy(ov_id[i]);
+ if (ret != 0) {
+ selftest(0, "overlay @\"%s\" failed destroy @\"%s\"\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i));
+ return;
+ }
+ }
+
+ for (i = 0; i < 2; i++) {
+ /* selftest device must be again in before state */
+ if (of_path_platform_device_exists(
+ selftest_path(selftest_nr + i))
+ != before) {
+ selftest(0, "overlay @\"%s\" with device @\"%s\" %s\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr + i),
+ !before ? "enabled" : "disabled");
+ return;
+ }
+ }
+
+ selftest(1, "overlay test %d passed\n", 6);
+}
+
+/* test overlay application in sequence */
+static void of_selftest_overlay_8(void)
+{
+ struct device_node *np;
+ int ret, i, ov_id[2];
+ int overlay_nr = 8, selftest_nr = 8;
+
+ /* we don't care about device state in this test */
+
+ /* apply the overlays */
+ for (i = 0; i < 2; i++) {
+
+ np = of_find_node_by_path(overlay_path(overlay_nr + i));
+ if (np == NULL) {
+ selftest(0, "could not find overlay node @\"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+
+ ret = of_overlay_create(np);
+ if (ret < 0) {
+ selftest(0, "could not create overlay from \"%s\"\n",
+ overlay_path(overlay_nr + i));
+ return;
+ }
+ ov_id[i] = ret;
+ }
+
+ /* now try to remove first overlay (it should fail) */
+ ret = of_overlay_destroy(ov_id[0]);
+ if (ret == 0) {
+ selftest(0, "overlay @\"%s\" was destroyed @\"%s\"\n",
+ overlay_path(overlay_nr + 0),
+ selftest_path(selftest_nr));
+ return;
+ }
+
+ /* removing them in order should work */
+ for (i = 1; i >= 0; i--) {
+ ret = of_overlay_destroy(ov_id[i]);
+ if (ret != 0) {
+ selftest(0, "overlay @\"%s\" not destroyed @\"%s\"\n",
+ overlay_path(overlay_nr + i),
+ selftest_path(selftest_nr));
+ return;
+ }
+ }
+
+ selftest(1, "overlay test %d passed\n", 8);
+}
+
+/* test insertion of a bus with parent devices */
+static void of_selftest_overlay_10(void)
+{
+ int ret;
+ char *child_path;
+
+ /* device should disable */
+ ret = of_selftest_apply_overlay_check(10, 10, 0, 1);
+ if (selftest(ret == 0, "overlay test %d failed; overlay application\n", 10))
+ return;
+
+ child_path = kasprintf(GFP_KERNEL, "%s/test-selftest101",
+ selftest_path(10));
+ if (selftest(child_path, "overlay test %d failed; kasprintf\n", 10))
+ return;
+
+ ret = of_path_platform_device_exists(child_path);
+ kfree(child_path);
+ if (selftest(ret, "overlay test %d failed; no child device\n", 10))
+ return;
+}
+
+/* test insertion of a bus with parent devices (and revert) */
+static void of_selftest_overlay_11(void)
+{
+ int ret;
+
+ /* device should disable */
+ ret = of_selftest_apply_revert_overlay_check(11, 11, 0, 1);
+ if (selftest(ret == 0, "overlay test %d failed; overlay application\n", 11))
+ return;
+}
+
+static void __init of_selftest_overlay(void)
+{
+ struct device_node *bus_np = NULL;
+ int ret;
+
+ ret = platform_driver_register(&selftest_driver);
+ if (ret != 0) {
+ selftest(0, "could not register selftest driver\n");
+ goto out;
+ }
+
+ bus_np = of_find_node_by_path(bus_path);
+ if (bus_np == NULL) {
+ selftest(0, "could not find bus_path \"%s\"\n", bus_path);
+ goto out;
+ }
+
+ ret = of_platform_populate(bus_np, of_default_bus_match_table,
+ NULL, NULL);
+ if (ret != 0) {
+ selftest(0, "could not populate bus @ \"%s\"\n", bus_path);
+ goto out;
+ }
+
+ if (!of_path_platform_device_exists(selftest_path(100))) {
+ selftest(0, "could not find selftest0 @ \"%s\"\n",
+ selftest_path(100));
+ goto out;
+ }
+
+ if (of_path_platform_device_exists(selftest_path(101))) {
+ selftest(0, "selftest1 @ \"%s\" should not exist\n",
+ selftest_path(101));
+ goto out;
+ }
+
+ selftest(1, "basic infrastructure of overlays passed");
+
+ /* tests in sequence */
+ of_selftest_overlay_0();
+ of_selftest_overlay_1();
+ of_selftest_overlay_2();
+ of_selftest_overlay_3();
+ of_selftest_overlay_4();
+ of_selftest_overlay_5();
+ of_selftest_overlay_6();
+ of_selftest_overlay_8();
+
+ of_selftest_overlay_10();
+ of_selftest_overlay_11();
+
+out:
+ of_node_put(bus_np);
}
+#else
+static inline void __init of_selftest_overlay(void) { }
+#endif
+
static int __init of_selftest(void)
{
struct device_node *np;
@@ -947,9 +1469,7 @@ static int __init of_selftest(void)
of_selftest_parse_interrupts_extended();
of_selftest_match_node();
of_selftest_platform_populate();
-
- /* removing selftest data from live tree */
- selftest_data_remove();
+ of_selftest_overlay();
/* Double check linkage after removing testcase data */
of_selftest_check_tree_linkage();
diff --git a/include/linux/of.h b/include/linux/of.h
index 4a6a489a7506..45447c6f21f7 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -24,6 +24,7 @@
#include <linux/topology.h>
#include <linux/notifier.h>
#include <linux/property.h>
+#include <linux/list.h>
#include <asm/byteorder.h>
#include <asm/errno.h>
@@ -56,8 +57,6 @@ struct device_node {
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
- struct device_node *next; /* next device of same type */
- struct device_node *allnext; /* next in list of all nodes */
struct kobject kobj;
unsigned long _flags;
void *data;
@@ -75,6 +74,12 @@ struct of_phandle_args {
uint32_t args[MAX_PHANDLE_ARGS];
};
+struct of_reconfig_data {
+ struct device_node *dn;
+ struct property *prop;
+ struct property *old_prop;
+};
+
/* initialize a node */
extern struct kobj_type of_node_ktype;
static inline void of_node_init(struct device_node *node)
@@ -106,18 +111,17 @@ static inline struct device_node *of_node_get(struct device_node *node)
static inline void of_node_put(struct device_node *node) { }
#endif /* !CONFIG_OF_DYNAMIC */
-#ifdef CONFIG_OF
-
/* Pointer for first entry in chain of all nodes. */
-extern struct device_node *of_allnodes;
+extern struct device_node *of_root;
extern struct device_node *of_chosen;
extern struct device_node *of_aliases;
extern struct device_node *of_stdout;
extern raw_spinlock_t devtree_lock;
+#ifdef CONFIG_OF
static inline bool of_have_populated_dt(void)
{
- return of_allnodes != NULL;
+ return of_root != NULL;
}
static inline bool of_node_is_root(const struct device_node *node)
@@ -161,6 +165,7 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag
clear_bit(flag, &p->_flags);
}
+extern struct device_node *__of_find_all_nodes(struct device_node *prev);
extern struct device_node *of_find_all_nodes(struct device_node *prev);
/*
@@ -216,8 +221,9 @@ static inline const char *of_node_full_name(const struct device_node *np)
return np ? np->full_name : "<no-node>";
}
-#define for_each_of_allnodes(dn) \
- for (dn = of_allnodes; dn; dn = dn->allnext)
+#define for_each_of_allnodes_from(from, dn) \
+ for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn))
+#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn)
extern struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);
extern struct device_node *of_find_node_by_type(struct device_node *from,
@@ -229,7 +235,13 @@ extern struct device_node *of_find_matching_node_and_match(
const struct of_device_id *matches,
const struct of_device_id **match);
-extern struct device_node *of_find_node_by_path(const char *path);
+extern struct device_node *of_find_node_opts_by_path(const char *path,
+ const char **opts);
+static inline struct device_node *of_find_node_by_path(const char *path)
+{
+ return of_find_node_opts_by_path(path, NULL);
+}
+
extern struct device_node *of_find_node_by_phandle(phandle handle);
extern struct device_node *of_get_parent(const struct device_node *node);
extern struct device_node *of_get_next_parent(struct device_node *node);
@@ -280,7 +292,7 @@ extern int of_property_read_string_helper(struct device_node *np,
const char **out_strs, size_t sz, int index);
extern int of_device_is_compatible(const struct device_node *device,
const char *);
-extern int of_device_is_available(const struct device_node *device);
+extern bool of_device_is_available(const struct device_node *device);
extern const void *of_get_property(const struct device_node *node,
const char *name,
int *lenp);
@@ -322,16 +334,6 @@ extern int of_update_property(struct device_node *np, struct property *newprop);
#define OF_RECONFIG_REMOVE_PROPERTY 0x0004
#define OF_RECONFIG_UPDATE_PROPERTY 0x0005
-struct of_prop_reconfig {
- struct device_node *dn;
- struct property *prop;
- struct property *old_prop;
-};
-
-extern int of_reconfig_notifier_register(struct notifier_block *);
-extern int of_reconfig_notifier_unregister(struct notifier_block *);
-extern int of_reconfig_notify(unsigned long, void *);
-
extern int of_attach_node(struct device_node *);
extern int of_detach_node(struct device_node *);
@@ -390,6 +392,12 @@ static inline struct device_node *of_find_node_by_path(const char *path)
return NULL;
}
+static inline struct device_node *of_find_node_opts_by_path(const char *path,
+ const char **opts)
+{
+ return NULL;
+}
+
static inline struct device_node *of_get_parent(const struct device_node *node)
{
return NULL;
@@ -431,9 +439,9 @@ static inline int of_device_is_compatible(const struct device_node *device,
return 0;
}
-static inline int of_device_is_available(const struct device_node *device)
+static inline bool of_device_is_available(const struct device_node *device)
{
- return 0;
+ return false;
}
static inline struct property *of_find_property(const struct device_node *np,
@@ -775,6 +783,13 @@ static inline int of_property_read_u32(const struct device_node *np,
return of_property_read_u32_array(np, propname, out_value, 1);
}
+static inline int of_property_read_s32(const struct device_node *np,
+ const char *propname,
+ s32 *out_value)
+{
+ return of_property_read_u32(np, propname, (u32*) out_value);
+}
+
#define of_property_for_each_u32(np, propname, prop, p, u) \
for (prop = of_find_property(np, propname, NULL), \
p = of_prop_next_u32(prop, NULL, &u); \
@@ -843,7 +858,7 @@ static inline int of_get_available_child_count(const struct device_node *np)
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#else
-#define _OF_DECLARE(table, name, compat, fn, fn_type) \
+#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__attribute__((unused)) \
= { .compatible = compat, \
@@ -894,7 +909,19 @@ struct of_changeset {
struct list_head entries;
};
+enum of_reconfig_change {
+ OF_RECONFIG_NO_CHANGE = 0,
+ OF_RECONFIG_CHANGE_ADD,
+ OF_RECONFIG_CHANGE_REMOVE,
+};
+
#ifdef CONFIG_OF_DYNAMIC
+extern int of_reconfig_notifier_register(struct notifier_block *);
+extern int of_reconfig_notifier_unregister(struct notifier_block *);
+extern int of_reconfig_notify(unsigned long, struct of_reconfig_data *rd);
+extern int of_reconfig_get_state_change(unsigned long action,
+ struct of_reconfig_data *arg);
+
extern void of_changeset_init(struct of_changeset *ocs);
extern void of_changeset_destroy(struct of_changeset *ocs);
extern int of_changeset_apply(struct of_changeset *ocs);
@@ -932,9 +959,58 @@ static inline int of_changeset_update_property(struct of_changeset *ocs,
{
return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop);
}
-#endif
+#else /* CONFIG_OF_DYNAMIC */
+static inline int of_reconfig_notifier_register(struct notifier_block *nb)
+{
+ return -EINVAL;
+}
+static inline int of_reconfig_notifier_unregister(struct notifier_block *nb)
+{
+ return -EINVAL;
+}
+static inline int of_reconfig_notify(unsigned long action,
+ struct of_reconfig_data *arg)
+{
+ return -EINVAL;
+}
+static inline int of_reconfig_get_state_change(unsigned long action,
+ struct of_reconfig_data *arg)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_OF_DYNAMIC */
/* CONFIG_OF_RESOLVE api */
extern int of_resolve_phandles(struct device_node *tree);
+/**
+ * Overlay support
+ */
+
+#ifdef CONFIG_OF_OVERLAY
+
+/* ID based overlays; the API for external users */
+int of_overlay_create(struct device_node *tree);
+int of_overlay_destroy(int id);
+int of_overlay_destroy_all(void);
+
+#else
+
+static inline int of_overlay_create(struct device_node *tree)
+{
+ return -ENOTSUPP;
+}
+
+static inline int of_overlay_destroy(int id)
+{
+ return -ENOTSUPP;
+}
+
+static inline int of_overlay_destroy_all(void)
+{
+ return -ENOTSUPP;
+}
+
+#endif
+
#endif /* _LINUX_OF_H */
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 8cb14eb393d6..d88e81be6368 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -106,7 +106,7 @@ extern int of_address_to_resource(struct device_node *dev, int index,
struct resource *r);
void __iomem *of_iomap(struct device_node *node, int index);
void __iomem *of_io_request_and_map(struct device_node *device,
- int index, char *name);
+ int index, const char *name);
#else
#include <linux/io.h>
@@ -123,7 +123,7 @@ static inline void __iomem *of_iomap(struct device_node *device, int index)
}
static inline void __iomem *of_io_request_and_map(struct device_node *device,
- int index, char *name)
+ int index, const char *name)
{
return IOMEM_ERR_PTR(-EINVAL);
}
diff --git a/include/linux/of_pdt.h b/include/linux/of_pdt.h
index c65a18a0cfdf..7e09244bb679 100644
--- a/include/linux/of_pdt.h
+++ b/include/linux/of_pdt.h
@@ -39,7 +39,6 @@ extern void *prom_early_alloc(unsigned long size);
/* for building the device tree */
extern void of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops);
-extern void (*of_pdt_build_more)(struct device_node *dp,
- struct device_node ***nextp);
+extern void (*of_pdt_build_more)(struct device_node *dp);
#endif /* _LINUX_OF_PDT_H */
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index c2b0627a2317..8a860f096c35 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -84,4 +84,10 @@ static inline int of_platform_populate(struct device_node *root,
static inline void of_platform_depopulate(struct device *parent) { }
#endif
+#ifdef CONFIG_OF_DYNAMIC
+extern void of_platform_register_reconfig_notifier(void);
+#else
+static inline void of_platform_register_reconfig_notifier(void) { }
+#endif
+
#endif /* _LINUX_OF_PLATFORM_H */