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) | |||
458 | } | 458 | } |
459 | EXPORT_SYMBOL_GPL(of_modalias_node); | 459 | EXPORT_SYMBOL_GPL(of_modalias_node); |
460 | 460 | ||
461 | /** | ||
462 | * of_parse_phandles_with_args - Find a node pointed by phandle in a list | ||
463 | * @np: pointer to a device tree node containing a list | ||
464 | * @list_name: property name that contains a list | ||
465 | * @cells_name: property name that specifies phandles' arguments count | ||
466 | * @index: index of a phandle to parse out | ||
467 | * @out_node: pointer to device_node struct pointer (will be filled) | ||
468 | * @out_args: pointer to arguments pointer (will be filled) | ||
469 | * | ||
470 | * This function is useful to parse lists of phandles and their arguments. | ||
471 | * Returns 0 on success and fills out_node and out_args, on error returns | ||
472 | * appropriate errno value. | ||
473 | * | ||
474 | * Example: | ||
475 | * | ||
476 | * phandle1: node1 { | ||
477 | * #list-cells = <2>; | ||
478 | * } | ||
479 | * | ||
480 | * phandle2: node2 { | ||
481 | * #list-cells = <1>; | ||
482 | * } | ||
483 | * | ||
484 | * node3 { | ||
485 | * list = <&phandle1 1 2 &phandle2 3>; | ||
486 | * } | ||
487 | * | ||
488 | * To get a device_node of the `node2' node you may call this: | ||
489 | * of_parse_phandles_with_args(node3, "list", "#list-cells", 2, &node2, &args); | ||
490 | */ | ||
491 | int of_parse_phandles_with_args(struct device_node *np, const char *list_name, | ||
492 | const char *cells_name, int index, | ||
493 | struct device_node **out_node, | ||
494 | const void **out_args) | ||
495 | { | ||
496 | int ret = -EINVAL; | ||
497 | const u32 *list; | ||
498 | const u32 *list_end; | ||
499 | int size; | ||
500 | int cur_index = 0; | ||
501 | struct device_node *node = NULL; | ||
502 | const void *args; | ||
503 | |||
504 | list = of_get_property(np, list_name, &size); | ||
505 | if (!list) { | ||
506 | ret = -ENOENT; | ||
507 | goto err0; | ||
508 | } | ||
509 | list_end = list + size / sizeof(*list); | ||
510 | |||
511 | while (list < list_end) { | ||
512 | const u32 *cells; | ||
513 | const phandle *phandle; | ||
514 | |||
515 | phandle = list; | ||
516 | args = list + 1; | ||
517 | |||
518 | /* one cell hole in the list = <>; */ | ||
519 | if (!*phandle) { | ||
520 | list++; | ||
521 | goto next; | ||
522 | } | ||
523 | |||
524 | node = of_find_node_by_phandle(*phandle); | ||
525 | if (!node) { | ||
526 | pr_debug("%s: could not find phandle\n", | ||
527 | np->full_name); | ||
528 | goto err0; | ||
529 | } | ||
530 | |||
531 | cells = of_get_property(node, cells_name, &size); | ||
532 | if (!cells || size != sizeof(*cells)) { | ||
533 | pr_debug("%s: could not get %s for %s\n", | ||
534 | np->full_name, cells_name, node->full_name); | ||
535 | goto err1; | ||
536 | } | ||
537 | |||
538 | /* Next phandle is at offset of one phandle cell + #cells */ | ||
539 | list += 1 + *cells; | ||
540 | if (list > list_end) { | ||
541 | pr_debug("%s: insufficient arguments length\n", | ||
542 | np->full_name); | ||
543 | goto err1; | ||
544 | } | ||
545 | next: | ||
546 | if (cur_index == index) | ||
547 | break; | ||
548 | |||
549 | of_node_put(node); | ||
550 | node = NULL; | ||
551 | cur_index++; | ||
552 | } | ||
553 | |||
554 | if (!node) { | ||
555 | ret = -ENOENT; | ||
556 | goto err0; | ||
557 | } | ||
558 | |||
559 | *out_node = node; | ||
560 | *out_args = args; | ||
561 | |||
562 | return 0; | ||
563 | err1: | ||
564 | of_node_put(node); | ||
565 | err0: | ||
566 | pr_debug("%s failed with status %d\n", __func__, ret); | ||
567 | return ret; | ||
568 | } | ||
569 | EXPORT_SYMBOL(of_parse_phandles_with_args); | ||