summaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2013-05-01 17:47:44 +0200
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2013-05-01 17:47:44 +0200
commitbf61c8840efe60fd8f91446860b63338fb424158 (patch)
tree7a71832407a4f0d6346db773343f4c3ae2257b19 /drivers/of
parentInput: wacom - fix "can not retrieve extra class descriptor" for DTH2242 (diff)
parentInput: trackpoint - Optimize trackpoint init to use power-on reset (diff)
downloadlinux-bf61c8840efe60fd8f91446860b63338fb424158.tar.xz
linux-bf61c8840efe60fd8f91446860b63338fb424158.zip
Merge branch 'next' into for-linus
Prepare first set of updates for 3.10 merge window.
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/Kconfig2
-rw-r--r--drivers/of/address.c2
-rw-r--r--drivers/of/base.c565
-rw-r--r--drivers/of/device.c13
-rw-r--r--drivers/of/fdt.c26
-rw-r--r--drivers/of/of_i2c.c2
-rw-r--r--drivers/of/of_mdio.c6
-rw-r--r--drivers/of/of_private.h36
-rw-r--r--drivers/of/pdt.c12
-rw-r--r--drivers/of/platform.c1
-rw-r--r--drivers/of/selftest.c54
11 files changed, 540 insertions, 179 deletions
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index dfba3e64d595..d37bfcf5a3a2 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -53,7 +53,7 @@ config OF_DEVICE
config OF_I2C
def_tristate I2C
- depends on I2C && !SPARC
+ depends on I2C
help
OpenFirmware I2C accessors
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 0125524c08c4..04da786c84d2 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -429,7 +429,7 @@ static u64 __of_translate_address(struct device_node *dev,
goto bail;
bus = of_match_bus(parent);
- /* Cound address cells & copy address locally */
+ /* Count address cells & copy address locally */
bus->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
diff --git a/drivers/of/base.c b/drivers/of/base.c
index af3b22ac7627..321d3ef05006 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -24,37 +24,21 @@
#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];
-};
+#include "of_private.h"
-static LIST_HEAD(aliases_lookup);
+LIST_HEAD(aliases_lookup);
-struct device_node *allnodes;
+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);
+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);
+DEFINE_RAW_SPINLOCK(devtree_lock);
int of_n_addr_cells(struct device_node *np)
{
@@ -163,16 +147,14 @@ void of_node_put(struct device_node *node)
EXPORT_SYMBOL(of_node_put);
#endif /* CONFIG_OF_DYNAMIC */
-struct property *of_find_property(const struct device_node *np,
- const char *name,
- int *lenp)
+static 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)
@@ -180,7 +162,20 @@ struct property *of_find_property(const struct device_node *np,
break;
}
}
- read_unlock(&devtree_lock);
+
+ return pp;
+}
+
+struct property *of_find_property(const struct device_node *np,
+ const char *name,
+ int *lenp)
+{
+ struct property *pp;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ pp = __of_find_property(np, name, lenp);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return pp;
}
@@ -198,13 +193,13 @@ struct device_node *of_find_all_nodes(struct device_node *prev)
{
struct device_node *np;
- read_lock(&devtree_lock);
- np = prev ? prev->allnext : allnodes;
+ raw_spin_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);
+ raw_spin_unlock(&devtree_lock);
return np;
}
EXPORT_SYMBOL(of_find_all_nodes);
@@ -213,8 +208,20 @@ EXPORT_SYMBOL(of_find_all_nodes);
* Find a property with a given name for a given node
* and return the value.
*/
+static 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;
+}
+
+/*
+ * 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)
+ int *lenp)
{
struct property *pp = of_find_property(np, name, lenp);
@@ -225,13 +232,13 @@ 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)
+static 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);
+ cp = __of_get_property(device, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
@@ -244,6 +251,21 @@ int of_device_is_compatible(const struct device_node *device,
return 0;
}
+
+/** 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)
+{
+ unsigned long flags;
+ int res;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ res = __of_device_is_compatible(device, compat);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ return res;
+}
EXPORT_SYMBOL(of_device_is_compatible);
/**
@@ -268,19 +290,19 @@ int of_machine_is_compatible(const char *compat)
EXPORT_SYMBOL(of_machine_is_compatible);
/**
- * of_device_is_available - check if a device is available for use
+ * __of_device_is_available - check if a device is available for use
*
- * @device: Node to check for availability
+ * @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
*/
-int of_device_is_available(const struct device_node *device)
+static int __of_device_is_available(const struct device_node *device)
{
const char *status;
int statlen;
- status = of_get_property(device, "status", &statlen);
+ status = __of_get_property(device, "status", &statlen);
if (status == NULL)
return 1;
@@ -291,6 +313,26 @@ int of_device_is_available(const struct device_node *device)
return 0;
}
+
+/**
+ * of_device_is_available - check if a device is available for use
+ *
+ * @device: Node to check for availability
+ *
+ * Returns 1 if the status property is absent or set to "okay" or "ok",
+ * 0 otherwise
+ */
+int of_device_is_available(const struct device_node *device)
+{
+ unsigned long flags;
+ int res;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ res = __of_device_is_available(device);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ return res;
+
+}
EXPORT_SYMBOL(of_device_is_available);
/**
@@ -303,13 +345,14 @@ EXPORT_SYMBOL(of_device_is_available);
struct device_node *of_get_parent(const struct device_node *node)
{
struct device_node *np;
+ unsigned long flags;
if (!node)
return NULL;
- read_lock(&devtree_lock);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
np = of_node_get(node->parent);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
EXPORT_SYMBOL(of_get_parent);
@@ -328,14 +371,15 @@ EXPORT_SYMBOL(of_get_parent);
struct device_node *of_get_next_parent(struct device_node *node)
{
struct device_node *parent;
+ unsigned long flags;
if (!node)
return NULL;
- read_lock(&devtree_lock);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
parent = of_node_get(node->parent);
of_node_put(node);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return parent;
}
@@ -351,14 +395,15 @@ struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev)
{
struct device_node *next;
+ unsigned long flags;
- read_lock(&devtree_lock);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
next = prev ? prev->sibling : node->child;
for (; next; next = next->sibling)
if (of_node_get(next))
break;
of_node_put(prev);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return next;
}
EXPORT_SYMBOL(of_get_next_child);
@@ -376,16 +421,16 @@ struct device_node *of_get_next_available_child(const struct device_node *node,
{
struct device_node *next;
- read_lock(&devtree_lock);
+ raw_spin_lock(&devtree_lock);
next = prev ? prev->sibling : node->child;
for (; next; next = next->sibling) {
- if (!of_device_is_available(next))
+ if (!__of_device_is_available(next))
continue;
if (of_node_get(next))
break;
}
of_node_put(prev);
- read_unlock(&devtree_lock);
+ raw_spin_unlock(&devtree_lock);
return next;
}
EXPORT_SYMBOL(of_get_next_available_child);
@@ -422,15 +467,16 @@ EXPORT_SYMBOL(of_get_child_by_name);
*/
struct device_node *of_find_node_by_path(const char *path)
{
- struct device_node *np = allnodes;
+ struct device_node *np = of_allnodes;
+ unsigned long flags;
- read_lock(&devtree_lock);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
for (; np; np = np->allnext) {
if (np->full_name && (of_node_cmp(np->full_name, path) == 0)
&& of_node_get(np))
break;
}
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
EXPORT_SYMBOL(of_find_node_by_path);
@@ -450,15 +496,16 @@ struct device_node *of_find_node_by_name(struct device_node *from,
const char *name)
{
struct device_node *np;
+ unsigned long flags;
- read_lock(&devtree_lock);
- np = from ? from->allnext : allnodes;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ np = from ? from->allnext : of_allnodes;
for (; np; np = np->allnext)
if (np->name && (of_node_cmp(np->name, name) == 0)
&& of_node_get(np))
break;
of_node_put(from);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
EXPORT_SYMBOL(of_find_node_by_name);
@@ -479,15 +526,16 @@ struct device_node *of_find_node_by_type(struct device_node *from,
const char *type)
{
struct device_node *np;
+ unsigned long flags;
- read_lock(&devtree_lock);
- np = from ? from->allnext : allnodes;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ np = from ? from->allnext : of_allnodes;
for (; np; np = np->allnext)
if (np->type && (of_node_cmp(np->type, type) == 0)
&& of_node_get(np))
break;
of_node_put(from);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
EXPORT_SYMBOL(of_find_node_by_type);
@@ -510,18 +558,20 @@ struct device_node *of_find_compatible_node(struct device_node *from,
const char *type, const char *compatible)
{
struct device_node *np;
+ unsigned long flags;
- read_lock(&devtree_lock);
- np = from ? from->allnext : allnodes;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ np = from ? from->allnext : of_allnodes;
for (; np; np = np->allnext) {
if (type
&& !(np->type && (of_node_cmp(np->type, type) == 0)))
continue;
- if (of_device_is_compatible(np, compatible) && of_node_get(np))
+ if (__of_device_is_compatible(np, compatible) &&
+ of_node_get(np))
break;
}
of_node_put(from);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
EXPORT_SYMBOL(of_find_compatible_node);
@@ -543,9 +593,10 @@ struct device_node *of_find_node_with_property(struct device_node *from,
{
struct device_node *np;
struct property *pp;
+ unsigned long flags;
- read_lock(&devtree_lock);
- np = from ? from->allnext : allnodes;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ np = from ? from->allnext : of_allnodes;
for (; np; np = np->allnext) {
for (pp = np->properties; pp; pp = pp->next) {
if (of_prop_cmp(pp->name, prop_name) == 0) {
@@ -556,20 +607,14 @@ struct device_node *of_find_node_with_property(struct device_node *from,
}
out:
of_node_put(from);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
EXPORT_SYMBOL(of_find_node_with_property);
-/**
- * of_match_node - Tell if an 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
- *
- * Low level utility function used by device matching.
- */
-const struct of_device_id *of_match_node(const struct of_device_id *matches,
- const struct device_node *node)
+static
+const struct of_device_id *__of_match_node(const struct of_device_id *matches,
+ const struct device_node *node)
{
if (!matches)
return NULL;
@@ -583,44 +628,74 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches,
match &= node->type
&& !strcmp(matches->type, node->type);
if (matches->compatible[0])
- match &= of_device_is_compatible(node,
- matches->compatible);
+ match &= __of_device_is_compatible(node,
+ matches->compatible);
if (match)
return matches;
matches++;
}
return NULL;
}
+
+/**
+ * of_match_node - Tell if an 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
+ *
+ * Low level utility function used by device matching.
+ */
+const struct of_device_id *of_match_node(const struct of_device_id *matches,
+ const struct device_node *node)
+{
+ const struct of_device_id *match;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ match = __of_match_node(matches, node);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ return match;
+}
EXPORT_SYMBOL(of_match_node);
/**
- * of_find_matching_node - Find a node based on an of_device_id match
- * table.
+ * of_find_matching_node_and_match - Find a node based on an of_device_id
+ * match table.
* @from: The node to start searching from or NULL, the node
* you pass will not be searched, only the next one
* will; typically, you pass what the previous call
* returned. of_node_put() will be called on it
* @matches: array of of device match structures to search in
+ * @match Updated to point at the matches entry which matched
*
* Returns a node pointer with refcount incremented, use
* of_node_put() on it when done.
*/
-struct device_node *of_find_matching_node(struct device_node *from,
- const struct of_device_id *matches)
+struct device_node *of_find_matching_node_and_match(struct device_node *from,
+ const struct of_device_id *matches,
+ const struct of_device_id **match)
{
struct device_node *np;
+ const struct of_device_id *m;
+ unsigned long flags;
+
+ if (match)
+ *match = NULL;
- read_lock(&devtree_lock);
- np = from ? from->allnext : allnodes;
+ raw_spin_lock_irqsave(&devtree_lock, flags);
+ np = from ? from->allnext : of_allnodes;
for (; np; np = np->allnext) {
- if (of_match_node(matches, np) && of_node_get(np))
+ m = __of_match_node(matches, np);
+ if (m && of_node_get(np)) {
+ if (match)
+ *match = m;
break;
+ }
}
of_node_put(from);
- read_unlock(&devtree_lock);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
-EXPORT_SYMBOL(of_find_matching_node);
+EXPORT_SYMBOL(of_find_matching_node_and_match);
/**
* of_modalias_node - Lookup appropriate modalias for a device node
@@ -660,23 +735,100 @@ struct device_node *of_find_node_by_phandle(phandle handle)
{
struct device_node *np;
- read_lock(&devtree_lock);
- for (np = allnodes; np; np = np->allnext)
+ raw_spin_lock(&devtree_lock);
+ for (np = of_allnodes; np; np = np->allnext)
if (np->phandle == handle)
break;
of_node_get(np);
- read_unlock(&devtree_lock);
+ raw_spin_unlock(&devtree_lock);
return np;
}
EXPORT_SYMBOL(of_find_node_by_phandle);
/**
+ * of_property_read_u8_array - Find and read an array of u8 from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_value: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 8-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * dts entry of array should be like:
+ * property = /bits/ 8 <0x50 0x60 0x70>;
+ *
+ * The out_value is modified only if a valid u8 value can be decoded.
+ */
+int of_property_read_u8_array(const struct device_node *np,
+ const char *propname, u8 *out_values, size_t sz)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+ const u8 *val;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ if ((sz * sizeof(*out_values)) > prop->length)
+ return -EOVERFLOW;
+
+ val = prop->value;
+ while (sz--)
+ *out_values++ = *val++;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u8_array);
+
+/**
+ * of_property_read_u16_array - Find and read an array of u16 from a property.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_value: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
+ *
+ * Search for a property in a device node and read 16-bit value(s) from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * dts entry of array should be like:
+ * property = /bits/ 16 <0x5000 0x6000 0x7000>;
+ *
+ * The out_value is modified only if a valid u16 value can be decoded.
+ */
+int of_property_read_u16_array(const struct device_node *np,
+ const char *propname, u16 *out_values, size_t sz)
+{
+ struct property *prop = of_find_property(np, propname, NULL);
+ const __be16 *val;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ if ((sz * sizeof(*out_values)) > prop->length)
+ return -EOVERFLOW;
+
+ val = prop->value;
+ while (sz--)
+ *out_values++ = be16_to_cpup(val++);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u16_array);
+
+/**
* of_property_read_u32_array - Find and read an array of 32 bit integers
* from a property.
*
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @out_value: pointer to return value, modified only if return value is 0.
+ * @sz: number of array elements to read
*
* Search for a property in a device node and read 32-bit value(s) from
* it. Returns 0 on success, -EINVAL if the property does not exist,
@@ -893,8 +1045,8 @@ EXPORT_SYMBOL_GPL(of_property_count_strings);
* Returns the device_node pointer with refcount incremented. Use
* of_node_put() on it when done.
*/
-struct device_node *
-of_parse_phandle(struct device_node *np, const char *phandle_name, int index)
+struct device_node *of_parse_phandle(const struct device_node *np,
+ const char *phandle_name, int index)
{
const __be32 *phandle;
int size;
@@ -939,12 +1091,13 @@ EXPORT_SYMBOL(of_parse_phandle);
* To get a device_node of the `node2' node you may call this:
* of_parse_phandle_with_args(node3, "list", "#list-cells", 1, &args);
*/
-int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
- const char *cells_name, int index,
- struct of_phandle_args *out_args)
+static int __of_parse_phandle_with_args(const struct device_node *np,
+ const char *list_name,
+ const char *cells_name, int index,
+ struct of_phandle_args *out_args)
{
const __be32 *list, *list_end;
- int size, cur_index = 0;
+ int rc = 0, size, cur_index = 0;
uint32_t count = 0;
struct device_node *node = NULL;
phandle phandle;
@@ -957,6 +1110,7 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
/* Loop over the phandles until all the requested entry is found */
while (list < list_end) {
+ rc = -EINVAL;
count = 0;
/*
@@ -973,13 +1127,13 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
if (!node) {
pr_err("%s: could not find phandle\n",
np->full_name);
- break;
+ goto err;
}
if (of_property_read_u32(node, cells_name, &count)) {
pr_err("%s: could not get %s for %s\n",
np->full_name, cells_name,
node->full_name);
- break;
+ goto err;
}
/*
@@ -989,7 +1143,7 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
if (list + count > list_end) {
pr_err("%s: arguments longer than property\n",
np->full_name);
- break;
+ goto err;
}
}
@@ -999,9 +1153,10 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
* index matches, then fill the out_args structure and return,
* or return -ENOENT for an empty entry.
*/
+ rc = -ENOENT;
if (cur_index == index) {
if (!phandle)
- return -ENOENT;
+ goto err;
if (out_args) {
int i;
@@ -1012,6 +1167,10 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
for (i = 0; i < count; i++)
out_args->args[i] = be32_to_cpup(list++);
}
+
+ /* Found it! return success */
+ if (node)
+ of_node_put(node);
return 0;
}
@@ -1021,34 +1180,95 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
cur_index++;
}
- /* Loop exited without finding a valid entry; return an error */
+ /*
+ * Unlock node before returning result; will be one of:
+ * -ENOENT : index is for empty phandle
+ * -EINVAL : parsing error on data
+ * [1..n] : Number of phandle (count mode; when index = -1)
+ */
+ rc = index < 0 ? cur_index : -ENOENT;
+ err:
if (node)
of_node_put(node);
- return -EINVAL;
+ return rc;
+}
+
+int of_parse_phandle_with_args(const struct device_node *np, const char *list_name,
+ const char *cells_name, int index,
+ struct of_phandle_args *out_args)
+{
+ if (index < 0)
+ return -EINVAL;
+ return __of_parse_phandle_with_args(np, list_name, cells_name, index, out_args);
}
EXPORT_SYMBOL(of_parse_phandle_with_args);
/**
- * prom_add_property - Add a property to a node
+ * of_count_phandle_with_args() - Find the number of phandles references in a property
+ * @np: pointer to a device tree node containing a list
+ * @list_name: property name that contains a list
+ * @cells_name: property name that specifies phandles' arguments count
+ *
+ * Returns the number of phandle + argument tuples within a property. It
+ * is a typical pattern to encode a list of phandle and variable
+ * arguments into a single property. The number of arguments is encoded
+ * by a property in the phandle-target node. For example, a gpios
+ * property would contain a list of GPIO specifies consisting of a
+ * phandle and 1 or more arguments. The number of arguments are
+ * determined by the #gpio-cells property in the node pointed to by the
+ * phandle.
+ */
+int of_count_phandle_with_args(const struct device_node *np, const char *list_name,
+ const char *cells_name)
+{
+ return __of_parse_phandle_with_args(np, list_name, cells_name, -1, NULL);
+}
+EXPORT_SYMBOL(of_count_phandle_with_args);
+
+#if defined(CONFIG_OF_DYNAMIC)
+static int of_property_notify(int action, struct device_node *np,
+ struct property *prop)
+{
+ struct of_prop_reconfig pr;
+
+ pr.dn = np;
+ pr.prop = prop;
+ return of_reconfig_notify(action, &pr);
+}
+#else
+static int of_property_notify(int action, struct device_node *np,
+ struct property *prop)
+{
+ return 0;
+}
+#endif
+
+/**
+ * of_add_property - Add a property to a node
*/
-int prom_add_property(struct device_node *np, struct property *prop)
+int of_add_property(struct device_node *np, struct property *prop)
{
struct property **next;
unsigned long flags;
+ int rc;
+
+ rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop);
+ if (rc)
+ return rc;
prop->next = NULL;
- write_lock_irqsave(&devtree_lock, flags);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
next = &np->properties;
while (*next) {
if (strcmp(prop->name, (*next)->name) == 0) {
/* duplicate ! don't insert it */
- write_unlock_irqrestore(&devtree_lock, flags);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
return -1;
}
next = &(*next)->next;
}
*next = prop;
- write_unlock_irqrestore(&devtree_lock, flags);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
#ifdef CONFIG_PROC_DEVICETREE
/* try to add to proc as well if it was initialized */
@@ -1060,20 +1280,25 @@ int prom_add_property(struct device_node *np, struct property *prop)
}
/**
- * prom_remove_property - Remove a property from a node.
+ * of_remove_property - Remove a property from a node.
*
* Note that we don't actually remove it, since we have given out
* who-knows-how-many pointers to the data using get-property.
* Instead we just move the property to the "dead properties"
* list, so it won't be found any more.
*/
-int prom_remove_property(struct device_node *np, struct property *prop)
+int of_remove_property(struct device_node *np, struct property *prop)
{
struct property **next;
unsigned long flags;
int found = 0;
+ int rc;
+
+ rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop);
+ if (rc)
+ return rc;
- write_lock_irqsave(&devtree_lock, flags);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
next = &np->properties;
while (*next) {
if (*next == prop) {
@@ -1086,7 +1311,7 @@ int prom_remove_property(struct device_node *np, struct property *prop)
}
next = &(*next)->next;
}
- write_unlock_irqrestore(&devtree_lock, flags);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
if (!found)
return -ENODEV;
@@ -1101,7 +1326,7 @@ int prom_remove_property(struct device_node *np, struct property *prop)
}
/*
- * prom_update_property - Update a property in a node, if the property does
+ * of_update_property - Update a property in a node, if the property does
* not exist, add it.
*
* Note that we don't actually remove it, since we have given out
@@ -1109,21 +1334,24 @@ int prom_remove_property(struct device_node *np, struct property *prop)
* Instead we just move the property to the "dead properties" list,
* and add the new property to the property list
*/
-int prom_update_property(struct device_node *np,
- struct property *newprop)
+int of_update_property(struct device_node *np, struct property *newprop)
{
struct property **next, *oldprop;
unsigned long flags;
- int found = 0;
+ int rc, found = 0;
+
+ rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
+ if (rc)
+ return rc;
if (!newprop->name)
return -EINVAL;
oldprop = of_find_property(np, newprop->name, NULL);
if (!oldprop)
- return prom_add_property(np, newprop);
+ return of_add_property(np, newprop);
- write_lock_irqsave(&devtree_lock, flags);
+ raw_spin_lock_irqsave(&devtree_lock, flags);
next = &np->properties;
while (*next) {
if (*next == oldprop) {
@@ -1137,7 +1365,7 @@ int prom_update_property(struct device_node *np,
}
next = &(*next)->next;
}
- write_unlock_irqrestore(&devtree_lock, flags);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
if (!found)
return -ENODEV;
@@ -1160,20 +1388,87 @@ int prom_update_property(struct device_node *np,
* device tree nodes.
*/
+static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
+
+int of_reconfig_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&of_reconfig_chain, nb);
+}
+EXPORT_SYMBOL_GPL(of_reconfig_notifier_register);
+
+int of_reconfig_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
+}
+EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
+
+int of_reconfig_notify(unsigned long action, void *p)
+{
+ int rc;
+
+ rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
+ return notifier_to_errno(rc);
+}
+
+#ifdef CONFIG_PROC_DEVICETREE
+static void of_add_proc_dt_entry(struct device_node *dn)
+{
+ struct proc_dir_entry *ent;
+
+ ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
+ if (ent)
+ proc_device_tree_add_node(dn, ent);
+}
+#else
+static void of_add_proc_dt_entry(struct device_node *dn)
+{
+ return;
+}
+#endif
+
/**
* of_attach_node - Plug a device node into the tree and global list.
*/
-void of_attach_node(struct device_node *np)
+int of_attach_node(struct device_node *np)
{
unsigned long flags;
+ int rc;
- write_lock_irqsave(&devtree_lock, flags);
+ rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
+ if (rc)
+ return rc;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
np->sibling = np->parent->child;
- np->allnext = allnodes;
+ np->allnext = of_allnodes;
np->parent->child = np;
- allnodes = np;
- write_unlock_irqrestore(&devtree_lock, flags);
+ of_allnodes = np;
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+
+ of_add_proc_dt_entry(np);
+ return 0;
+}
+
+#ifdef CONFIG_PROC_DEVICETREE
+static void of_remove_proc_dt_entry(struct device_node *dn)
+{
+ struct device_node *parent = dn->parent;
+ struct property *prop = dn->properties;
+
+ while (prop) {
+ remove_proc_entry(prop->name, dn->pde);
+ prop = prop->next;
+ }
+
+ if (dn->pde)
+ remove_proc_entry(dn->pde->name, parent->pde);
+}
+#else
+static void of_remove_proc_dt_entry(struct device_node *dn)
+{
+ return;
}
+#endif
/**
* of_detach_node - "Unplug" a node from the device tree.
@@ -1181,22 +1476,35 @@ void of_attach_node(struct device_node *np)
* The caller must hold a reference to the node. The memory associated with
* the node is not freed until its refcount goes to zero.
*/
-void of_detach_node(struct device_node *np)
+int of_detach_node(struct device_node *np)
{
struct device_node *parent;
unsigned long flags;
+ int rc = 0;
+
+ rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
+ if (rc)
+ return rc;
+
+ raw_spin_lock_irqsave(&devtree_lock, flags);
- write_lock_irqsave(&devtree_lock, flags);
+ if (of_node_check_flag(np, OF_DETACHED)) {
+ /* someone already detached it */
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ return rc;
+ }
parent = np->parent;
- if (!parent)
- goto out_unlock;
+ if (!parent) {
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
+ return rc;
+ }
- if (allnodes == np)
- allnodes = np->allnext;
+ if (of_allnodes == np)
+ of_allnodes = np->allnext;
else {
struct device_node *prev;
- for (prev = allnodes;
+ for (prev = of_allnodes;
prev->allnext != np;
prev = prev->allnext)
;
@@ -1215,9 +1523,10 @@ void of_detach_node(struct device_node *np)
}
of_node_set_flag(np, OF_DETACHED);
+ raw_spin_unlock_irqrestore(&devtree_lock, flags);
-out_unlock:
- write_unlock_irqrestore(&devtree_lock, flags);
+ of_remove_proc_dt_entry(np);
+ return rc;
}
#endif /* defined(CONFIG_OF_DYNAMIC) */
diff --git a/drivers/of/device.c b/drivers/of/device.c
index 4c74e4fc5a51..f685e55e0717 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -8,6 +8,7 @@
#include <linux/slab.h>
#include <asm/errno.h>
+#include "of_private.h"
/**
* of_match_device - Tell if a struct device matches an of_device_id list
@@ -131,6 +132,7 @@ ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
void of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
const char *compat;
+ struct alias_prop *app;
int seen = 0, cplen, sl;
if ((!dev) || (!dev->of_node))
@@ -153,6 +155,17 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
seen++;
}
add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen);
+
+ seen = 0;
+ mutex_lock(&of_aliases_mutex);
+ list_for_each_entry(app, &aliases_lookup, link) {
+ if (dev->of_node == app->np) {
+ add_uevent_var(env, "OF_ALIAS_%d=%s", seen,
+ app->alias);
+ seen++;
+ }
+ }
+ mutex_unlock(&of_aliases_mutex);
}
int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 91a375fb6ae6..808be06bb67e 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -186,6 +186,8 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
*/
fpsize = 1;
allocl = 2;
+ l = 1;
+ *pathp = '\0';
} else {
/* account for '/' and path size minus terminal 0
* already in 'l'
@@ -198,10 +200,10 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (allnextpp) {
+ char *fn;
memset(np, 0, sizeof(*np));
- np->full_name = ((char *)np) + sizeof(struct device_node);
+ np->full_name = fn = ((char *)np) + sizeof(*np);
if (new_format) {
- char *fn = np->full_name;
/* rebuild full path for new format */
if (dad && dad->parent) {
strcpy(fn, dad->full_name);
@@ -215,9 +217,9 @@ static unsigned long unflatten_dt_node(struct boot_param_header *blob,
fn += strlen(fn);
}
*(fn++) = '/';
- memcpy(fn, pathp, l);
- } else
- memcpy(np->full_name, pathp, l);
+ }
+ memcpy(fn, pathp, l);
+
prev_pp = &np->properties;
**allnextpp = np;
*allnextpp = &np->allnext;
@@ -459,7 +461,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
do {
u32 tag = be32_to_cpup((__be32 *)p);
- char *pathp;
+ const char *pathp;
p += 4;
if (tag == OF_DT_END_NODE) {
@@ -486,14 +488,8 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
depth++;
pathp = (char *)p;
p = ALIGN(p + strlen(pathp) + 1, 4);
- if ((*pathp) == '/') {
- char *lp, *np;
- for (lp = NULL, np = pathp; *np; np++)
- if ((*np) == '/')
- lp = np+1;
- if (lp != NULL)
- pathp = lp;
- }
+ if (*pathp == '/')
+ pathp = kbasename(pathp);
rc = it(p, pathp, depth, data);
if (rc != 0)
break;
@@ -710,7 +706,7 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
*/
void __init unflatten_device_tree(void)
{
- __unflatten_device_tree(initial_boot_params, &allnodes,
+ __unflatten_device_tree(initial_boot_params, &of_allnodes,
early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
diff --git a/drivers/of/of_i2c.c b/drivers/of/of_i2c.c
index 3550f3bf4f92..b667264222cc 100644
--- a/drivers/of/of_i2c.c
+++ b/drivers/of/of_i2c.c
@@ -29,7 +29,7 @@ void of_i2c_register_devices(struct i2c_adapter *adap)
dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
- for_each_child_of_node(adap->dev.of_node, node) {
+ for_each_available_child_of_node(adap->dev.of_node, node) {
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *addr;
diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c
index 8e6c25f35040..e3a8b22ef9dd 100644
--- a/drivers/of/of_mdio.c
+++ b/drivers/of/of_mdio.c
@@ -53,7 +53,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
return rc;
/* Loop over the child nodes and register a phy_device for each one */
- for_each_child_of_node(np, child) {
+ for_each_available_child_of_node(np, child) {
const __be32 *paddr;
u32 addr;
int len;
@@ -157,7 +157,7 @@ struct phy_device *of_phy_connect(struct net_device *dev,
if (!phy)
return NULL;
- return phy_connect_direct(dev, phy, hndlr, flags, iface) ? NULL : phy;
+ return phy_connect_direct(dev, phy, hndlr, iface) ? NULL : phy;
}
EXPORT_SYMBOL(of_phy_connect);
@@ -194,7 +194,7 @@ struct phy_device *of_phy_connect_fixed_link(struct net_device *dev,
sprintf(bus_id, PHY_ID_FMT, "fixed-0", be32_to_cpu(phy_id[0]));
- phy = phy_connect(dev, bus_id, hndlr, 0, iface);
+ phy = phy_connect(dev, bus_id, hndlr, iface);
return IS_ERR(phy) ? NULL : phy;
}
EXPORT_SYMBOL(of_phy_connect_fixed_link);
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
new file mode 100644
index 000000000000..ff350c8fa7ac
--- /dev/null
+++ b/drivers/of/of_private.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_OF_PRIVATE_H
+#define _LINUX_OF_PRIVATE_H
+/*
+ * Private symbols used by OF support code
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * 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.
+ */
+
+/**
+ * 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];
+};
+
+extern struct mutex of_aliases_mutex;
+extern struct list_head aliases_lookup;
+#endif /* _LINUX_OF_PRIVATE_H */
diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c
index 07cc1d678e4d..37b56fd716e6 100644
--- a/drivers/of/pdt.c
+++ b/drivers/of/pdt.c
@@ -241,15 +241,15 @@ void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops)
BUG_ON(!ops);
of_pdt_prom_ops = ops;
- allnodes = of_pdt_create_node(root_node, NULL);
+ of_allnodes = of_pdt_create_node(root_node, NULL);
#if defined(CONFIG_SPARC)
- allnodes->path_component_name = "";
+ of_allnodes->path_component_name = "";
#endif
- allnodes->full_name = "/";
+ of_allnodes->full_name = "/";
- nextp = &allnodes->allnext;
- allnodes->child = of_pdt_build_tree(allnodes,
- of_pdt_prom_ops->getchild(allnodes->phandle), &nextp);
+ nextp = &of_allnodes->allnext;
+ of_allnodes->child = of_pdt_build_tree(of_allnodes,
+ of_pdt_prom_ops->getchild(of_allnodes->phandle), &nextp);
/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
of_alias_scan(kernel_tree_alloc);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index b80891b43816..e0a6514ab46c 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -436,6 +436,7 @@ EXPORT_SYMBOL(of_platform_bus_probe);
* of_platform_populate() - Populate platform_devices from device tree data
* @root: parent of the first level to probe or NULL for the root of the tree
* @matches: match table, NULL to use the default
+ * @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent to hook devices from, NULL for toplevel
*
* Similar to of_platform_bus_probe(), this function walks the device tree
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c
index f24ffd7088d2..0eb5c38b4e07 100644
--- a/drivers/of/selftest.c
+++ b/drivers/of/selftest.c
@@ -2,7 +2,7 @@
* Self tests for device tree subsystem
*/
-#define pr_fmt(fmt) "### %s(): " fmt, __func__
+#define pr_fmt(fmt) "### dt-test ### " fmt
#include <linux/clk.h>
#include <linux/err.h>
@@ -16,26 +16,30 @@
static bool selftest_passed = true;
#define selftest(result, fmt, ...) { \
- selftest_passed &= (result); \
- if (!(result)) \
+ if (!(result)) { \
pr_err("FAIL %s:%i " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \
+ selftest_passed = false; \
+ } else { \
+ pr_info("pass %s:%i\n", __FILE__, __LINE__); \
+ } \
}
static void __init of_selftest_parse_phandle_with_args(void)
{
struct device_node *np;
struct of_phandle_args args;
- int rc, i;
- bool passed_all = true;
+ int i, rc;
- pr_info("start\n");
np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");
if (!np) {
pr_err("missing testcase data\n");
return;
}
- for (i = 0; i < 7; i++) {
+ rc = of_count_phandle_with_args(np, "phandle-list", "#phandle-cells");
+ selftest(rc == 7, "of_count_phandle_with_args() returned %i, expected 7\n", rc);
+
+ for (i = 0; i < 8; i++) {
bool passed = true;
rc = of_parse_phandle_with_args(np, "phandle-list",
"#phandle-cells", i, &args);
@@ -79,45 +83,47 @@ static void __init of_selftest_parse_phandle_with_args(void)
passed &= (args.args[0] == (i + 1));
break;
case 7:
- passed &= (rc == -EINVAL);
+ passed &= (rc == -ENOENT);
break;
default:
passed = false;
}
- if (!passed) {
- int j;
- pr_err("index %i - data error on node %s rc=%i regs=[",
- i, args.np->full_name, rc);
- for (j = 0; j < args.args_count; j++)
- printk(" %i", args.args[j]);
- printk(" ]\n");
-
- passed_all = false;
- }
+ selftest(passed, "index %i - data error on node %s rc=%i\n",
+ i, args.np->full_name, rc);
}
/* Check for missing list property */
rc = of_parse_phandle_with_args(np, "phandle-list-missing",
"#phandle-cells", 0, &args);
- passed_all &= (rc == -EINVAL);
+ selftest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
+ rc = of_count_phandle_with_args(np, "phandle-list-missing",
+ "#phandle-cells");
+ selftest(rc == -ENOENT, "expected:%i got:%i\n", -ENOENT, rc);
/* Check for missing cells property */
rc = of_parse_phandle_with_args(np, "phandle-list",
"#phandle-cells-missing", 0, &args);
- passed_all &= (rc == -EINVAL);
+ selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+ rc = of_count_phandle_with_args(np, "phandle-list",
+ "#phandle-cells-missing");
+ selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for bad phandle in list */
rc = of_parse_phandle_with_args(np, "phandle-list-bad-phandle",
"#phandle-cells", 0, &args);
- passed_all &= (rc == -EINVAL);
+ selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+ rc = of_count_phandle_with_args(np, "phandle-list-bad-phandle",
+ "#phandle-cells");
+ selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
/* Check for incorrectly formed argument list */
rc = of_parse_phandle_with_args(np, "phandle-list-bad-args",
"#phandle-cells", 1, &args);
- passed_all &= (rc == -EINVAL);
-
- pr_info("end - %s\n", passed_all ? "PASS" : "FAIL");
+ selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
+ rc = of_count_phandle_with_args(np, "phandle-list-bad-args",
+ "#phandle-cells");
+ selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
}
static void __init of_selftest_property_match_string(void)