diff options
Diffstat (limited to 'drivers/of/base.c')
| -rw-r--r-- | drivers/of/base.c | 128 | 
1 files changed, 73 insertions, 55 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index ff85450d5683..89e888a78899 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -342,27 +342,72 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)  }  EXPORT_SYMBOL(of_get_cpu_node); -/** Checks if the given "compat" string matches one of the strings in - * the device's "compatible" property +/** + * __of_device_is_compatible() - Check if the node matches given constraints + * @device: pointer to node + * @compat: required compatible string, NULL or "" for any match + * @type: required device_type value, NULL or "" for any match + * @name: required node name, NULL or "" for any match + * + * Checks if the given @compat, @type and @name strings match the + * properties of the given @device. A constraints can be skipped by + * passing NULL or an empty string as the constraint. + * + * Returns 0 for no match, and a positive integer on match. The return + * value is a relative score with larger values indicating better + * matches. The score is weighted for the most specific compatible value + * to get the highest score. Matching type is next, followed by matching + * name. Practically speaking, this results in the following priority + * order for matches: + * + * 1. specific compatible && type && name + * 2. specific compatible && type + * 3. specific compatible && name + * 4. specific compatible + * 5. general compatible && type && name + * 6. general compatible && type + * 7. general compatible && name + * 8. general compatible + * 9. type && name + * 10. type + * 11. name   */  static int __of_device_is_compatible(const struct device_node *device, -				     const char *compat) +				     const char *compat, const char *type, const char *name)  { -	const char* cp; -	int cplen, l; +	struct property *prop; +	const char *cp; +	int index = 0, score = 0; -	cp = __of_get_property(device, "compatible", &cplen); -	if (cp == NULL) -		return 0; -	while (cplen > 0) { -		if (of_compat_cmp(cp, compat, strlen(compat)) == 0) -			return 1; -		l = strlen(cp) + 1; -		cp += l; -		cplen -= l; +	/* Compatible match has highest priority */ +	if (compat && compat[0]) { +		prop = __of_find_property(device, "compatible", NULL); +		for (cp = of_prop_next_string(prop, NULL); cp; +		     cp = of_prop_next_string(prop, cp), index++) { +			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) { +				score = INT_MAX/2 - (index << 2); +				break; +			} +		} +		if (!score) +			return 0;  	} -	return 0; +	/* Matching type is better than matching name */ +	if (type && type[0]) { +		if (!device->type || of_node_cmp(type, device->type)) +			return 0; +		score += 2; +	} + +	/* Matching name is a bit better than not */ +	if (name && name[0]) { +		if (!device->name || of_node_cmp(name, device->name)) +			return 0; +		score++; +	} + +	return score;  }  /** Checks if the given "compat" string matches one of the strings in @@ -375,7 +420,7 @@ int of_device_is_compatible(const struct device_node *device,  	int res;  	raw_spin_lock_irqsave(&devtree_lock, flags); -	res = __of_device_is_compatible(device, compat); +	res = __of_device_is_compatible(device, compat, NULL, NULL);  	raw_spin_unlock_irqrestore(&devtree_lock, flags);  	return res;  } @@ -681,10 +726,7 @@ struct device_node *of_find_compatible_node(struct device_node *from,  	raw_spin_lock_irqsave(&devtree_lock, flags);  	np = from ? from->allnext : of_allnodes;  	for (; np; np = np->allnext) { -		if (type -		    && !(np->type && (of_node_cmp(np->type, type) == 0))) -			continue; -		if (__of_device_is_compatible(np, compatible) && +		if (__of_device_is_compatible(np, compatible, type, NULL) &&  		    of_node_get(np))  			break;  	} @@ -734,43 +776,22 @@ static  const struct of_device_id *__of_match_node(const struct of_device_id *matches,  					   const struct device_node *node)  { -	const char *cp; -	int cplen, l; +	const struct of_device_id *best_match = NULL; +	int score, best_score = 0;  	if (!matches)  		return NULL; -	cp = __of_get_property(node, "compatible", &cplen); -	do { -		const struct of_device_id *m = matches; - -		/* Check against matches with current compatible string */ -		while (m->name[0] || m->type[0] || m->compatible[0]) { -			int match = 1; -			if (m->name[0]) -				match &= node->name -					&& !strcmp(m->name, node->name); -			if (m->type[0]) -				match &= node->type -					&& !strcmp(m->type, node->type); -			if (m->compatible[0]) -				match &= cp -					&& !of_compat_cmp(m->compatible, cp, -							strlen(m->compatible)); -			if (match) -				return m; -			m++; +	for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) { +		score = __of_device_is_compatible(node, matches->compatible, +						  matches->type, matches->name); +		if (score > best_score) { +			best_match = matches; +			best_score = score;  		} +	} -		/* Get node's next compatible string */  -		if (cp) { -			l = strlen(cp) + 1; -			cp += l; -			cplen -= l; -		} -	} while (cp && (cplen > 0)); - -	return NULL; +	return best_match;  }  /** @@ -778,10 +799,7 @@ const struct of_device_id *__of_match_node(const struct of_device_id *matches,   *	@matches:	array of of device match structures to search in   *	@node:		the of device structure to match against   * - *	Low level utility function used by device matching. Matching order - *	is to compare each of the node's compatibles with all given matches - *	first. This implies node's compatible is sorted from specific to - *	generic while matches can be in any order. + *	Low level utility function used by device matching.   */  const struct of_device_id *of_match_node(const struct of_device_id *matches,  					 const struct device_node *node)  | 
