From: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> This implements the remaining .graph_* callbacks in the fwnode operations vector for the software nodes. That makes the fwnode_graph*() functions available in the drivers also when software nodes are used. The implementation tries to mimic the "OF graph" as much as possible, but there is no support for the "reg" device property. The ports will need to have the index in their name which starts with "port" (for example "port0", "port1", ...) and endpoints will use the index of the software node that is given to them during creation. The port nodes can also be grouped under a specially named "ports" subnode, just like in DT, if necessary. The remote-endpoints are reference properties under the endpoint nodes that are named "remote-endpoint". Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> Co-developed-by: Daniel Scally <djrscally@xxxxxxxxx> Signed-off-by: Daniel Scally <djrscally@xxxxxxxxx> --- changes in v2: - added software_node_device_is_available - altered software_node_get_next_child to get references - altered software_node_get_next_endpoint to release references to ports and avoid passing invalid combinations of swnodes to software_node_get_next_child - altered swnode_graph_find_next_port to release port rather than old drivers/base/swnode.c | 129 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index 010828fc785b..d69034b807e3 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -363,6 +363,11 @@ static void software_node_put(struct fwnode_handle *fwnode) kobject_put(&swnode->kobj); } +static bool software_node_device_is_available(const struct fwnode_handle *fwnode) +{ + return is_software_node(fwnode); +} + static bool software_node_property_present(const struct fwnode_handle *fwnode, const char *propname) { @@ -450,7 +455,7 @@ software_node_get_next_child(const struct fwnode_handle *fwnode, c = list_next_entry(c, entry); else c = list_first_entry(&p->children, struct swnode, entry); - return &c->fwnode; + return software_node_get(&c->fwnode); } static struct fwnode_handle * @@ -536,9 +541,125 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, return 0; } +static struct fwnode_handle * +swnode_graph_find_next_port(const struct fwnode_handle *parent, + struct fwnode_handle *port) +{ + struct fwnode_handle *old = port; + + while ((port = software_node_get_next_child(parent, old))) { + if (!strncmp(to_swnode(port)->node->name, "port", 4)) + return port; + fwnode_handle_put(port); + old = port; + } + + return NULL; +} + +static struct fwnode_handle * +software_node_graph_get_next_endpoint(const struct fwnode_handle *fwnode, + struct fwnode_handle *endpoint) +{ + struct swnode *swnode = to_swnode(fwnode); + struct fwnode_handle *old = endpoint; + struct fwnode_handle *parent_of_old; + struct fwnode_handle *parent; + struct fwnode_handle *port; + + if (!swnode) + return NULL; + + if (endpoint) { + port = software_node_get_parent(endpoint); + parent = software_node_get_parent(port); + } else { + parent = software_node_get_named_child_node(fwnode, "ports"); + if (!parent) + parent = software_node_get(&swnode->fwnode); + + port = swnode_graph_find_next_port(parent, NULL); + } + + for (; port; port = swnode_graph_find_next_port(parent, port)) { + + if (old) { + parent_of_old = software_node_get_parent(old); + + if (parent_of_old != port) + old = NULL; + + fwnode_handle_put(parent_of_old); + } + + endpoint = software_node_get_next_child(port, old); + fwnode_handle_put(old); + if (endpoint) + break; + + fwnode_handle_put(port); + } + + fwnode_handle_put(port); + software_node_put(parent); + + return endpoint; +} + +static struct fwnode_handle * +software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode) +{ + struct swnode *swnode = to_swnode(fwnode); + const struct software_node_ref_args *ref; + const struct property_entry *prop; + + if (!swnode) + return NULL; + + prop = property_entry_get(swnode->node->properties, "remote-endpoint"); + if (!prop || prop->type != DEV_PROP_REF || prop->is_inline) + return NULL; + + ref = prop->pointer; + + return software_node_get(software_node_fwnode(ref[0].node)); +} + +static struct fwnode_handle * +software_node_graph_get_port_parent(struct fwnode_handle *fwnode) +{ + struct swnode *swnode = to_swnode(fwnode); + struct fwnode_handle *parent; + + if (!strcmp(swnode->parent->node->name, "ports")) + parent = &swnode->parent->parent->fwnode; + else + parent = &swnode->parent->fwnode; + + return software_node_get(parent); +} + +static int +software_node_graph_parse_endpoint(const struct fwnode_handle *fwnode, + struct fwnode_endpoint *endpoint) +{ + struct swnode *swnode = to_swnode(fwnode); + int ret; + + ret = kstrtou32(swnode->parent->node->name + 4, 10, &endpoint->port); + if (ret) + return ret; + + endpoint->id = swnode->id; + endpoint->local_fwnode = fwnode; + + return 0; +} + static const struct fwnode_operations software_node_ops = { .get = software_node_get, .put = software_node_put, + .device_is_available = software_node_device_is_available, .property_present = software_node_property_present, .property_read_int_array = software_node_read_int_array, .property_read_string_array = software_node_read_string_array, @@ -547,7 +668,11 @@ static const struct fwnode_operations software_node_ops = { .get_parent = software_node_get_parent, .get_next_child_node = software_node_get_next_child, .get_named_child_node = software_node_get_named_child_node, - .get_reference_args = software_node_get_reference_args + .get_reference_args = software_node_get_reference_args, + .graph_get_next_endpoint = software_node_graph_get_next_endpoint, + .graph_get_remote_endpoint = software_node_graph_get_remote_endpoint, + .graph_get_port_parent = software_node_graph_get_port_parent, + .graph_parse_endpoint = software_node_graph_parse_endpoint, }; /* -------------------------------------------------------------------------- */ -- 2.17.1