diff options
Diffstat (limited to 'drivers/of/base.c')
-rw-r--r-- | drivers/of/base.c | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index cd1ce7ab8517..4270eb4a26a1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -458,3 +458,112 @@ int of_modalias_node(struct device_node *node, char *modalias, int len) } EXPORT_SYMBOL_GPL(of_modalias_node); +/** + * of_parse_phandles_with_args - Find a node pointed by phandle in a list + * @np: pointer to a device tree node containing a list + * @list_name: property name that contains a list + * @cells_name: property name that specifies phandles' arguments count + * @index: index of a phandle to parse out + * @out_node: pointer to device_node struct pointer (will be filled) + * @out_args: pointer to arguments pointer (will be filled) + * + * This function is useful to parse lists of phandles and their arguments. + * Returns 0 on success and fills out_node and out_args, on error returns + * appropriate errno value. + * + * Example: + * + * phandle1: node1 { + * #list-cells = <2>; + * } + * + * phandle2: node2 { + * #list-cells = <1>; + * } + * + * node3 { + * list = <&phandle1 1 2 &phandle2 3>; + * } + * + * To get a device_node of the `node2' node you may call this: + * of_parse_phandles_with_args(node3, "list", "#list-cells", 2, &node2, &args); + */ +int of_parse_phandles_with_args(struct device_node *np, const char *list_name, + const char *cells_name, int index, + struct device_node **out_node, + const void **out_args) +{ + int ret = -EINVAL; + const u32 *list; + const u32 *list_end; + int size; + int cur_index = 0; + struct device_node *node = NULL; + const void *args; + + list = of_get_property(np, list_name, &size); + if (!list) { + ret = -ENOENT; + goto err0; + } + list_end = list + size / sizeof(*list); + + while (list < list_end) { + const u32 *cells; + const phandle *phandle; + + phandle = list; + args = list + 1; + + /* one cell hole in the list = <>; */ + if (!*phandle) { + list++; + goto next; + } + + node = of_find_node_by_phandle(*phandle); + if (!node) { + pr_debug("%s: could not find phandle\n", + np->full_name); + goto err0; + } + + cells = of_get_property(node, cells_name, &size); + if (!cells || size != sizeof(*cells)) { + pr_debug("%s: could not get %s for %s\n", + np->full_name, cells_name, node->full_name); + goto err1; + } + + /* Next phandle is at offset of one phandle cell + #cells */ + list += 1 + *cells; + if (list > list_end) { + pr_debug("%s: insufficient arguments length\n", + np->full_name); + goto err1; + } +next: + if (cur_index == index) + break; + + of_node_put(node); + node = NULL; + cur_index++; + } + + if (!node) { + ret = -ENOENT; + goto err0; + } + + *out_node = node; + *out_args = args; + + return 0; +err1: + of_node_put(node); +err0: + pr_debug("%s failed with status %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(of_parse_phandles_with_args); |