of: reimplement the matching method for __of_match_node()

In the current implementation of __of_match_node(), it will compare
each given match entry against all the node's compatible strings
with of_device_is_compatible().

To achieve multiple compatible strings per node with ordering from
specific to generic, this requires given matches to be ordered from
specific to generic. For most of the drivers this is not true and
also an alphabetical ordering is more sane there.

Therefore, we define a following priority order for the match, and
then scan all the entries to find the best match.
  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

v5: Fix nested locking bug
v4: Short-circuit failure cases instead of mucking with score, and
    remove extra __of_device_is_compatible() wrapper stub.
    Move scoring logic directly into __of_device_is_compatible()
v3: Also need to bail out when there does have a compatible member in match
    entry, but it doesn't match with the device node's compatible.
v2: Fix the bug such as we get the same score for the following two match
    entries with the empty node 'name2 { };'
	struct of_device_id matches[] = {
		{.name = "name2", },
		{.name = "name2", .type = "type1", },
		{}
	};

Signed-off-by: Kevin Hao <haokexin@gmail.com>
[grant.likely: added v4 changes]
Signed-off-by: Grant Likely <grant.likely@linaro.org>
Tested-by: Paul Gortmaker <paul.gortmaker@windriver.com>
Tested-by: Stephen Chivers <schivers@csc.com>
Tested-by: Sachin Kamat <sachin.kamat@linaro.org>
This commit is contained in:
Kevin Hao 2014-02-19 16:15:45 +08:00 committed by Grant Likely
parent 71c5498eed
commit 215a14cfac
1 changed files with 74 additions and 35 deletions

View File

@ -342,27 +342,72 @@ struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
} }
EXPORT_SYMBOL(of_get_cpu_node); 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, 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; struct property *prop;
int cplen, l; const char *cp;
int index = 0, score = 0;
cp = __of_get_property(device, "compatible", &cplen); /* Compatible match has highest priority */
if (cp == NULL) if (compat && compat[0]) {
return 0; prop = __of_find_property(device, "compatible", NULL);
while (cplen > 0) { for (cp = of_prop_next_string(prop, NULL); cp;
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) cp = of_prop_next_string(prop, cp), index++) {
return 1; if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
l = strlen(cp) + 1; score = INT_MAX/2 - (index << 2);
cp += l; break;
cplen -= l; }
}
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 /** 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; int res;
raw_spin_lock_irqsave(&devtree_lock, flags); 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); raw_spin_unlock_irqrestore(&devtree_lock, flags);
return res; return res;
} }
@ -681,10 +726,7 @@ struct device_node *of_find_compatible_node(struct device_node *from,
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
np = from ? from->allnext : of_allnodes; np = from ? from->allnext : of_allnodes;
for (; np; np = np->allnext) { for (; np; np = np->allnext) {
if (type if (__of_device_is_compatible(np, compatible, type, NULL) &&
&& !(np->type && (of_node_cmp(np->type, type) == 0)))
continue;
if (__of_device_is_compatible(np, compatible) &&
of_node_get(np)) of_node_get(np))
break; break;
} }
@ -734,25 +776,22 @@ static
const struct of_device_id *__of_match_node(const struct of_device_id *matches, const struct of_device_id *__of_match_node(const struct of_device_id *matches,
const struct device_node *node) const struct device_node *node)
{ {
const struct of_device_id *best_match = NULL;
int score, best_score = 0;
if (!matches) if (!matches)
return NULL; return NULL;
while (matches->name[0] || matches->type[0] || matches->compatible[0]) { for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
int match = 1; score = __of_device_is_compatible(node, matches->compatible,
if (matches->name[0]) matches->type, matches->name);
match &= node->name if (score > best_score) {
&& !strcmp(matches->name, node->name); best_match = matches;
if (matches->type[0]) best_score = score;
match &= node->type }
&& !strcmp(matches->type, node->type);
if (matches->compatible[0])
match &= __of_device_is_compatible(node,
matches->compatible);
if (match)
return matches;
matches++;
} }
return NULL;
return best_match;
} }
/** /**