summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/Kconfig5
-rw-r--r--drivers/of/Makefile4
-rw-r--r--drivers/of/address.c4
-rw-r--r--drivers/of/base.c107
-rw-r--r--drivers/of/dynamic.c13
-rw-r--r--drivers/of/fdt.c87
-rw-r--r--drivers/of/pdt.c27
-rw-r--r--drivers/of/platform.c10
-rw-r--r--drivers/of/unittest-data/testcases.dts (renamed from drivers/of/testcase-data/testcases.dts)0
-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-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)110
14 files changed, 191 insertions, 176 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 1a13f5b722c5..fbe8f8d418f7 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -7,8 +7,8 @@ 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
@@ -23,6 +23,7 @@ config OF_FLATTREE
bool
select DTC
select LIBFDT
+ select CRC32
config OF_EARLY_FLATTREE
bool
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index ca9209ce50cd..d90553fcd37f 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -6,8 +6,8 @@ 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
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 06af494184d6..ad2906919d45 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -491,7 +491,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
*/
ranges = of_get_property(parent, rprop, &rlen);
if (ranges == NULL && !of_empty_ranges_quirk()) {
- pr_err("OF: no ranges; cannot translate\n");
+ pr_debug("OF: no ranges; cannot translate\n");
return 1;
}
if (ranges == NULL || rlen == 0) {
@@ -884,7 +884,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 3823edf2d012..2d5dfb8b2e65 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -32,8 +32,8 @@
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;
@@ -48,7 +48,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 +204,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 +245,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 +276,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;
@@ -485,7 +500,7 @@ EXPORT_SYMBOL(of_device_is_compatible);
* of_machine_is_compatible - Test root of device tree for a given compatible value
* @compat: compatible string to look for in root node's compatible property.
*
- * Returns true if the root node has the given value in its
+ * Returns a positive integer if the root node has the given value in its
* compatible property.
*/
int of_machine_is_compatible(const char *compat)
@@ -507,27 +522,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 +550,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);
@@ -577,9 +592,9 @@ EXPORT_SYMBOL(of_get_parent);
* of_get_next_parent - Iterate to a node's parent
* @node: Node to get parent of
*
- * This is like of_get_parent() except that it drops the
- * refcount on the passed node, making it suitable for iterating
- * through a node's parents.
+ * This is like of_get_parent() except that it drops the
+ * refcount on the passed node, making it suitable for iterating
+ * through a node's parents.
*
* Returns a node pointer with refcount incremented, use
* of_node_put() on it when done.
@@ -736,7 +751,7 @@ struct device_node *of_find_node_by_path(const char *path)
unsigned long flags;
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 != '/') {
@@ -761,7 +776,7 @@ 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);
@@ -790,8 +805,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 +834,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 +865,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 +895,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);
@@ -923,7 +933,7 @@ const struct of_device_id *__of_match_node(const struct of_device_id *matches,
}
/**
- * of_match_node - Tell if an device_node has a matching of_match structure
+ * of_match_node - Tell if a device_node has a matching of_match structure
* @matches: array of of device match structures to search in
* @node: the of device structure to match against
*
@@ -967,8 +977,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 +1034,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);
@@ -1516,21 +1525,21 @@ EXPORT_SYMBOL(of_parse_phandle);
* Returns 0 on success and fills out_args, on error returns appropriate
* errno value.
*
- * Caller is responsible to call of_node_put() on the returned out_args->node
+ * Caller is responsible to call of_node_put() on the returned out_args->np
* pointer.
*
* Example:
*
* phandle1: node1 {
- * #list-cells = <2>;
+ * #list-cells = <2>;
* }
*
* phandle2: node2 {
- * #list-cells = <1>;
+ * #list-cells = <1>;
* }
*
* node3 {
- * list = <&phandle1 1 2 &phandle2 3>;
+ * list = <&phandle1 1 2 &phandle2 3>;
* }
*
* To get a device_node of the `node2' node you may call this:
@@ -1559,7 +1568,7 @@ EXPORT_SYMBOL(of_parse_phandle_with_args);
* Returns 0 on success and fills out_args, on error returns appropriate
* errno value.
*
- * Caller is responsible to call of_node_put() on the returned out_args->node
+ * Caller is responsible to call of_node_put() on the returned out_args->np
* pointer.
*
* Example:
@@ -1571,7 +1580,7 @@ EXPORT_SYMBOL(of_parse_phandle_with_args);
* }
*
* node3 {
- * list = <&phandle1 0 2 &phandle2 2 3>;
+ * list = <&phandle1 0 2 &phandle2 2 3>;
* }
*
* To get a device_node of the `node2' node you may call this:
@@ -1805,14 +1814,14 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np,
}
/**
- * of_alias_scan - Scan all properties of 'aliases' node
+ * of_alias_scan - Scan all properties of the 'aliases' node
*
- * The function scans all the properties of 'aliases' node and populate
- * the the global lookup table with the properties. It returns the
- * number of alias_prop found, or error code in error case.
+ * The function scans all the properties of the 'aliases' node and populates
+ * the global lookup table with the properties. It returns the
+ * number of alias properties found, or an error code in case of failure.
*
* @dt_alloc: An allocator that provides a virtual address to memory
- * for the resulting tree
+ * for storing the resulting tree
*/
void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align))
{
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index d4994177dec2..d43f3059963b 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -117,8 +117,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);
}
@@ -154,17 +152,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 {
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 30e97bcc4f88..7f6ee31d5650 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,8 +224,6 @@ 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'*/
@@ -254,7 +254,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 +296,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 +308,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,11 +324,13 @@ 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);
+ if (nodepp)
+ *nodepp = np;
return mem;
}
@@ -352,7 +354,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 +374,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 +389,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 +425,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
*/
@@ -930,6 +932,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);
}
@@ -994,15 +1001,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;
}
@@ -1041,7 +1047,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 */
@@ -1080,27 +1086,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/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 3b64d0bf5bba..656cccf0e680 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -138,7 +138,7 @@ struct platform_device *of_device_alloc(struct device_node *np,
}
dev->dev.of_node = of_node_get(np);
- dev->dev.parent = parent;
+ dev->dev.parent = parent ? : &platform_bus;
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
@@ -291,7 +291,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
/* setup generic device info */
dev->dev.of_node = of_node_get(node);
- dev->dev.parent = parent;
+ dev->dev.parent = parent ? : &platform_bus;
dev->dev.platform_data = platform_data;
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
@@ -500,6 +500,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;
@@ -542,7 +543,10 @@ 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);
diff --git a/drivers/of/testcase-data/testcases.dts b/drivers/of/unittest-data/testcases.dts
index 6994e15c24bf..6994e15c24bf 100644
--- a/drivers/of/testcase-data/testcases.dts
+++ b/drivers/of/unittest-data/testcases.dts
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/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..46af7019d291 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/unittest.c
@@ -30,15 +30,17 @@ 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)
{
@@ -148,7 +150,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 +160,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 +174,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"
@@ -702,10 +696,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 +727,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 +784,29 @@ 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;
- /* skip root node */
- np = np->child;
- /* storing a copy in temporary node */
- dup = np;
+ dup = of_find_node_by_path(np->full_name);
+ if (dup) {
+ update_node_properties(np, dup);
+ return 0;
+ }
- while (dup) {
+ /* Children of the root need to be remembered for removal */
+ if (np->parent == of_root) {
if (WARN_ON(last_node_index >= NO_OF_NODES))
return -EINVAL;
- nodes[last_node_index++] = dup;
- dup = dup->sibling;
+ nodes[last_node_index++] = np;
}
- 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;
+ np->sibling = NULL;
+ of_attach_node(np);
+ while (child) {
+ next = child->sibling;
+ attach_node_and_children(child);
+ child = next;
}
return 0;
@@ -846,10 +851,10 @@ static int __init selftest_data_add(void)
return -EINVAL;
}
- if (!of_allnodes) {
+ if (!of_root) {
/* enabling flag for removing nodes */
selftest_live_tree = true;
- of_allnodes = selftest_data_node;
+ of_root = selftest_data_node;
for_each_of_allnodes(np)
__of_attach_node_sysfs(np);
@@ -859,7 +864,14 @@ 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;
}
/**
@@ -889,10 +901,10 @@ static void selftest_data_remove(void)
of_node_put(of_chosen);
of_aliases = NULL;
of_chosen = NULL;
- for_each_child_of_node(of_allnodes, np)
+ for_each_child_of_node(of_root, np)
detach_node_and_children(np);
- __of_detach_node_sysfs(of_allnodes);
- of_allnodes = NULL;
+ __of_detach_node_sysfs(of_root);
+ of_root = NULL;
return;
}