aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/of/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/base.c')
-rw-r--r--drivers/of/base.c134
1 files changed, 114 insertions, 20 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c
index a7264644003f..7c79e94a35ea 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -420,13 +420,12 @@ static struct of_modalias_table of_modalias_table[] = {
420 * @len: Length of modalias value 420 * @len: Length of modalias value
421 * 421 *
422 * Based on the value of the compatible property, this routine will determine 422 * Based on the value of the compatible property, this routine will determine
423 * an appropriate modalias value for a particular device tree node. Three 423 * an appropriate modalias value for a particular device tree node. Two
424 * separate methods are used to derive a modalias value. 424 * separate methods are attempted to derive a modalias value.
425 * 425 *
426 * First method is to lookup the compatible value in of_modalias_table. 426 * First method is to lookup the compatible value in of_modalias_table.
427 * Second is to look for a "linux,<modalias>" entry in the compatible list 427 * Second is to strip off the manufacturer prefix from the first
428 * and used that for modalias. Third is to strip off the manufacturer 428 * compatible entry and use the remainder as modalias
429 * prefix from the first compatible entry and use the remainder as modalias
430 * 429 *
431 * This routine returns 0 on success 430 * This routine returns 0 on success
432 */ 431 */
@@ -449,21 +448,7 @@ int of_modalias_node(struct device_node *node, char *modalias, int len)
449 if (!compatible) 448 if (!compatible)
450 return -ENODEV; 449 return -ENODEV;
451 450
452 /* 2. search for linux,<modalias> entry */ 451 /* 2. take first compatible entry and strip manufacturer */
453 p = compatible;
454 while (cplen > 0) {
455 if (!strncmp(p, "linux,", 6)) {
456 p += 6;
457 strlcpy(modalias, p, len);
458 return 0;
459 }
460
461 i = strlen(p) + 1;
462 p += i;
463 cplen -= i;
464 }
465
466 /* 3. take first compatible entry and strip manufacturer */
467 p = strchr(compatible, ','); 452 p = strchr(compatible, ',');
468 if (!p) 453 if (!p)
469 return -ENODEV; 454 return -ENODEV;
@@ -473,3 +458,112 @@ int of_modalias_node(struct device_node *node, char *modalias, int len)
473} 458}
474EXPORT_SYMBOL_GPL(of_modalias_node); 459EXPORT_SYMBOL_GPL(of_modalias_node);
475 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 */
491int 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 }
545next:
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;
563err1:
564 of_node_put(node);
565err0:
566 pr_debug("%s failed with status %d\n", __func__, ret);
567 return ret;
568}
569EXPORT_SYMBOL(of_parse_phandles_with_args);