/*
* Procedures for creating, accessing and interpreting the device tree.
*
* Paul Mackerras August 1996.
* Copyright (C) 1996-2005 Paul Mackerras.
*
* Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
* {engebret|bergner}@us.ibm.com
*
* Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net
*
* Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and
* Grant Likely.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
/**
* struct alias_prop - Alias property in 'aliases' node
* @link: List node to link the structure in aliases_lookup list
* @alias: Alias property name
* @np: Pointer to device_node that the alias stands for
* @id: Index value from end of alias name
* @stem: Alias string without the index
*
* The structure represents one alias property of 'aliases' node as
* an entry in aliases_lookup list.
*/
struct alias_prop {
struct list_head link;
const char *alias;
struct device_node *np;
int id;
char stem[0];
};
static LIST_HEAD(aliases_lookup);
struct device_node *of_allnodes;
EXPORT_SYMBOL(of_allnodes);
struct device_node *of_chosen;
struct device_node *of_aliases;
static DEFINE_MUTEX(of_aliases_mutex);
/* use when traversing tree through the allnext, child, sibling,
* or parent members of struct device_node.
*/
DEFINE_RWLOCK(devtree_lock);
int of_n_addr_cells(struct device_node *np)
{
const __be32 *ip;
do {
if (np->parent)
np = np->parent;
ip = of_get_property(np, "#address-cells", NULL);
if (ip)
return be32_to_cpup(ip);
} while (np->parent);
/* No #address-cells property for the root node */
return OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
}
EXPORT_SYMBOL(of_n_addr_cells);
int of_n_size_cells(struct device_node *np)
{
const __be32 *ip;
do {
if (np->parent)
np = np->parent;
ip = of_get_property(np, "#size-cells", NULL);
if (ip)
return be32_to_cpup(ip);
} while (np->parent);
/* No #size-cells property for the root node */
return OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
}
EXPORT_SYMBOL(of_n_size_cells);
#if defined(CONFIG_OF_DYNAMIC)
/**
* of_node_get - Increment refcount of a node
* @node: Node to inc refcount, NULL is supported to
* simplify writing of callers
*
* Returns node.
*/
struct device_node *of_node_get(struct device_node *node)
{
if (node)
kref_get(&node->kref);
return node;
}
EXPORT_SYMBOL(of_node_get);
static inline struct device_node *kref_to_device_node(struct kref *kref)
{
return container_of(kref, struct device_node, kref);
}
/**
* of_node_release - release a dynamically allocated node
* @kref: kref element of the node to be released
*
* In of_node_put() this function is passed to kref_put()
* as the destructor.
*/
static void of_node_release(struct kref *kref)
{
struct device_node *node = kref_to_device_node(kref);
struct property *prop = node->properties;
/* We should never be releasing nodes that haven't been detached. */
if (!of_node_check_flag(node, OF_DETACHED)) {
pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
dump_stack();
kref_init(&node->kref);
return;
}
if (!of_node_check_flag(node, OF_DYNAMIC))
return;
while (prop) {
struct property *next = prop->next;
kfree(prop->name);
kfree(prop->value);
kfree(prop);
prop = next;
if (!prop) {
prop = node->deadprops;
node->deadprops = NULL;
}
}
kfree(node->full_name);
kfree(node->data);
kfree(node);
}
/**
* of_node_put - Decrement refcount of a node
* @node: Node to dec refcount, NULL is supported to
* simplify writing of callers
*
*/
void of_node_put(struct device_node *node)
{
if (node)
kref_put(&node->kref, of_node_release);
}
EXPORT_SYMBOL(of_node_put);
#endif /* CONFIG_OF_DYNAMIC */
struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
{
struct property *pp;
if (!np)
return NULL;
read_lock(&devtree_lock);
for (pp = np->properties; pp; pp = pp->next) {
if (of_prop_cmp(pp->name, name) == 0) {
if (lenp)
*lenp = pp->length;
break;
}
}
read_unlock(&devtree_lock);
return pp;
}
EXPORT_SYMBOL(of_find_property);
/**
* of_find_all_nodes - Get next node in global list
* @prev: Previous node or NULL to start iteration
* of_node_put() will be called on it
*
* Returns a node pointer with refcount incremented, use
* of_node_put() on it when done.
*/
struct device_node *of_find_all_nodes(struct device_node *prev)
{
struct device_node *np;
read_lock(&devtree_lock);
np = prev ? prev->allnext : of_allnodes;
for (; np != NULL; np = np->allnext)
if (of_node_get(np))
break;
of_node_put(prev);
read_unlock(&devtree_lock);
return np;
}
EXPORT_SYMBOL(of_find_all_nodes);
/*
* Find a property with a given name for a given node
* and return the value.
*/
const void *of_get_property(const struct device_node *np, const char *name,
int *lenp)
{
struct property *pp = of_find_property(np, name, lenp);
return pp ? pp->value : NULL;
}
EXPORT_SYMBOL(of_get_property);
/** Checks if the given "compat" string matches one of the strings in
* the device's "compatible" property
*/
int of_device_is_compatible(const struct device_node *device,
const char *compat)
{
const char* cp;
int cplen, l;
cp = of_get_property(device, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
return 1;
l = strlen(cp) + 1;
cp += l;
cplen -= l;
}
return 0;
}
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
* compatible property.
*/
int of_machine_is_compatible(const char *compat)
{
struct device_node *root;
int rc = 0;
root = of_find_node_by_path("/");
if (root) {
rc = of_device_is_compatible(root, compat);
of_node_put(root);
}
return rc;
}
EXPORT_SYMBOL(of_machine_is_compatible);
/**
* of_device_is_available - check if a device is available for use
*
* @device: Node to check for av
|