Devicetree changes for v3.18

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.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJUOEytAAoJEMWQL496c2LNq9AP/RPbMr7rPLX3flHJbWOeHCF7
 U1TzAZDoBdl7xHFCrsLF5yQlt5rGleJinMxphf8XK9Aui7L18NO4LDqoYGeGOC/u
 hYPcfpLuyRJiBr2xVyt+e0zivPe62P5618wUP4DmqZq7rQ3IYR71bR/g2K4N33VG
 LLD4HmQUCfAUpsF9ruijSShM9ez21oloURSR02xD+yvCfqpXjaysp5XLDJJQLfql
 O2E084QOk0d5LI+buTdmenMzuOAa8TrmDwdEKpbL4maf4Frr7H5QQnQ7xrIkUR0w
 Lu9XxjGhmNG4iLSQcH4lmWpzf6N9nHvfVmjhCZ3UdpYc651v6sb0Lyi8rYWMne2E
 rUoOSpfmUgQ1WlAsFp5R6USUyrJd1Xe0hlqwCwVl97psNLBcZrYmi7YEYWugAAep
 IBHrJk80exBVASErUXr4dgRI257AuHMhIiJxlyaec+mSGJBIzjdjrJbZDtdKVPWw
 liY0PthfFPJUWTjmWiiDK00m3dtpoxnw/ugTAYAKuQGCyXdgcMKNJxwJtpts8kxY
 jDCaNpr1Jf69b0nn1HSlmI40QVgjOnPfNvXGVbQBMxzHorxb1GEiv/uGFavw2bzo
 aEZjxq1/uKMWyvkbJSsGQjGQXuKKwyj5iJ0sSd6U2JfD4Pze+1o+FaWMGo6Bcz7o
 tpbR+vQRBIV2f6pc4PzR
 =VYfI
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2014-10-11 06:47:50 -04:00
commit e98d6e7f76
11 changed files with 590 additions and 24 deletions

View file

@ -11,6 +11,7 @@ config OF_SELFTEST
bool "Device Tree Runtime self tests"
depends on OF_IRQ && OF_EARLY_FLATTREE
select OF_DYNAMIC
select OF_RESOLVE
help
This option builds in test cases for the device tree infrastructure
that are executed once at boot time, and the results dumped to the
@ -79,4 +80,7 @@ config OF_RESERVED_MEM
help
Helpers to allow for reservation of memory regions
config OF_RESOLVE
bool
endmenu # OF

View file

@ -13,6 +13,7 @@ obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
obj-$(CONFIG_OF_RESOLVE) += resolver.o
CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt
CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt

View file

@ -1021,6 +1021,9 @@ struct device_node *of_find_node_by_phandle(phandle handle)
struct device_node *np;
unsigned long flags;
if (!handle)
return NULL;
raw_spin_lock_irqsave(&devtree_lock, flags);
for (np = of_allnodes; np; np = np->allnext)
if (np->phandle == handle)

336
drivers/of/resolver.c Normal file
View file

@ -0,0 +1,336 @@
/*
* 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);

View file

@ -7,6 +7,7 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/hashtable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
@ -24,7 +25,7 @@ static struct selftest_results {
int failed;
} selftest_results;
#define NO_OF_NODES 2
#define NO_OF_NODES 3
static struct device_node *nodes[NO_OF_NODES];
static int last_node_index;
static bool selftest_live_tree;
@ -145,6 +146,97 @@ static void __init of_selftest_dynamic(void)
"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)
{
struct device_node *np;
@ -637,6 +729,8 @@ static int attach_node_and_children(struct device_node *np)
dup = np;
while (dup) {
if (WARN_ON(last_node_index >= NO_OF_NODES))
return -EINVAL;
nodes[last_node_index++] = dup;
dup = dup->sibling;
}
@ -670,6 +764,7 @@ static int __init selftest_data_add(void)
extern uint8_t __dtb_testcases_begin[];
extern uint8_t __dtb_testcases_end[];
const int size = __dtb_testcases_end - __dtb_testcases_begin;
int rc;
if (!size) {
pr_warn("%s: No testcase data to attach; not running tests\n",
@ -690,6 +785,12 @@ static int __init selftest_data_add(void)
pr_warn("%s: No tree to attach; not running tests\n", __func__);
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) {
/* enabling flag for removing nodes */
@ -717,10 +818,6 @@ static void detach_node_and_children(struct device_node *np)
{
while (np->child)
detach_node_and_children(np->child);
while (np->sibling)
detach_node_and_children(np->sibling);
of_detach_node(np);
}
@ -749,8 +846,7 @@ static void selftest_data_remove(void)
if (nodes[last_node_index]) {
np = of_find_node_by_path(nodes[last_node_index]->full_name);
if (strcmp(np->full_name, "/aliases") != 0) {
detach_node_and_children(np->child);
of_detach_node(np);
detach_node_and_children(np);
} else {
for_each_property_of_node(np, prop) {
if (strcmp(prop->name, "testcase-alias") == 0)
@ -780,6 +876,8 @@ static int __init of_selftest(void)
of_node_put(np);
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_dynamic();
of_selftest_parse_phandle_with_args();
@ -790,12 +888,16 @@ static int __init of_selftest(void)
of_selftest_parse_interrupts_extended();
of_selftest_match_node();
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 */
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;
}
late_initcall(of_selftest);

View file

@ -13,3 +13,38 @@
#include "tests-interrupts.dtsi"
#include "tests-match.dtsi"
#include "tests-platform.dtsi"
/*
* phandle fixup data - generated by dtc patches that aren't upstream.
* This data must be regenerated whenever phandle references are modified in
* the testdata tree.
*
* The format of this data may be subject to change. For the time being consider
* this a kernel-internal data format.
*/
/ { __local_fixups__ {
fixup = "/testcase-data/testcase-device2:interrupt-parent:0",
"/testcase-data/testcase-device1:interrupt-parent:0",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:60",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:52",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:44",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:36",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:24",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:8",
"/testcase-data/interrupts/interrupts-extended0:interrupts-extended:0",
"/testcase-data/interrupts/interrupts1:interrupt-parent:0",
"/testcase-data/interrupts/interrupts0:interrupt-parent:0",
"/testcase-data/interrupts/intmap1:interrupt-map:12",
"/testcase-data/interrupts/intmap0:interrupt-map:52",
"/testcase-data/interrupts/intmap0:interrupt-map:36",
"/testcase-data/interrupts/intmap0:interrupt-map:16",
"/testcase-data/interrupts/intmap0:interrupt-map:4",
"/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:12",
"/testcase-data/phandle-tests/consumer-a:phandle-list-bad-args:0",
"/testcase-data/phandle-tests/consumer-a:phandle-list:56",
"/testcase-data/phandle-tests/consumer-a:phandle-list:52",
"/testcase-data/phandle-tests/consumer-a:phandle-list:40",
"/testcase-data/phandle-tests/consumer-a:phandle-list:24",
"/testcase-data/phandle-tests/consumer-a:phandle-list:8",
"/testcase-data/phandle-tests/consumer-a:phandle-list:0";
}; };