[RFC PATCH 1/3] of: Parse OF graph into graph structure

From: Philipp Zabel
Date: Wed Mar 19 2014 - 11:12:29 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.
The internal graph structure is not exported, the API still uses struct
device_node pointers as handles.
At the same time, of_graph_get_next_endpoint is changed to drop the reference
count of its prev parameter, so it can be used in a for_each_endpoint_of_node
macro, which is added to conveniently iterate over all endpoints of a node.

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

diff --git a/drivers/of/base.c b/drivers/of/base.c
index fd4b9c2..abfc462 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -28,7 +28,70 @@

#include "of_private.h"

+/**
+ * struct of_graph_entity - In-kernel representation of a node with ports
+ *
+ * @of_node: The corresponding device node in the device tree.
+ * @list: List head in the global graph entity list.
+ * @ports: List of ports contained in this entity.
+ *
+ * This is a shortcut directly containing a list of pe-parsed ports, whether
+ * they are contained in a 'ports' subnode, or whether they are direct children
+ * of the device node. While the of_graph_entity usually will correspond to a
+ * struct device with the same of_node, this is not always the case.
+ */
+struct of_graph_entity {
+ struct device_node *of_node;
+ struct list_head list;
+ struct list_head ports;
+};
+
+/**
+ * struct of_graph_port - In-kernel representation of a port node
+ *
+ * @of_node: The corresponding port node in the device tree.
+ * @parent: The in-kernel representation of the parent device_node.
+ * This is either a direct parent of the of_node, or its grandparent,
+ * if the parent is a 'ports' node.
+ * @list: List head in the parent entity 'ports' list.
+ * @endpoints: List of endpoints contained in this port.
+ * @id: Index of the port in the device_node, set by the 'reg' property.
+ */
+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 - In-kernel representation of an endpoint node
+ *
+ * @of_node: The corresponding endpoint node in the device tree.
+ * @parent: The in-kernel port representation of the parent port node.
+ * @list: List head in the parent port 'endpoints' list.
+ * @global_list: List head in the global endpoint list.
+ * @remote_node: Remote endpoint node, which either is pointed to by the local
+ * 'remote-endpoint' phandle property, or which points to the
+ * local endpoint node this way.
+ * @remote_endpoint: Pointer to remote in-kernel endpoint representation after
+ * linking of endpoint nodes.
+ * @id: Index of the endpoint in the port, set by the 'reg' property.
+ */
+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 +2047,32 @@ struct device_node *of_find_next_cache_node(const struct device_node *np)
return NULL;
}

+static struct of_graph_entity *__of_graph_lookup_entity(
+ const struct device_node *node)
+{
+ struct of_graph_entity *entity;
+
+ list_for_each_entry(entity, &entity_list, list) {
+ if (entity->of_node == node)
+ return entity;
+ }
+
+ 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 +2083,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;
}
@@ -2017,75 +2101,63 @@ EXPORT_SYMBOL(of_graph_parse_endpoint);

/**
* of_graph_get_next_endpoint() - get next endpoint node
+ *
* @parent: pointer to the parent device node
* @prev: previous endpoint node, or NULL to get first
*
- * Return: An 'endpoint' node pointer with refcount incremented. Refcount
- * of the passed @prev node is not decremented, the caller have to use
- * of_node_put() on it when done.
+ * Return: An 'endpoint' node pointer with refcount incremented. The caller
+ * has to use of_node_put() on it when done.
*/
struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
- struct device_node *prev)
+ struct device_node *prev)
{
- struct device_node *endpoint;
- struct device_node *port = NULL;
+ struct of_graph_entity *entity;
+ struct of_graph_port *port;
+ struct of_graph_endpoint *endpoint = NULL;

if (!parent)
return NULL;

if (!prev) {
- struct device_node *node;
- /*
- * It's the first call, we have to find a port subnode
- * within this node or within an optional 'ports' node.
- */
- node = of_get_child_by_name(parent, "ports");
- if (node)
- parent = node;
+ /* It's the first call, we have to find the first port. */
+ entity = __of_graph_lookup_entity(parent);
+ if (WARN_ONCE(!entity || list_empty(&entity->ports),
+ "%s(): no ports specified for %s\n",
+ __func__, parent->full_name))
+ return NULL;

- port = of_get_child_by_name(parent, "port");
+ port = list_first_entry(&entity->ports,
+ struct of_graph_port, list);
+ } else {
+ endpoint = __of_graph_lookup_endpoint(prev);
+ of_node_put(prev);
+ if (WARN_ONCE(!endpoint, "%s(): endpoint %s not in global list\n",
+ __func__, prev->full_name))
+ return NULL;
+ port = endpoint->parent;
+ }

- if (port) {
- /* Found a port, get an endpoint. */
- endpoint = of_get_next_child(port, NULL);
- of_node_put(port);
+ entity = port->parent;
+ list_for_each_entry_from(port, &entity->ports, list) {
+ if (!endpoint) {
+ endpoint = list_first_entry(&port->endpoints,
+ struct of_graph_endpoint, list);
+ } else if (endpoint != list_last_entry(&port->endpoints,
+ struct of_graph_endpoint, list)) {
+ endpoint = list_next_entry(endpoint, list);
} else {
+ /*
+ * The previous endpoint is the port's last one,
+ * continue with the next port.
+ */
endpoint = NULL;
+ continue;
}

- if (!endpoint)
- pr_err("%s(): no endpoint nodes specified for %s\n",
- __func__, parent->full_name);
- of_node_put(node);
-
- return endpoint;
+ return of_node_get(endpoint->of_node);
}

- port = of_get_parent(prev);
- if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
- __func__, prev->full_name))
- return NULL;
-
- /* Avoid dropping prev node refcount to 0. */
- of_node_get(prev);
- endpoint = of_get_next_child(port, prev);
- if (endpoint) {
- of_node_put(port);
- return endpoint;
- }
-
- /* No more endpoints under this port, try the next one. */
- do {
- port = of_get_next_child(parent, port);
- if (!port)
- return NULL;
- } while (of_node_cmp(port->name, "port"));
-
- /* Pick up the first endpoint in this port. */
- endpoint = of_get_next_child(port, NULL);
- of_node_put(port);
-
- return endpoint;
+ return NULL;
}
EXPORT_SYMBOL(of_graph_get_next_endpoint);

@@ -2099,19 +2171,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 == 2 && of_node_cmp(np->name, "ports"))
- break;
- }
- return np;
+ return port->parent->of_node;
}
EXPORT_SYMBOL(of_graph_get_remote_port_parent);

@@ -2124,12 +2193,190 @@ 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;
- return of_get_next_parent(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;
+}
+
+/**
+ * of_graph_entity() - parse all ports contained in a node
+ *
+ * @node: The parent node containing the ports.
+ * @child: Either the 'ports' node or the first 'port' node contained in node.
+ * @ports_node: If true, child is a 'ports' node, otherwise it is the first
+ * 'port'.
+ *
+ * This function is given either the 'ports' node or the first 'port' as
+ * second parameter so the parsing that was already done in of_graph_recurse
+ * does not have to be repeated. It parses all ports and collects them in an
+ * of_graph_entity structure that is added to the global entity_list.
+ *
+ * Returns zero on success, an error value otherwise.
+ */
+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) {
+ /*
+ * Find the first 'ports' or 'port' subnode. If one is found,
+ * parse all ports contained in the node
+ */
+ if ((of_node_cmp(child->name, "ports") == 0) &&
+ (!of_find_property(child, "compatible", NULL))) {
+ 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;
+}
+
+/**
+ * of_graph_populate() - populates the of graph from the device tree
+ *
+ * @root: Root node of the device tree.
+ *
+ * This function scans the device tree for port and endpoint nodes and
+ * generates the of graph from that, linking together endpoints that point
+ * at each other.
+ *
+ * Returns zero on success, an error value otherwise.
+ */
+int of_graph_populate(struct device_node *root)
+{
+ struct of_graph_endpoint *ep1, *ep2;
+ 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 befef42..b6dd55b 100644
--- a/include/linux/of_graph.h
+++ b/include/linux/of_graph.h
@@ -26,6 +26,10 @@ struct of_endpoint {
const struct device_node *local_node;
};

+#define for_each_endpoint_of_node(dn, ep) \
+ for (ep = of_graph_get_next_endpoint(dn, NULL); ep != NULL; \
+ ep = of_graph_get_next_endpoint(dn, ep))
+
#ifdef CONFIG_OF
int of_graph_parse_endpoint(const struct device_node *node,
struct of_endpoint *endpoint);
@@ -34,6 +38,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,
@@ -61,6 +68,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

--
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/