diff --git a/drivers/base/property.c b/drivers/base/property.c index 8b91ab380d14..348b37e64944 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -983,6 +983,81 @@ fwnode_graph_get_remote_node(const struct fwnode_handle *fwnode, u32 port_id, } EXPORT_SYMBOL_GPL(fwnode_graph_get_remote_node); +/** + * fwnode_graph_get_endpoint_by_id - get endpoint by port and endpoint numbers + * @fwnode: parent fwnode_handle containing the graph + * @port: identifier of the port node + * @endpoint: identifier of the endpoint node under the port node + * @flags: fwnode lookup flags + * + * Return the fwnode handle of the local endpoint corresponding the port and + * endpoint IDs or NULL if not found. + * + * If FWNODE_GRAPH_ENDPOINT_NEXT is passed in @flags and the specified endpoint + * has not been found, look for the closest endpoint ID greater than the + * specified one and return the endpoint that corresponds to it, if present. + * + * Do not return endpoints that belong to disabled devices, unless + * FWNODE_GRAPH_DEVICE_DISABLED is passed in @flags. + * + * The returned endpoint needs to be released by calling fwnode_handle_put() on + * it when it is not needed any more. + */ +struct fwnode_handle * +fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, + u32 port, u32 endpoint, unsigned long flags) +{ + struct fwnode_handle *ep = NULL, *best_ep = NULL; + unsigned int best_ep_id = 0; + bool endpoint_next = flags & FWNODE_GRAPH_ENDPOINT_NEXT; + bool enabled_only = !(flags & FWNODE_GRAPH_DEVICE_DISABLED); + + while ((ep = fwnode_graph_get_next_endpoint(fwnode, ep))) { + struct fwnode_endpoint fwnode_ep = { 0 }; + int ret; + + if (enabled_only) { + struct fwnode_handle *dev_node; + bool available; + + dev_node = fwnode_graph_get_remote_port_parent(ep); + available = fwnode_device_is_available(dev_node); + fwnode_handle_put(dev_node); + if (!available) + continue; + } + + ret = fwnode_graph_parse_endpoint(ep, &fwnode_ep); + if (ret < 0) + continue; + + if (fwnode_ep.port != port) + continue; + + if (fwnode_ep.id == endpoint) + return ep; + + if (!endpoint_next) + continue; + + /* + * If the endpoint that has just been found is not the first + * matching one and the ID of the one found previously is closer + * to the requested endpoint ID, skip it. + */ + if (fwnode_ep.id < endpoint || + (best_ep && best_ep_id < fwnode_ep.id)) + continue; + + fwnode_handle_put(best_ep); + best_ep = fwnode_handle_get(ep); + best_ep_id = fwnode_ep.id; + } + + return best_ep; +} +EXPORT_SYMBOL_GPL(fwnode_graph_get_endpoint_by_id); + /** * fwnode_graph_parse_endpoint - parse common endpoint node properties * @fwnode: pointer to endpoint fwnode_handle diff --git a/include/linux/property.h b/include/linux/property.h index 65d3420dd5d1..a29369c89e6e 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -13,6 +13,7 @@ #ifndef _LINUX_PROPERTY_H_ #define _LINUX_PROPERTY_H_ +#include #include #include @@ -304,6 +305,23 @@ struct fwnode_handle * fwnode_graph_get_remote_node(const struct fwnode_handle *fwnode, u32 port, u32 endpoint); +/* + * Fwnode lookup flags + * + * @FWNODE_GRAPH_ENDPOINT_NEXT: In the case of no exact match, look for the + * closest endpoint ID greater than the specified + * one. + * @FWNODE_GRAPH_DEVICE_DISABLED: That the device to which the remote + * endpoint of the given endpoint belongs to, + * may be disabled. + */ +#define FWNODE_GRAPH_ENDPOINT_NEXT BIT(0) +#define FWNODE_GRAPH_DEVICE_DISABLED BIT(1) + +struct fwnode_handle * +fwnode_graph_get_endpoint_by_id(const struct fwnode_handle *fwnode, + u32 port, u32 endpoint, unsigned long flags); + #define fwnode_graph_for_each_endpoint(fwnode, child) \ for (child = NULL; \ (child = fwnode_graph_get_next_endpoint(fwnode, child)); )