|
17 | 17 | * as published by the Free Software Foundation; either version |
18 | 18 | * 2 of the License, or (at your option) any later version. |
19 | 19 | */ |
| 20 | +#include <linux/ctype.h> |
20 | 21 | #include <linux/module.h> |
21 | 22 | #include <linux/of.h> |
22 | 23 | #include <linux/spinlock.h> |
23 | 24 | #include <linux/slab.h> |
24 | 25 | #include <linux/proc_fs.h> |
25 | 26 |
|
| 27 | +/** |
| 28 | + * struct alias_prop - Alias property in 'aliases' node |
| 29 | + * @link: List node to link the structure in aliases_lookup list |
| 30 | + * @alias: Alias property name |
| 31 | + * @np: Pointer to device_node that the alias stands for |
| 32 | + * @id: Index value from end of alias name |
| 33 | + * @stem: Alias string without the index |
| 34 | + * |
| 35 | + * The structure represents one alias property of 'aliases' node as |
| 36 | + * an entry in aliases_lookup list. |
| 37 | + */ |
| 38 | +struct alias_prop { |
| 39 | + struct list_head link; |
| 40 | + const char *alias; |
| 41 | + struct device_node *np; |
| 42 | + int id; |
| 43 | + char stem[0]; |
| 44 | +}; |
| 45 | + |
| 46 | +static LIST_HEAD(aliases_lookup); |
| 47 | + |
26 | 48 | struct device_node *allnodes; |
27 | 49 | struct device_node *of_chosen; |
| 50 | +struct device_node *of_aliases; |
| 51 | + |
| 52 | +static DEFINE_MUTEX(of_aliases_mutex); |
28 | 53 |
|
29 | 54 | /* use when traversing tree through the allnext, child, sibling, |
30 | 55 | * or parent members of struct device_node. |
@@ -988,3 +1013,99 @@ void of_detach_node(struct device_node *np) |
988 | 1013 | } |
989 | 1014 | #endif /* defined(CONFIG_OF_DYNAMIC) */ |
990 | 1015 |
|
| 1016 | +static void of_alias_add(struct alias_prop *ap, struct device_node *np, |
| 1017 | + int id, const char *stem, int stem_len) |
| 1018 | +{ |
| 1019 | + ap->np = np; |
| 1020 | + ap->id = id; |
| 1021 | + strncpy(ap->stem, stem, stem_len); |
| 1022 | + ap->stem[stem_len] = 0; |
| 1023 | + list_add_tail(&ap->link, &aliases_lookup); |
| 1024 | + pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", |
| 1025 | + ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); |
| 1026 | +} |
| 1027 | + |
| 1028 | +/** |
| 1029 | + * of_alias_scan - Scan all properties of 'aliases' node |
| 1030 | + * |
| 1031 | + * The function scans all the properties of 'aliases' node and populate |
| 1032 | + * the the global lookup table with the properties. It returns the |
| 1033 | + * number of alias_prop found, or error code in error case. |
| 1034 | + * |
| 1035 | + * @dt_alloc: An allocator that provides a virtual address to memory |
| 1036 | + * for the resulting tree |
| 1037 | + */ |
| 1038 | +void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) |
| 1039 | +{ |
| 1040 | + struct property *pp; |
| 1041 | + |
| 1042 | + of_chosen = of_find_node_by_path("/chosen"); |
| 1043 | + if (of_chosen == NULL) |
| 1044 | + of_chosen = of_find_node_by_path("/chosen@0"); |
| 1045 | + of_aliases = of_find_node_by_path("/aliases"); |
| 1046 | + if (!of_aliases) |
| 1047 | + return; |
| 1048 | + |
| 1049 | + for_each_property(pp, of_aliases->properties) { |
| 1050 | + const char *start = pp->name; |
| 1051 | + const char *end = start + strlen(start); |
| 1052 | + struct device_node *np; |
| 1053 | + struct alias_prop *ap; |
| 1054 | + int id, len; |
| 1055 | + |
| 1056 | + /* Skip those we do not want to proceed */ |
| 1057 | + if (!strcmp(pp->name, "name") || |
| 1058 | + !strcmp(pp->name, "phandle") || |
| 1059 | + !strcmp(pp->name, "linux,phandle")) |
| 1060 | + continue; |
| 1061 | + |
| 1062 | + np = of_find_node_by_path(pp->value); |
| 1063 | + if (!np) |
| 1064 | + continue; |
| 1065 | + |
| 1066 | + /* walk the alias backwards to extract the id and work out |
| 1067 | + * the 'stem' string */ |
| 1068 | + while (isdigit(*(end-1)) && end > start) |
| 1069 | + end--; |
| 1070 | + len = end - start; |
| 1071 | + |
| 1072 | + if (kstrtoint(end, 10, &id) < 0) |
| 1073 | + continue; |
| 1074 | + |
| 1075 | + /* Allocate an alias_prop with enough space for the stem */ |
| 1076 | + ap = dt_alloc(sizeof(*ap) + len + 1, 4); |
| 1077 | + if (!ap) |
| 1078 | + continue; |
| 1079 | + ap->alias = start; |
| 1080 | + of_alias_add(ap, np, id, start, len); |
| 1081 | + } |
| 1082 | +} |
| 1083 | + |
| 1084 | +/** |
| 1085 | + * of_alias_get_id - Get alias id for the given device_node |
| 1086 | + * @np: Pointer to the given device_node |
| 1087 | + * @stem: Alias stem of the given device_node |
| 1088 | + * |
| 1089 | + * The function travels the lookup table to get alias id for the given |
| 1090 | + * device_node and alias stem. It returns the alias id if find it. |
| 1091 | + */ |
| 1092 | +int of_alias_get_id(struct device_node *np, const char *stem) |
| 1093 | +{ |
| 1094 | + struct alias_prop *app; |
| 1095 | + int id = -ENODEV; |
| 1096 | + |
| 1097 | + mutex_lock(&of_aliases_mutex); |
| 1098 | + list_for_each_entry(app, &aliases_lookup, link) { |
| 1099 | + if (strcmp(app->stem, stem) != 0) |
| 1100 | + continue; |
| 1101 | + |
| 1102 | + if (np == app->np) { |
| 1103 | + id = app->id; |
| 1104 | + break; |
| 1105 | + } |
| 1106 | + } |
| 1107 | + mutex_unlock(&of_aliases_mutex); |
| 1108 | + |
| 1109 | + return id; |
| 1110 | +} |
| 1111 | +EXPORT_SYMBOL_GPL(of_alias_get_id); |
0 commit comments