[PATCH] of: Parse OF graph into graph structure

From: Philipp Zabel
Date: Tue Mar 11 2014 - 10:56:18 EST


This patch adds a function of_graph_populate() that parses the complete
device tree for port and endpoint nodes and stores them in a graph
structure.

Signed-off-by: Philipp Zabel <p.zabel@xxxxxxxxxxxxxx>
---
drivers/of/base.c | 243
++++++++++++++++++++++++++++++++++++++++-------
include/linux/of_graph.h | 17 +++-
2 files changed, 224 insertions(+), 36 deletions(-)

diff --git a/drivers/of/base.c b/drivers/of/base.c
index ebb001a..a844ca2 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -28,7 +28,33 @@

#include "of_private.h"

+struct of_graph_entity {
+ struct device_node *of_node;
+ struct list_head list;
+ struct list_head ports;
+};
+
+struct of_graph_port {
+ struct device_node *of_node;
+ struct of_graph_entity *parent;
+ struct list_head list;
+ struct list_head endpoints;
+ int id;
+};
+
+struct of_graph_endpoint {
+ struct device_node *of_node;
+ struct of_graph_port *parent;
+ struct list_head list;
+ struct list_head global_list;
+ struct device_node *remote_node;
+ struct of_graph_endpoint *remote_endpoint;
+ int id;
+};
+
LIST_HEAD(aliases_lookup);
+LIST_HEAD(entity_list);
+LIST_HEAD(endpoint_list);

struct device_node *of_allnodes;
EXPORT_SYMBOL(of_allnodes);
@@ -1984,6 +2010,19 @@ struct device_node *of_find_next_cache_node(const
struct device_node *np)
return NULL;
}

+static struct of_graph_endpoint *__of_graph_lookup_endpoint(
+ const struct device_node *node)
+{
+ struct of_graph_endpoint *endpoint;
+
+ list_for_each_entry(endpoint, &endpoint_list, global_list) {
+ if (endpoint->of_node == node)
+ return endpoint;
+ }
+
+ return NULL;
+}
+
/**
* of_graph_parse_endpoint() - parse common endpoint node properties
* @node: pointer to endpoint device_node
@@ -1994,22 +2033,17 @@ struct device_node
*of_find_next_cache_node(const struct device_node *np)
int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint)
{
- struct device_node *port_node = of_get_parent(node);
-
- WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
- __func__, node->full_name);
+ struct of_graph_endpoint *ep;

memset(endpoint, 0, sizeof(*endpoint));

- endpoint->local_node = node;
- /*
- * It doesn't matter whether the two calls below succeed.
- * If they don't then the default value 0 is used.
- */
- of_property_read_u32(port_node, "reg", &endpoint->port);
- of_property_read_u32(node, "reg", &endpoint->id);
+ ep = __of_graph_lookup_endpoint(node);
+ if (!ep || !ep->parent)
+ return -EINVAL;

- of_node_put(port_node);
+ endpoint->local_node = ep->of_node;
+ endpoint->port = ep->parent->id;
+ endpoint->id = ep->id;

return 0;
}
@@ -2136,21 +2170,16 @@ EXPORT_SYMBOL(of_graph_get_next_endpoint);
struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node)
{
- struct device_node *np;
- unsigned int depth;
+ struct of_graph_port *port;
+ struct of_graph_endpoint *ep = __of_graph_lookup_endpoint(node);
+ if (!ep || !ep->remote_endpoint)
+ return NULL;

- /* Get remote endpoint node. */
- np = of_parse_phandle(node, "remote-endpoint", 0);
+ port = ep->remote_endpoint->parent;
+ if (!port || !port->parent)
+ return NULL;

- /* Walk 3 levels up only if there is 'ports' node */
- for (depth = 3; depth && np; depth--) {
- np = of_get_next_parent(np);
- if (depth == 3 && of_node_cmp(np->name, "port"))
- break;
- if (depth == 2 && of_node_cmp(np->name, "ports"))
- break;
- }
- return np;
+ return port->parent->of_node;
}
EXPORT_SYMBOL(of_graph_get_remote_port_parent);

@@ -2163,17 +2192,161 @@ EXPORT_SYMBOL(of_graph_get_remote_port_parent);
*/
struct device_node *of_graph_get_remote_port(const struct device_node
*node)
{
- struct device_node *np;
+ struct of_graph_endpoint *ep;

- /* Get remote endpoint node. */
- np = of_parse_phandle(node, "remote-endpoint", 0);
- if (!np)
+ ep = __of_graph_lookup_endpoint(node);
+ if (!ep || !ep->remote_endpoint || !ep->remote_endpoint->parent)
return NULL;
- np = of_get_next_parent(np);
- if (of_node_cmp(np->name, "port")) {
- of_node_put(np);
- return NULL;
- }
- return np;
+
+ return ep->remote_endpoint->parent->of_node;
}
EXPORT_SYMBOL(of_graph_get_remote_port);
+
+static int of_graph_add_endpoint(struct of_graph_port *port,
+ struct device_node *node)
+{
+ struct of_graph_endpoint *ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ ep->of_node = node;
+ ep->parent = port;
+ of_property_read_u32(node, "reg", &ep->id);
+ ep->remote_node = of_parse_phandle(node, "remote-endpoint", 0);
+
+ list_add_tail(&ep->global_list, &endpoint_list);
+ list_add_tail(&ep->list, &port->endpoints);
+ return 0;
+}
+
+static int of_graph_add_port(struct of_graph_entity *entity,
+ struct device_node *node)
+{
+ struct device_node *child;
+ struct of_graph_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->of_node = node;
+ port->parent = entity;
+ of_property_read_u32(node, "reg", &port->id);
+ INIT_LIST_HEAD(&port->endpoints);
+
+ for_each_child_of_node(node, child) {
+ int rc;
+ if (of_node_cmp(child->name, "endpoint") != 0) {
+ pr_warn("%s(): non-endpoint node inside port %s\n",
+ __func__, node->full_name);
+ continue;
+ }
+
+ rc = of_graph_add_endpoint(port, child);
+ if (rc)
+ return rc;
+ }
+
+ list_add_tail(&port->list, &entity->ports);
+ return 0;
+}
+
+static int of_graph_add_entity(struct device_node *node,
+ struct device_node *child, bool ports_node)
+{
+ struct device_node *parent = node;
+ struct of_graph_entity *entity;
+ int rc = 0;
+
+ entity = kzalloc(sizeof(*entity), GFP_KERNEL);
+ if (!entity)
+ return -ENOMEM;
+
+ entity->of_node = node;
+ INIT_LIST_HEAD(&entity->ports);
+
+ if (ports_node) {
+ parent = child;
+ child = of_get_next_child(parent, NULL);
+ }
+ while (child) {
+ rc = of_graph_add_port(entity, child);
+ if (rc)
+ return rc;
+
+ do {
+ child = of_get_next_child(parent, child);
+ if (!child)
+ break;
+ } while (of_node_cmp(child->name, "port"));
+ }
+
+ list_add_tail(&entity->list, &entity_list);
+ return 0;
+}
+
+static int of_graph_recurse(struct device_node *node)
+{
+ struct device_node *child;
+ int rc = 0;
+
+ for_each_child_of_node(node, child) {
+ if (of_node_cmp(child->name, "ports") == 0) {
+ rc = of_graph_add_entity(node, child, true);
+ break;
+ } else if ((of_node_cmp(child->name, "port") == 0) &&
+ (!of_find_property(child, "compatible", NULL))) {
+ rc = of_graph_add_entity(node, child, false);
+ break;
+ } else {
+ rc = of_graph_recurse(child);
+ }
+ }
+
+ return rc;
+}
+
+int of_graph_populate(struct device_node *root)
+{
+ struct of_graph_endpoint *ep1, *ep2;
+ struct of_graph_entity *entity;
+ struct of_graph_port *port;
+ int rc = 0;
+
+ /* Skip if the graph is already populated */
+ if (!list_empty(&endpoint_list))
+ return 0;
+
+ root = root ? of_node_get(root) : of_find_node_by_path("/");
+
+ /* Parse device tree */
+ rc = of_graph_recurse(root);
+ if (rc)
+ return rc;
+
+ /* Connect endpoints */
+ list_for_each_entry(ep1, &endpoint_list, global_list) {
+ ep2 = ep1;
+ list_for_each_entry_continue(ep2, &endpoint_list, global_list) {
+ struct of_graph_endpoint *from, *to;
+
+ if (ep1->remote_node) {
+ from = ep1;
+ to = ep2;
+ } else {
+ from = ep2;
+ to = ep1;
+ }
+ if (from->remote_node &&
+ from->remote_node == to->of_node) {
+ WARN_ON(to->remote_node &&
+ to->remote_node != from->of_node);
+ to->remote_node = from->of_node;
+ to->remote_endpoint = from;
+ from->remote_endpoint = to;
+ }
+ }
+ }
+
+ of_node_put(root);
+ return rc;
+}
+EXPORT_SYMBOL(of_graph_populate);
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h
index 3a3c5a9..70f5809 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -23,7 +23,14 @@
struct of_endpoint {
unsigned int port;
unsigned int id;
- const struct device_node *local_node;
+ struct device_node *local_node;
+ struct device_node *remote_node;
+ struct of_endpoint *remote_ep;
+
+ /* Internal use only */
+ struct list_head list;
+ void (*callback)(struct of_endpoint *ep, void *data);
+ void *data;
};

#ifdef CONFIG_OF
@@ -35,6 +42,9 @@ struct device_node *of_graph_get_next_endpoint(const
struct device_node *parent,
struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node);
struct device_node *of_graph_get_remote_port(const struct device_node
*node);
+
+int of_graph_populate(struct device_node *root);
+
#else

static inline int of_graph_parse_endpoint(const struct device_node
*node,
@@ -68,6 +78,11 @@ static inline struct device_node
*of_graph_get_remote_port(
return NULL;
}

+static inline int of_graph_populate(struct device_node *root)
+{
+ return -ENOSYS;
+}
+
#endif /* CONFIG_OF */

#endif /* __LINUX_OF_GRAPH_H */
--
1.9.0

regards
Philipp

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/