diff options
-rw-r--r-- | Documentation/devicetree/of_selftest.txt | 20 | ||||
-rw-r--r-- | Documentation/devicetree/todo.txt | 1 | ||||
-rw-r--r-- | drivers/mfd/vexpress-sysreg.c | 2 | ||||
-rw-r--r-- | drivers/of/Kconfig | 5 | ||||
-rw-r--r-- | drivers/of/Makefile | 4 | ||||
-rw-r--r-- | drivers/of/address.c | 4 | ||||
-rw-r--r-- | drivers/of/base.c | 107 | ||||
-rw-r--r-- | drivers/of/dynamic.c | 13 | ||||
-rw-r--r-- | drivers/of/fdt.c | 87 | ||||
-rw-r--r-- | drivers/of/pdt.c | 27 | ||||
-rw-r--r-- | drivers/of/platform.c | 10 | ||||
-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 | ||||
-rw-r--r-- | include/linux/of.h | 26 | ||||
-rw-r--r-- | include/linux/of_address.h | 4 | ||||
-rw-r--r-- | include/linux/of_pdt.h | 3 |
20 files changed, 215 insertions, 208 deletions
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/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/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..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; } diff --git a/include/linux/of.h b/include/linux/of.h index 29f0adc5f3e4..27635c89d8c2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -56,7 +56,6 @@ struct device_node { 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; @@ -108,7 +107,7 @@ static inline void of_node_put(struct device_node *node) { } #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; @@ -116,7 +115,7 @@ extern raw_spinlock_t devtree_lock; 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) @@ -160,6 +159,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); /* @@ -215,8 +215,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, @@ -275,7 +276,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); @@ -426,9 +427,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, @@ -760,6 +761,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); \ @@ -828,7 +836,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, \ 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 */ |