forked from mirrors/linux
		
	dt: add of_alias_scan and of_alias_get_id
The patch adds function of_alias_scan to populate a global lookup
table with the properties of 'aliases' node and function
of_alias_get_id for drivers to find alias id from the lookup table.
v3: Split out automatic addition of aliases on id lookup so that it can be
    debated separately from the core functionality.
v2: - Add of_chosen/of_aliases populating and of_alias_scan() invocation
    for OF_PROMTREE.
    - Add locking
    - rework parse loop
Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
			
			
This commit is contained in:
		
							parent
							
								
									a81dd364de
								
							
						
					
					
						commit
						611cad7201
					
				
					 4 changed files with 138 additions and 4 deletions
				
			
		|  | @ -17,14 +17,39 @@ | |||
|  *      as published by the Free Software Foundation; either version | ||||
|  *      2 of the License, or (at your option) any later version. | ||||
|  */ | ||||
| #include <linux/ctype.h> | ||||
| #include <linux/module.h> | ||||
| #include <linux/of.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/proc_fs.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * struct alias_prop - Alias property in 'aliases' node | ||||
|  * @link:	List node to link the structure in aliases_lookup list | ||||
|  * @alias:	Alias property name | ||||
|  * @np:		Pointer to device_node that the alias stands for | ||||
|  * @id:		Index value from end of alias name | ||||
|  * @stem:	Alias string without the index | ||||
|  * | ||||
|  * The structure represents one alias property of 'aliases' node as | ||||
|  * an entry in aliases_lookup list. | ||||
|  */ | ||||
| struct alias_prop { | ||||
| 	struct list_head link; | ||||
| 	const char *alias; | ||||
| 	struct device_node *np; | ||||
| 	int id; | ||||
| 	char stem[0]; | ||||
| }; | ||||
| 
 | ||||
| static LIST_HEAD(aliases_lookup); | ||||
| 
 | ||||
| struct device_node *allnodes; | ||||
| struct device_node *of_chosen; | ||||
| struct device_node *of_aliases; | ||||
| 
 | ||||
| static DEFINE_MUTEX(of_aliases_mutex); | ||||
| 
 | ||||
| /* use when traversing tree through the allnext, child, sibling,
 | ||||
|  * or parent members of struct device_node. | ||||
|  | @ -988,3 +1013,99 @@ void of_detach_node(struct device_node *np) | |||
| } | ||||
| #endif /* defined(CONFIG_OF_DYNAMIC) */ | ||||
| 
 | ||||
| static void of_alias_add(struct alias_prop *ap, struct device_node *np, | ||||
| 			 int id, const char *stem, int stem_len) | ||||
| { | ||||
| 	ap->np = np; | ||||
| 	ap->id = id; | ||||
| 	strncpy(ap->stem, stem, stem_len); | ||||
| 	ap->stem[stem_len] = 0; | ||||
| 	list_add_tail(&ap->link, &aliases_lookup); | ||||
| 	pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", | ||||
| 		 ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * of_alias_scan - Scan all properties of 'aliases' node | ||||
|  * | ||||
|  * The function scans all the properties of 'aliases' node and populate | ||||
|  * the the global lookup table with the properties.  It returns the | ||||
|  * number of alias_prop found, or error code in error case. | ||||
|  * | ||||
|  * @dt_alloc:	An allocator that provides a virtual address to memory | ||||
|  *		for the resulting tree | ||||
|  */ | ||||
| void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) | ||||
| { | ||||
| 	struct property *pp; | ||||
| 
 | ||||
| 	of_chosen = of_find_node_by_path("/chosen"); | ||||
| 	if (of_chosen == NULL) | ||||
| 		of_chosen = of_find_node_by_path("/chosen@0"); | ||||
| 	of_aliases = of_find_node_by_path("/aliases"); | ||||
| 	if (!of_aliases) | ||||
| 		return; | ||||
| 
 | ||||
| 	for_each_property(pp, of_aliases->properties) { | ||||
| 		const char *start = pp->name; | ||||
| 		const char *end = start + strlen(start); | ||||
| 		struct device_node *np; | ||||
| 		struct alias_prop *ap; | ||||
| 		int id, len; | ||||
| 
 | ||||
| 		/* Skip those we do not want to proceed */ | ||||
| 		if (!strcmp(pp->name, "name") || | ||||
| 		    !strcmp(pp->name, "phandle") || | ||||
| 		    !strcmp(pp->name, "linux,phandle")) | ||||
| 			continue; | ||||
| 
 | ||||
| 		np = of_find_node_by_path(pp->value); | ||||
| 		if (!np) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* walk the alias backwards to extract the id and work out
 | ||||
| 		 * the 'stem' string */ | ||||
| 		while (isdigit(*(end-1)) && end > start) | ||||
| 			end--; | ||||
| 		len = end - start; | ||||
| 
 | ||||
| 		if (kstrtoint(end, 10, &id) < 0) | ||||
| 			continue; | ||||
| 
 | ||||
| 		/* Allocate an alias_prop with enough space for the stem */ | ||||
| 		ap = dt_alloc(sizeof(*ap) + len + 1, 4); | ||||
| 		if (!ap) | ||||
| 			continue; | ||||
| 		ap->alias = start; | ||||
| 		of_alias_add(ap, np, id, start, len); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * of_alias_get_id - Get alias id for the given device_node | ||||
|  * @np:		Pointer to the given device_node | ||||
|  * @stem:	Alias stem of the given device_node | ||||
|  * | ||||
|  * The function travels the lookup table to get alias id for the given | ||||
|  * device_node and alias stem.  It returns the alias id if find it. | ||||
|  */ | ||||
| int of_alias_get_id(struct device_node *np, const char *stem) | ||||
| { | ||||
| 	struct alias_prop *app; | ||||
| 	int id = -ENODEV; | ||||
| 
 | ||||
| 	mutex_lock(&of_aliases_mutex); | ||||
| 	list_for_each_entry(app, &aliases_lookup, link) { | ||||
| 		if (strcmp(app->stem, stem) != 0) | ||||
| 			continue; | ||||
| 
 | ||||
| 		if (np == app->np) { | ||||
| 			id = app->id; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	mutex_unlock(&of_aliases_mutex); | ||||
| 
 | ||||
| 	return id; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(of_alias_get_id); | ||||
|  |  | |||
|  | @ -707,10 +707,8 @@ void __init unflatten_device_tree(void) | |||
| 	__unflatten_device_tree(initial_boot_params, &allnodes, | ||||
| 				early_init_dt_alloc_memory_arch); | ||||
| 
 | ||||
| 	/* Get pointer to OF "/chosen" node for use everywhere */ | ||||
| 	of_chosen = of_find_node_by_path("/chosen"); | ||||
| 	if (of_chosen == NULL) | ||||
| 		of_chosen = of_find_node_by_path("/chosen@0"); | ||||
| 	/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */ | ||||
| 	of_alias_scan(early_init_dt_alloc_memory_arch); | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_OF_EARLY_FLATTREE */ | ||||
|  |  | |||
|  | @ -229,6 +229,11 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void *kernel_tree_alloc(u64 size, u64 align) | ||||
| { | ||||
| 	return prom_early_alloc(size); | ||||
| } | ||||
| 
 | ||||
| void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) | ||||
| { | ||||
| 	struct device_node **nextp; | ||||
|  | @ -245,4 +250,7 @@ void __init of_pdt_build_devicetree(phandle root_node, struct of_pdt_ops *ops) | |||
| 	nextp = &allnodes->allnext; | ||||
| 	allnodes->child = of_pdt_build_tree(allnodes, | ||||
| 			of_pdt_prom_ops->getchild(allnodes->phandle), &nextp); | ||||
| 
 | ||||
| 	/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */ | ||||
| 	of_alias_scan(kernel_tree_alloc); | ||||
| } | ||||
|  |  | |||
|  | @ -68,6 +68,7 @@ struct device_node { | |||
| /* Pointer for first entry in chain of all nodes. */ | ||||
| extern struct device_node *allnodes; | ||||
| extern struct device_node *of_chosen; | ||||
| extern struct device_node *of_aliases; | ||||
| extern rwlock_t devtree_lock; | ||||
| 
 | ||||
| static inline bool of_have_populated_dt(void) | ||||
|  | @ -209,6 +210,9 @@ extern int of_device_is_available(const struct device_node *device); | |||
| extern const void *of_get_property(const struct device_node *node, | ||||
| 				const char *name, | ||||
| 				int *lenp); | ||||
| #define for_each_property(pp, properties) \ | ||||
| 	for (pp = properties; pp != NULL; pp = pp->next) | ||||
| 
 | ||||
| extern int of_n_addr_cells(struct device_node *np); | ||||
| extern int of_n_size_cells(struct device_node *np); | ||||
| extern const struct of_device_id *of_match_node( | ||||
|  | @ -221,6 +225,9 @@ extern 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); | ||||
| 
 | ||||
| extern void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)); | ||||
| extern int of_alias_get_id(struct device_node *np, const char *stem); | ||||
| 
 | ||||
| extern int of_machine_is_compatible(const char *compat); | ||||
| 
 | ||||
| extern int prom_add_property(struct device_node* np, struct property* prop); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Shawn Guo
						Shawn Guo