diff options
-rw-r--r-- | drivers/of/base.c | 109 | ||||
-rw-r--r-- | drivers/of/gpio.c | 81 | ||||
-rw-r--r-- | include/linux/of.h | 3 |
3 files changed, 131 insertions, 62 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); | ||
diff --git a/drivers/of/gpio.c b/drivers/of/gpio.c index 1c9cab844f10..7cd7301b5839 100644 --- a/drivers/of/gpio.c +++ b/drivers/of/gpio.c | |||
@@ -28,78 +28,35 @@ | |||
28 | */ | 28 | */ |
29 | int of_get_gpio(struct device_node *np, int index) | 29 | int of_get_gpio(struct device_node *np, int index) |
30 | { | 30 | { |
31 | int ret = -EINVAL; | 31 | int ret; |
32 | struct device_node *gc; | 32 | struct device_node *gc; |
33 | struct of_gpio_chip *of_gc = NULL; | 33 | struct of_gpio_chip *of_gc = NULL; |
34 | int size; | 34 | int size; |
35 | const u32 *gpios; | ||
36 | u32 nr_cells; | ||
37 | int i; | ||
38 | const void *gpio_spec; | 35 | const void *gpio_spec; |
39 | const u32 *gpio_cells; | 36 | const u32 *gpio_cells; |
40 | int gpio_index = 0; | ||
41 | 37 | ||
42 | gpios = of_get_property(np, "gpios", &size); | 38 | ret = of_parse_phandles_with_args(np, "gpios", "#gpio-cells", index, |
43 | if (!gpios) { | 39 | &gc, &gpio_spec); |
44 | ret = -ENOENT; | 40 | if (ret) { |
41 | pr_debug("%s: can't parse gpios property\n", __func__); | ||
45 | goto err0; | 42 | goto err0; |
46 | } | 43 | } |
47 | nr_cells = size / sizeof(u32); | ||
48 | |||
49 | for (i = 0; i < nr_cells; gpio_index++) { | ||
50 | const phandle *gpio_phandle; | ||
51 | |||
52 | gpio_phandle = gpios + i; | ||
53 | gpio_spec = gpio_phandle + 1; | ||
54 | |||
55 | /* one cell hole in the gpios = <>; */ | ||
56 | if (!*gpio_phandle) { | ||
57 | if (gpio_index == index) | ||
58 | return -ENOENT; | ||
59 | i++; | ||
60 | continue; | ||
61 | } | ||
62 | |||
63 | gc = of_find_node_by_phandle(*gpio_phandle); | ||
64 | if (!gc) { | ||
65 | pr_debug("%s: could not find phandle for gpios\n", | ||
66 | np->full_name); | ||
67 | goto err0; | ||
68 | } | ||
69 | |||
70 | of_gc = gc->data; | ||
71 | if (!of_gc) { | ||
72 | pr_debug("%s: gpio controller %s isn't registered\n", | ||
73 | np->full_name, gc->full_name); | ||
74 | goto err1; | ||
75 | } | ||
76 | |||
77 | gpio_cells = of_get_property(gc, "#gpio-cells", &size); | ||
78 | if (!gpio_cells || size != sizeof(*gpio_cells) || | ||
79 | *gpio_cells != of_gc->gpio_cells) { | ||
80 | pr_debug("%s: wrong #gpio-cells for %s\n", | ||
81 | np->full_name, gc->full_name); | ||
82 | goto err1; | ||
83 | } | ||
84 | |||
85 | /* Next phandle is at phandle cells + #gpio-cells */ | ||
86 | i += sizeof(*gpio_phandle) / sizeof(u32) + *gpio_cells; | ||
87 | if (i >= nr_cells + 1) { | ||
88 | pr_debug("%s: insufficient gpio-spec length\n", | ||
89 | np->full_name); | ||
90 | goto err1; | ||
91 | } | ||
92 | |||
93 | if (gpio_index == index) | ||
94 | break; | ||
95 | |||
96 | of_gc = NULL; | ||
97 | of_node_put(gc); | ||
98 | } | ||
99 | 44 | ||
45 | of_gc = gc->data; | ||
100 | if (!of_gc) { | 46 | if (!of_gc) { |
101 | ret = -ENOENT; | 47 | pr_debug("%s: gpio controller %s isn't registered\n", |
102 | goto err0; | 48 | np->full_name, gc->full_name); |
49 | ret = -ENODEV; | ||
50 | goto err1; | ||
51 | } | ||
52 | |||
53 | gpio_cells = of_get_property(gc, "#gpio-cells", &size); | ||
54 | if (!gpio_cells || size != sizeof(*gpio_cells) || | ||
55 | *gpio_cells != of_gc->gpio_cells) { | ||
56 | pr_debug("%s: wrong #gpio-cells for %s\n", | ||
57 | np->full_name, gc->full_name); | ||
58 | ret = -EINVAL; | ||
59 | goto err1; | ||
103 | } | 60 | } |
104 | 61 | ||
105 | ret = of_gc->xlate(of_gc, np, gpio_spec); | 62 | ret = of_gc->xlate(of_gc, np, gpio_spec); |
diff --git a/include/linux/of.h b/include/linux/of.h index 79886ade070f..e2488f5e7cb2 100644 --- a/include/linux/of.h +++ b/include/linux/of.h | |||
@@ -71,5 +71,8 @@ extern int of_n_size_cells(struct device_node *np); | |||
71 | extern const struct of_device_id *of_match_node( | 71 | extern const struct of_device_id *of_match_node( |
72 | const struct of_device_id *matches, const struct device_node *node); | 72 | const struct of_device_id *matches, const struct device_node *node); |
73 | extern int of_modalias_node(struct device_node *node, char *modalias, int len); | 73 | extern int of_modalias_node(struct device_node *node, char *modalias, int len); |
74 | extern int of_parse_phandles_with_args(struct device_node *np, | ||
75 | const char *list_name, const char *cells_name, int index, | ||
76 | struct device_node **out_node, const void **out_args); | ||
74 | 77 | ||
75 | #endif /* _LINUX_OF_H */ | 78 | #endif /* _LINUX_OF_H */ |