Commit e98d6e7f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'devicetree-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux

Pull devicetree changes from Grant Likely:
 "This branch contains bug fixes and new features for the devicetree
  code.

  Most of the changes are either new testcases for the selftest code or
  documentation changes.  The most notable change is the addition of a
  phandle resolver for use when grafting in a second device tree blob
  into the core tree.  The resolver isn't currently used by anything
  other than the selftest module, but it will be used to support device
  tree overlays; probably in the v3.19 timeframe.

  Also note that I've moved my normal tree from git.secretlab.ca to
  git.kernel.org"

* tag 'devicetree-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux:
  of/selftest: Move hash table off stack to fix large frame size
  To remove non-ascii characters in of_selftest.txt
  of/selftest: Use the resolver to fixup phandles
  of: Introduce Device Tree resolve support.
  of/selftest: Add a test for duplicate phandles
  of: Don't try to search when phandle == 0
  of/selftest: Test structure of device tree
  of: Fix NULL dereference in selftest removal code
  of: add vendor prefix for Chipidea
  of: Add vendor prefix for Innolux Corporation
  of: Add vendor prefix for Sitronix
  devicetree: bindings: Document Gateworks vendor prefix
  of: Add vendor prefix for Energy Micro
  dt/documentation: add specification of dma bus information
parents 4e0b7fe3 2118f4b8
...@@ -46,6 +46,7 @@ dmo Data Modul AG ...@@ -46,6 +46,7 @@ dmo Data Modul AG
ebv EBV Elektronik ebv EBV Elektronik
edt Emerging Display Technologies edt Emerging Display Technologies
emmicro EM Microelectronic emmicro EM Microelectronic
energymicro Silicon Laboratories (formerly Energy Micro AS)
epcos EPCOS AG epcos EPCOS AG
epfl Ecole Polytechnique Fédérale de Lausanne epfl Ecole Polytechnique Fédérale de Lausanne
epson Seiko Epson Corp. epson Seiko Epson Corp.
...@@ -62,6 +63,7 @@ globalscale Globalscale Technologies, Inc. ...@@ -62,6 +63,7 @@ globalscale Globalscale Technologies, Inc.
gmt Global Mixed-mode Technology, Inc. gmt Global Mixed-mode Technology, Inc.
google Google, Inc. google Google, Inc.
gumstix Gumstix, Inc. gumstix Gumstix, Inc.
gw Gateworks Corporation
haoyu Haoyu Microelectronic Co. Ltd. haoyu Haoyu Microelectronic Co. Ltd.
hisilicon Hisilicon Limited. hisilicon Hisilicon Limited.
honeywell Honeywell honeywell Honeywell
...@@ -71,6 +73,7 @@ ibm International Business Machines (IBM) ...@@ -71,6 +73,7 @@ ibm International Business Machines (IBM)
idt Integrated Device Technologies, Inc. idt Integrated Device Technologies, Inc.
iom Iomega Corporation iom Iomega Corporation
img Imagination Technologies Ltd. img Imagination Technologies Ltd.
innolux Innolux Corporation
intel Intel Corporation intel Intel Corporation
intercontrol Inter Control Group intercontrol Inter Control Group
isee ISEE 2007 S.L. isee ISEE 2007 S.L.
...@@ -132,6 +135,7 @@ simtek ...@@ -132,6 +135,7 @@ simtek
sii Seiko Instruments, Inc. sii Seiko Instruments, Inc.
silergy Silergy Corp. silergy Silergy Corp.
sirf SiRF Technology, Inc. sirf SiRF Technology, Inc.
sitronix Sitronix Technology Corporation
smsc Standard Microsystems Corporation smsc Standard Microsystems Corporation
snps Synopsys, Inc. snps Synopsys, Inc.
solidrun SolidRun solidrun SolidRun
......
...@@ -51,6 +51,8 @@ Table of Contents ...@@ -51,6 +51,8 @@ Table of Contents
VIII - Specifying device power management information (sleep property) VIII - Specifying device power management information (sleep property)
IX - Specifying dma bus information
Appendix A - Sample SOC node for MPC8540 Appendix A - Sample SOC node for MPC8540
...@@ -1332,6 +1334,57 @@ reasonably grouped in this manner, then create a virtual sleep controller ...@@ -1332,6 +1334,57 @@ reasonably grouped in this manner, then create a virtual sleep controller
(similar to an interrupt nexus, except that defining a standardized (similar to an interrupt nexus, except that defining a standardized
sleep-map should wait until its necessity is demonstrated). sleep-map should wait until its necessity is demonstrated).
IX - Specifying dma bus information
Some devices may have DMA memory range shifted relatively to the beginning of
RAM, or even placed outside of kernel RAM. For example, the Keystone 2 SoC
worked in LPAE mode with 4G memory has:
- RAM range: [0x8 0000 0000, 0x8 FFFF FFFF]
- DMA range: [ 0x8000 0000, 0xFFFF FFFF]
and DMA range is aliased into first 2G of RAM in HW.
In such cases, DMA addresses translation should be performed between CPU phys
and DMA addresses. The "dma-ranges" property is intended to be used
for describing the configuration of such system in DT.
In addition, each DMA master device on the DMA bus may or may not support
coherent DMA operations. The "dma-coherent" property is intended to be used
for identifying devices supported coherent DMA operations in DT.
* DMA Bus master
Optional property:
- dma-ranges: <prop-encoded-array> encoded as arbitrary number of triplets of
(child-bus-address, parent-bus-address, length). Each triplet specified
describes a contiguous DMA address range.
The dma-ranges property is used to describe the direct memory access (DMA)
structure of a memory-mapped bus whose device tree parent can be accessed
from DMA operations originating from the bus. It provides a means of
defining a mapping or translation between the physical address space of
the bus and the physical address space of the parent of the bus.
(for more information see ePAPR specification)
* DMA Bus child
Optional property:
- dma-ranges: <empty> value. if present - It means that DMA addresses
translation has to be enabled for this device.
- dma-coherent: Present if dma operations are coherent
Example:
soc {
compatible = "ti,keystone","simple-bus";
ranges = <0x0 0x0 0x0 0xc0000000>;
dma-ranges = <0x80000000 0x8 0x00000000 0x80000000>;
[...]
usb: usb@2680000 {
compatible = "ti,keystone-dwc3";
[...]
dma-coherent;
};
};
Appendix A - Sample SOC node for MPC8540 Appendix A - Sample SOC node for MPC8540
======================================== ========================================
......
Device Tree Dynamic Resolver Notes
----------------------------------
This document describes the implementation of the in-kernel
Device Tree resolver, residing in drivers/of/resolver.c and is a
companion document to Documentation/devicetree/dt-object-internal.txt[1]
How the resolver works
----------------------
The resolver is given as an input an arbitrary tree compiled with the
proper dtc option and having a /plugin/ tag. This generates the
appropriate __fixups__ & __local_fixups__ nodes as described in [1].
In sequence the resolver works by the following steps:
1. Get the maximum device tree phandle value from the live tree + 1.
2. Adjust all the local phandles of the tree to resolve by that amount.
3. Using the __local__fixups__ node information adjust all local references
by the same amount.
4. For each property in the __fixups__ node locate the node it references
in the live tree. This is the label used to tag the node.
5. Retrieve the phandle of the target of the fixup.
6. For each fixup in the property locate the node:property:offset location
and replace it with the phandle value.
...@@ -67,14 +67,14 @@ struct device_node { ...@@ -67,14 +67,14 @@ struct device_node {
... ...
}; };
Figure 1, describes a generic structure of machines un-flattened device tree Figure 1, describes a generic structure of machine's un-flattened device tree
considering only child and sibling pointers. There exists another pointer, considering only child and sibling pointers. There exists another pointer,
*parent, that is used to traverse the tree in the reverse direction. So, at *parent, that is used to traverse the tree in the reverse direction. So, at
a particular level the child node and all the sibling nodes will have a parent a particular level the child node and all the sibling nodes will have a parent
pointer pointing to a common node (e.g. child1, sibling2, sibling3, sibling4s pointer pointing to a common node (e.g. child1, sibling2, sibling3, sibling4's
parent points to root node) parent points to root node)
root (‘/’) root ('/')
| |
child1 -> sibling2 -> sibling3 -> sibling4 -> null child1 -> sibling2 -> sibling3 -> sibling4 -> null
| | | | | | | |
...@@ -113,8 +113,8 @@ via the following kernel symbols: ...@@ -113,8 +113,8 @@ via the following kernel symbols:
__dtb_testcases_begin - address marking the start of test data blob __dtb_testcases_begin - address marking the start of test data blob
__dtb_testcases_end - address marking the end of test data blob __dtb_testcases_end - address marking the end of test data blob
Secondly, it calls of_fdt_unflatten_device_tree() to unflatten the flattened Secondly, it calls of_fdt_unflatten_tree() to unflatten the flattened
blob. And finally, if the machines device tree (i.e live tree) is present, blob. And finally, if the machine's device tree (i.e live tree) is present,
then it attaches the unflattened test data tree to the live tree, else it then it attaches the unflattened test data tree to the live tree, else it
attaches itself as a live device tree. attaches itself as a live device tree.
...@@ -122,7 +122,7 @@ attach_node_and_children() uses of_attach_node() to attach the nodes into the ...@@ -122,7 +122,7 @@ attach_node_and_children() uses of_attach_node() to attach the nodes into the
live tree as explained below. To explain the same, the test data tree described live tree as explained below. To explain the same, the test data tree described
in Figure 2 is attached to the live tree described in Figure 1. in Figure 2 is attached to the live tree described in Figure 1.
root (‘/’) root ('/')
| |
testcase-data testcase-data
| |
...@@ -138,8 +138,8 @@ root->testcase-data->test-child0->test-child01->test-sibling1->test-sibling2 ...@@ -138,8 +138,8 @@ root->testcase-data->test-child0->test-child01->test-sibling1->test-sibling2
Figure 2: Example test data tree to be attached to live tree. 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 isnt According to the scenario above, the live tree is already present so it isn't
required to attach the root(‘/’) node. All other nodes are attached by calling required to attach the root('/') node. All other nodes are attached by calling
of_attach_node() on each node. of_attach_node() on each node.
In the function of_attach_node(), the new node is attached as the child of the In the function of_attach_node(), the new node is attached as the child of the
...@@ -148,7 +148,7 @@ replaces the current child and turns it into its sibling. So, when the testcase ...@@ -148,7 +148,7 @@ replaces the current child and turns it into its sibling. So, when the testcase
data node is attached to the live tree above (Figure 1), the final structure is data node is attached to the live tree above (Figure 1), the final structure is
as shown in Figure 3. as shown in Figure 3.
root (‘/’) root ('/')
| |
testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
| | | | | | | | | |
...@@ -170,7 +170,7 @@ testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null ...@@ -170,7 +170,7 @@ testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
null null
----------------------------------------------------------------------- -----------------------------------------------------------------------
root (‘/’) root ('/')
| |
testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null testcase-data -> child1 -> sibling2 -> sibling3 -> sibling4 -> null
| | | | | | | | | |
...@@ -191,8 +191,8 @@ test-child0 the test-sibling1 is attached that pushes the child node ...@@ -191,8 +191,8 @@ test-child0 the test-sibling1 is attached that pushes the child node
as mentioned above. as mentioned above.
If a duplicate node is found (i.e. if a node with same full_name property is If a duplicate node is found (i.e. if a node with same full_name property is
already present in the live tree), then the node isnt attached rather its already present in the live tree), then the node isn't attached rather its
properties are updated to the live trees node by calling the function properties are updated to the live tree's node by calling the function
update_node_properties(). update_node_properties().
...@@ -205,7 +205,7 @@ whole tree). selftest_data_remove() calls detach_node_and_children() that uses ...@@ -205,7 +205,7 @@ whole tree). selftest_data_remove() calls detach_node_and_children() that uses
of_detach_node() to detach the nodes from the live device tree. 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 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 attaching the previous node's allnext to current node's allnext pointer. And
then, it either updates the child pointer of given nodes parent to its then, it either updates the child pointer of given node's parent to its
sibling or attaches the previous sibling to the given nodes sibling, as sibling or attaches the previous sibling to the given node's sibling, as
appropriate. That is it :) appropriate. That is it :)
...@@ -11,6 +11,7 @@ config OF_SELFTEST ...@@ -11,6 +11,7 @@ config OF_SELFTEST
bool "Device Tree Runtime self tests" bool "Device Tree Runtime self tests"
depends on OF_IRQ && OF_EARLY_FLATTREE depends on OF_IRQ && OF_EARLY_FLATTREE
select OF_DYNAMIC select OF_DYNAMIC
select OF_RESOLVE
help help
This option builds in test cases for the device tree infrastructure This option builds in test cases for the device tree infrastructure
that are executed once at boot time, and the results dumped to the that are executed once at boot time, and the results dumped to the
...@@ -79,4 +80,7 @@ config OF_RESERVED_MEM ...@@ -79,4 +80,7 @@ config OF_RESERVED_MEM
help help
Helpers to allow for reservation of memory regions Helpers to allow for reservation of memory regions
config OF_RESOLVE
bool
endmenu # OF endmenu # OF
...@@ -13,6 +13,7 @@ obj-$(CONFIG_OF_PCI) += of_pci.o ...@@ -13,6 +13,7 @@ obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt
...@@ -1021,6 +1021,9 @@ struct device_node *of_find_node_by_phandle(phandle handle) ...@@ -1021,6 +1021,9 @@ struct device_node *of_find_node_by_phandle(phandle handle)
struct device_node *np; struct device_node *np;
unsigned long flags; unsigned long flags;
if (!handle)
return NULL;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
for (np = of_allnodes; np; np = np->allnext) for (np = of_allnodes; np; np = np->allnext)
if (np->phandle == handle) if (np->phandle == handle)
......
/*
* Functions for dealing with DT resolution
*
* 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.
*/
#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>
/* illegal phandle value (set when unresolved) */
#define OF_PHANDLE_ILLEGAL 0xdeadbeef
/**
* Find a node with the give full name by recursively following any of
* the child node links.
*/
static struct device_node *__of_find_node_by_full_name(struct device_node *node,
const char *full_name)
{
struct device_node *child, *found;
if (node == NULL)
return NULL;
/* check */
if (of_node_cmp(node->full_name, full_name) == 0)
return node;
for_each_child_of_node(node, child) {
found = __of_find_node_by_full_name(child, full_name);
if (found != NULL)
return found;
}
return NULL;
}
/*
* Find live tree's maximum phandle value.
*/
static phandle of_get_tree_max_phandle(void)
{
struct device_node *node;
phandle phandle;
unsigned long flags;
/* now search recursively */
raw_spin_lock_irqsave(&devtree_lock, flags);
phandle = 0;
for_each_of_allnodes(node) {
if (node->phandle != OF_PHANDLE_ILLEGAL &&
node->phandle > phandle)
phandle = node->phandle;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return phandle;
}
/*
* Adjust a subtree's phandle values by a given delta.
* Makes sure not to just adjust the device node's phandle value,
* but modify the phandle properties values as well.
*/
static void __of_adjust_tree_phandles(struct device_node *node,
int phandle_delta)
{
struct device_node *child;
struct property *prop;
phandle phandle;
/* first adjust the node's phandle direct value */
if (node->phandle != 0 && node->phandle != OF_PHANDLE_ILLEGAL)
node->phandle += phandle_delta;
/* now adjust phandle & linux,phandle values */
for_each_property_of_node(node, prop) {
/* only look for these two */
if (of_prop_cmp(prop->name, "phandle") != 0 &&
of_prop_cmp(prop->name, "linux,phandle") != 0)
continue;
/* must be big enough */
if (prop->length < 4)
continue;
/* read phandle value */
phandle = be32_to_cpup(prop->value);
if (phandle == OF_PHANDLE_ILLEGAL) /* unresolved */
continue;
/* adjust */
*(uint32_t *)prop->value = cpu_to_be32(node->phandle);
}
/* now do the children recursively */
for_each_child_of_node(node, child)
__of_adjust_tree_phandles(child, phandle_delta);
}
static int __of_adjust_phandle_ref(struct device_node *node, struct property *rprop, int value, bool is_delta)
{
phandle phandle;
struct device_node *refnode;
struct property *sprop;
char *propval, *propcur, *propend, *nodestr, *propstr, *s;
int offset, propcurlen;
int err = 0;
/* make a copy */
propval = kmalloc(rprop->length, GFP_KERNEL);
if (!propval) {
pr_err("%s: Could not copy value of '%s'\n",
__func__, rprop->name);
return -ENOMEM;
}
memcpy(propval, rprop->value, rprop->length);
propend = propval + rprop->length;
for (propcur = propval; propcur < propend; propcur += propcurlen + 1) {
propcurlen = strlen(propcur);
nodestr = propcur;
s = strchr(propcur, ':');
if (!s) {
pr_err("%s: Illegal symbol entry '%s' (1)\n",
__func__, propcur);
err = -EINVAL;
goto err_fail;
}
*s++ = '\0';
propstr = s;
s = strchr(s, ':');
if (!s) {
pr_err("%s: Illegal symbol entry '%s' (2)\n",
__func__, (char *)rprop->value);
err = -EINVAL;
goto err_fail;
}
*s++ = '\0';
err = kstrtoint(s, 10, &offset);
if (err != 0) {
pr_err("%s: Could get offset '%s'\n",
__func__, (char *)rprop->value);
goto err_fail;
}
/* look into the resolve node for the full path */
refnode = __of_find_node_by_full_name(node, nodestr);
if (!refnode) {
pr_warn("%s: Could not find refnode '%s'\n",
__func__, (char *)rprop->value);
continue;
}
/* now find the property */
for_each_property_of_node(refnode, sprop) {
if (of_prop_cmp(sprop->name, propstr) == 0)
break;
}
if (!sprop) {
pr_err("%s: Could not find property '%s'\n",
__func__, (char *)rprop->value);
err = -ENOENT;
goto err_fail;
}
phandle = is_delta ? be32_to_cpup(sprop->value + offset) + value : value;
*(__be32 *)(sprop->value + offset) = cpu_to_be32(phandle);
}
err_fail:
kfree(propval);
return err;
}
/*
* Adjust the local phandle references by the given phandle delta.
* Assumes the existances of a __local_fixups__ node at the root
* of the tree. Does not take any devtree locks so make sure you
* call this on a tree which is at the detached state.
*/
static int __of_adjust_tree_phandle_references(struct device_node *node,
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;
/* no local fixups */
if (!child)
return 0;
/* find the local fixups property */
for_each_property_of_node(child, rprop) {
/* skip properties added automatically */
if (of_prop_cmp(rprop->name, "name") == 0)
continue;
err = __of_adjust_phandle_ref(node, rprop, phandle_delta, true);
if (err)
return err;
}
return 0;
}
/**
* of_resolve - Resolve the given node against the live tree.
*
* @resolve: Node to resolve
*
* Perform dynamic Device Tree resolution against the live tree
* to the given node to resolve. This depends on the live tree
* having a __symbols__ node, and the resolve node the __fixups__ &
* __local_fixups__ nodes (if needed).
* The result of the operation is a resolve node that it's contents
* are fit to be inserted or operate upon the live tree.
* Returns 0 on success or a negative error value on error.
*/
int of_resolve_phandles(struct device_node *resolve)
{
struct device_node *child, *refnode;
struct device_node *root_sym, *resolve_sym, *resolve_fix;
struct property *rprop;
const char *refpath;
phandle phandle, phandle_delta;
int err;
/* the resolve node must exist, and be detached */
if (!resolve || !of_node_check_flag(resolve, OF_DETACHED))
return -EINVAL;
/* 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;
root_sym = NULL;
resolve_sym = NULL;
resolve_fix = NULL;
/* this may fail (if no fixups are required) */
root_sym = of_find_node_by_path("/__symbols__");
/* locate the symbols & fixups nodes on resolve */
for_each_child_of_node(resolve, child) {
if (!resolve_sym &&
of_node_cmp(child->name, "__symbols__") == 0)
resolve_sym = child;
if (!resolve_fix &&
of_node_cmp(child->name, "__fixups__") == 0)
resolve_fix = child;
/* both found, don't bother anymore */
if (resolve_sym && resolve_fix)
break;
}
/* we do allow for the case where no fixups are needed */
if (!resolve_fix) {
err = 0; /* no error */
goto out;
}
/* we need to fixup, but no root symbols... */
if (!root_sym) {
err = -EINVAL;
goto out;
}
for_each_property_of_node(resolve_fix, rprop) {
/* skip properties added automatically */
if (of_prop_cmp(rprop->name, "name") == 0)
continue;
err = of_property_read_string(root_sym,
rprop->name, &refpath);
if (err != 0) {
pr_err("%s: Could not find symbol '%s'\n",
__func__, rprop->name);
goto out;
}
refnode = of_find_node_by_path(refpath);
if (!refnode) {
pr_err("%s: Could not find node by path '%s'\n",
__func__, refpath);
err = -ENOENT;
goto out;
}
phandle = refnode->phandle;
of_node_put(refnode);
pr_debug("%s: %s phandle is 0x%08x\n",
__func__, rprop->name, phandle);
err = __of_adjust_phandle_ref(resolve, rprop, phandle, false);
if (err)
break;
}
out:
/* NULL is handled by of_node_put as NOP */
of_node_put(root_sym);
return err;
}
EXPORT_SYMBOL_GPL(of_resolve_phandles);
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/hashtable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_fdt.h> #include <linux/of_fdt.h>
...@@ -24,7 +25,7 @@ static struct selftest_results { ...@@ -24,7 +25,7 @@ static struct selftest_results {
int failed; int failed;
} selftest_results; } selftest_results;
#define NO_OF_NODES 2 #define NO_OF_NODES 3
static struct device_node *nodes[NO_OF_NODES]; static struct device_node *nodes[NO_OF_NODES];
static int last_node_index; static int last_node_index;
static bool selftest_live_tree; static bool selftest_live_tree;
...@@ -145,6 +146,97 @@ static void __init of_selftest_dynamic(void) ...@@ -145,6 +146,97 @@ static void __init of_selftest_dynamic(void)
"Adding a large property should have passed\n"); "Adding a large property should have passed\n");
} }
static int __init of_selftest_check_node_linkage(struct device_node *np)
{
struct device_node *child, *allnext_index = np;
int count = 0, rc;
for_each_child_of_node(np, child) {
if (child->parent != np) {
pr_err("Child node %s links to wrong parent %s\n",
child->name, np->name);
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;
count += rc;
}
return count + 1;
}
static void __init of_selftest_check_tree_linkage(void)
{
struct device_node *np;
int allnode_count = 0, child_count;
if (!of_allnodes)
return;
for_each_of_allnodes(np)
allnode_count++;
child_count = of_selftest_check_node_linkage(of_allnodes);
selftest(child_count > 0, "Device node data structure is corrupted\n");
selftest(child_count == allnode_count, "allnodes list size (%i) doesn't match"
"sibling lists size (%i)\n", allnode_count, child_count);
pr_debug("allnodes list size (%i); sibling lists size (%i)\n", allnode_count, child_count);
}
struct node_hash {
struct hlist_node node;
struct device_node *np;
};
static DEFINE_HASHTABLE(phandle_ht, 8);
static void __init of_selftest_check_phandles(void)
{
struct device_node *np;
struct node_hash *nh;
struct hlist_node *tmp;
int i, dup_count = 0, phandle_count = 0;
for_each_of_allnodes(np) {
if (!np->phandle)
continue;
hash_for_each_possible(phandle_ht, nh, node, np->phandle) {
if (nh->np->phandle == np->phandle) {
pr_info("Duplicate phandle! %i used by %s and %s\n",
np->phandle, nh->np->full_name, np->full_name);
dup_count++;
break;
}
}
nh = kzalloc(sizeof(*nh), GFP_KERNEL);
if (WARN_ON(!nh))
return;
nh->np = np;
hash_add(phandle_ht, &nh->node, np->phandle);
phandle_count++;
}
selftest(dup_count == 0, "Found %i duplicates in %i phandles\n",
dup_count, phandle_count);
/* Clean up */
hash_for_each_safe(phandle_ht, i, tmp, nh, node) {
hash_del(&nh->node);
kfree(nh);
}
}
static void __init of_selftest_parse_phandle_with_args(void) static void __init of_selftest_parse_phandle_with_args(void)
{ {
struct device_node *np; struct device_node *np;
...@@ -637,6 +729,8 @@ static int attach_node_and_children(struct device_node *np) ...@@ -637,6 +729,8 @@ static int attach_node_and_children(struct device_node *np)
dup = np; dup = np;
while (dup) { while (dup) {
if (WARN_ON(last_node_index >= NO_OF_NODES))
return -EINVAL;
nodes[last_node_index++] = dup; nodes[last_node_index++] = dup;
dup = dup->sibling; dup = dup->sibling;
} }
...@@ -670,6 +764,7 @@ static int __init selftest_data_add(void) ...@@ -670,6 +764,7 @@ static int __init selftest_data_add(void)
extern uint8_t __dtb_testcases_begin[]; extern uint8_t __dtb_testcases_begin[];
extern uint8_t __dtb_testcases_end[]; extern uint8_t __dtb_testcases_end[];
const int size = __dtb_testcases_end - __dtb_testcases_begin; const int size = __dtb_testcases_end - __dtb_testcases_begin;
int rc;
if (!size) { if (!size) {
pr_warn("%s: No testcase data to attach; not running tests\n", pr_warn("%s: No testcase data to attach; not running tests\n",
...@@ -690,6 +785,12 @@ static int __init selftest_data_add(void) ...@@ -690,6 +785,12 @@ static int __init selftest_data_add(void)
pr_warn("%s: No tree to attach; not running tests\n", __func__); pr_warn("%s: No tree to attach; not running tests\n", __func__);
return -ENODATA; return -ENODATA;
} }
of_node_set_flag(selftest_data_node, OF_DETACHED);
rc = of_resolve_phandles(selftest_data_node);
if (rc) {
pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc);
return -EINVAL;
}
if (!of_allnodes) { if (!of_allnodes) {
/* enabling flag for removing nodes */ /* enabling flag for removing nodes */
...@@ -717,10 +818,6 @@ static void detach_node_and_children(struct device_node *np) ...@@ -717,10 +818,6 @@ static void detach_node_and_children(struct device_node *np)
{ {
while (np->child) while (np->child)
detach_node_and_children(np->child); detach_node_and_children(np->child);
while (np->sibling)
detach_node_and_children(np->sibling);
of_detach_node(np); of_detach_node(np);
} }
...@@ -749,8 +846,7 @@ static void selftest_data_remove(void) ...@@ -749,8 +846,7 @@ static void selftest_data_remove(void)
if (nodes[last_node_index]) { if (nodes[last_node_index]) {
np = of_find_node_by_path(nodes[last_node_index]->full_name); np = of_find_node_by_path(nodes[last_node_index]->full_name);
if (strcmp(np->full_name, "/aliases") != 0) { if (strcmp(np->full_name, "/aliases") != 0) {
detach_node_and_children(np->child); detach_node_and_children(np);
of_detach_node(np);
} else { } else {
for_each_property_of_node(np, prop) { for_each_property_of_node(np, prop) {
if (strcmp(prop->name, "testcase-alias") == 0) if (strcmp(prop->name, "testcase-alias") == 0)
...@@ -780,6 +876,8 @@ static int __init of_selftest(void) ...@@ -780,6 +876,8 @@ static int __init of_selftest(void)
of_node_put(np); of_node_put(np);
pr_info("start of selftest - you will see error messages\n"); pr_info("start of selftest - you will see error messages\n");
of_selftest_check_tree_linkage();
of_selftest_check_phandles();
of_selftest_find_node_by_name(); of_selftest_find_node_by_name();
of_selftest_dynamic(); of_selftest_dynamic();
of_selftest_parse_phandle_with_args(); of_selftest_parse_phandle_with_args();
...@@ -790,12 +888,16 @@ static int __init of_selftest(void) ...@@ -790,12 +888,16 @@ static int __init of_selftest(void)
of_selftest_parse_interrupts_extended(); of_selftest_parse_interrupts_extended();
of_selftest_match_node(); of_selftest_match_node();
of_selftest_platform_populate(); of_selftest_platform_populate();
pr_info("end of selftest - %i passed, %i failed\n",
selftest_results.passed, selftest_results.failed);
/* removing selftest data from live tree */ /* removing selftest data from live tree */
selftest_data_remove(); selftest_data_remove();
/* Double check linkage after removing testcase data */
of_selftest_check_tree_linkage();
pr_info("end of selftest - %i passed, %i failed\n",
selftest_results.passed, selftest_results.failed);
return 0; return 0;
} }
late_initcall(of_selftest); late_initcall(of_selftest);
...@@ -13,3 +13,38 @@ node-remove { ...@@ -13,3 +13,38 @@ node-remove {
#include "tests-interrupts.dtsi" #include "tests-interrupts.dtsi"
#include "tests-match.dtsi" #include "tests-match.dtsi"
#include "tests-platform.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";
}; };
...@@ -863,4 +863,7 @@ static inline int of_changeset_update_property(struct of_changeset *ocs, ...@@ -863,4 +863,7 @@ static inline int of_changeset_update_property(struct of_changeset *ocs,
} }
#endif #endif
/* CONFIG_OF_RESOLVE api */
extern int of_resolve_phandles(struct device_node *tree);
#endif /* _LINUX_OF_H */ #endif /* _LINUX_OF_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment