of: Make of_find_node_by_path() handle /aliases
authorGrant Likely <grant.likely@linaro.org>
Fri, 14 Mar 2014 17:07:12 +0000 (17:07 +0000)
committerGrant Likely <grant.likely@linaro.org>
Fri, 23 May 2014 02:35:06 +0000 (11:35 +0900)
Make of_find_node_by_path() handle aliases as prefixes. To make this
work the name search is refactored to search by path component instead
of by full string. This should be a more efficient search, and it makes
it possible to start a search at a subnode of a tree.

Signed-off-by: David Daney <david.daney@cavium.com>
Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
[grant.likely: Rework to not require allocating at runtime]
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Grant Likely <grant.likely@linaro.org>
drivers/of/base.c

index 9df50c74162c41869a17320c2fdf03758854d377..e67b308819c903c1dd04206d3d71c4d5ccffe1f4 100644 (file)
@@ -783,23 +783,78 @@ struct device_node *of_get_child_by_name(const struct device_node *node,
 }
 EXPORT_SYMBOL(of_get_child_by_name);
 
+static struct device_node *__of_find_node_by_path(struct device_node *parent,
+                                               const char *path)
+{
+       struct device_node *child;
+       int len = strchrnul(path, '/') - path;
+
+       if (!len)
+               return NULL;
+
+       __for_each_child_of_node(parent, child) {
+               const char *name = strrchr(child->full_name, '/');
+               if (WARN(!name, "malformed device_node %s\n", child->full_name))
+                       continue;
+               name++;
+               if (strncmp(path, name, len) == 0 && (strlen(name) == len))
+                       return child;
+       }
+       return NULL;
+}
+
 /**
  *     of_find_node_by_path - Find a node matching a full OF path
- *     @path:  The full path to match
+ *     @path: Either the full path to match, or if the path does not
+ *            start with '/', the name of a property of the /aliases
+ *            node (an alias).  In the case of an alias, the node
+ *            matching the alias' value will be returned.
+ *
+ *     Valid paths:
+ *             /foo/bar        Full path
+ *             foo             Valid alias
+ *             foo/bar         Valid alias + relative path
  *
  *     Returns a node pointer with refcount incremented, use
  *     of_node_put() on it when done.
  */
 struct device_node *of_find_node_by_path(const char *path)
 {
-       struct device_node *np = of_allnodes;
+       struct device_node *np = NULL;
+       struct property *pp;
        unsigned long flags;
 
+       if (strcmp(path, "/") == 0)
+               return of_node_get(of_allnodes);
+
+       /* The path could begin with an alias */
+       if (*path != '/') {
+               char *p = strchrnul(path, '/');
+               int len = p - path;
+
+               /* of_aliases must not be NULL */
+               if (!of_aliases)
+                       return NULL;
+
+               for_each_property_of_node(of_aliases, pp) {
+                       if (strlen(pp->name) == len && !strncmp(pp->name, path, len)) {
+                               np = of_find_node_by_path(pp->value);
+                               break;
+                       }
+               }
+               if (!np)
+                       return NULL;
+               path = p;
+       }
+
+       /* Step down the tree matching path components */
        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;
+       if (!np)
+               np = of_node_get(of_allnodes);
+       while (np && *path == '/') {
+               path++; /* Increment past '/' delimiter */
+               np = __of_find_node_by_path(np, path);
+               path = strchrnul(path, '/');
        }
        raw_spin_unlock_irqrestore(&devtree_lock, flags);
        return np;