[PATCH] of: Implement of_graph_register_endpoint

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


This patch adds a function that lets drivers register their endpoints in a
global list. Newly registered endpoints are compared against known endpoints
to check if a connection should be made. If so, the driver is notified via
a simple callback.

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

diff --git a/drivers/of/base.c b/drivers/of/base.c
index ebb001a..77ae54a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -29,6 +29,7 @@
#include "of_private.h"

LIST_HEAD(aliases_lookup);
+LIST_HEAD(endpoint_list);

struct device_node *of_allnodes;
EXPORT_SYMBOL(of_allnodes);
@@ -2002,6 +2003,7 @@ int of_graph_parse_endpoint(const struct device_node *node,
memset(endpoint, 0, sizeof(*endpoint));

endpoint->local_node = node;
+ endpoint->remote_node = of_parse_phandle(node, "remote-endpoint", 0);
/*
* It doesn't matter whether the two calls below succeed.
* If they don't then the default value 0 is used.
@@ -2126,6 +2128,19 @@ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
}
EXPORT_SYMBOL(of_graph_get_next_endpoint);

+static struct of_endpoint *__of_graph_lookup_endpoint(
+ const struct device_node *node)
+{
+ struct of_endpoint *ep;
+
+ list_for_each_entry(ep, &endpoint_list, list) {
+ if (ep->local_node == node)
+ return ep;
+ }
+
+ return NULL;
+}
+
/**
* of_graph_get_remote_port_parent() - get remote port's parent node
* @node: pointer to a local endpoint device_node
@@ -2136,11 +2151,15 @@ EXPORT_SYMBOL(of_graph_get_next_endpoint);
struct device_node *of_graph_get_remote_port_parent(
const struct device_node *node)
{
+ struct of_endpoint *ep;
struct device_node *np;
unsigned int depth;

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

/* Walk 3 levels up only if there is 'ports' node */
for (depth = 3; depth && np; depth--) {
@@ -2163,13 +2182,14 @@ EXPORT_SYMBOL(of_graph_get_remote_port_parent);
*/
struct device_node *of_graph_get_remote_port(const struct device_node *node)
{
+ struct of_endpoint *ep;
struct device_node *np;

/* Get remote endpoint node. */
- np = of_parse_phandle(node, "remote-endpoint", 0);
- if (!np)
+ ep = __of_graph_lookup_endpoint(node);
+ if (!ep || !ep->remote_node)
return NULL;
- np = of_get_next_parent(np);
+ np = of_get_next_parent(ep->remote_node);
if (of_node_cmp(np->name, "port")) {
of_node_put(np);
return NULL;
@@ -2177,3 +2197,44 @@ struct device_node *of_graph_get_remote_port(const struct device_node *node)
return np;
}
EXPORT_SYMBOL(of_graph_get_remote_port);
+
+int of_graph_register_endpoint(const struct device_node *node,
+ void (*cb)(struct of_endpoint *ep, void *data), void *data)
+{
+ struct of_endpoint *remote_ep, *ep = kmalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ of_graph_parse_endpoint(node, ep);
+ ep->callback = cb;
+ ep->data = data;
+
+ list_add(&ep->list, &endpoint_list);
+
+ list_for_each_entry(remote_ep, &endpoint_list, list) {
+ struct of_endpoint *from, *to;
+ if (ep->remote_node) {
+ from = ep;
+ to = remote_ep;
+ } else {
+ from = remote_ep;
+ to = ep;
+ }
+ if (from->remote_node &&
+ from->remote_node == to->local_node) {
+ WARN_ON(to->remote_node &&
+ to->remote_node != from->local_node);
+ to->remote_node = from->local_node;
+ to->remote_ep = from;
+ from->remote_ep = to;
+ if (from->callback)
+ from->callback(from, from->data);
+ if (to->callback)
+ to->callback(to, to->data);
+ return 0;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(of_graph_register_endpoint);
diff --git a/include/linux/of_graph.h b/include/linux/of_graph.h
index 3a3c5a9..f00ac4e 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,10 @@ 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_register_endpoint(const struct device_node *ep_node,
+ void (*cb)(struct of_endpoint *ep, void *data),
+ void *data);
#else

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

+static inline int of_graph_register_endpoint(const struct device_node *ep_node,
+ void (*cb)(struct of_endpoint *ep, void *data),
+ void *data);
+{
+ 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/