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 devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html