summaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
authorAndrey Smirnov <andrew.smirnov@gmail.com>2017-03-08 14:08:58 -0800
committerSascha Hauer <s.hauer@pengutronix.de>2017-03-09 11:51:27 +0100
commit3494084017c4b7e6b44923219a9fd07689fcc2fc (patch)
treeab2b89771d9f5703cd2ccbe9dea5536abb9bf951 /drivers/of
parenta8b41f2c7266cb4fd01f68937fdc2391f10a11cf (diff)
downloadbarebox-3494084017c4b7e6b44923219a9fd07689fcc2fc.tar.gz
barebox-3494084017c4b7e6b44923219a9fd07689fcc2fc.tar.xz
of: base: Use scoring in DT device matching
Port Linux kernel algorithm for both of_device_is_compatible() and of_match_node(). With this change former now returns a score on the scale of 0 to INT_MAX/2, and the latter goes through all compatiblity entries and selects the entry that has the best matching score. This is needed for SoCs where IP blocks are backwards compatible and corresponding OF nodes can proclaim compatibility with several entries found in driver's compatiblity table. One such example would be PIO pinctrl block on AT91SAM9x5 SoCs which declare compatibility with with both "atmel,at91sam9x5-pinctrl" and "atmel,at91rm9200-pinctrl". Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/base.c37
1 files changed, 21 insertions, 16 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 0c20fcd5c3..bef8f1de1a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -472,21 +472,20 @@ EXPORT_SYMBOL(of_get_cpu_node);
int of_device_is_compatible(const struct device_node *device,
const char *compat)
{
+ struct property *prop;
const char *cp;
- int cplen, l;
+ 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;
+ 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;
+ }
}
- return 0;
+ return score;
}
EXPORT_SYMBOL(of_device_is_compatible);
@@ -602,16 +601,22 @@ EXPORT_SYMBOL(of_find_node_with_property);
const struct of_device_id *of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
+ const struct of_device_id *best_match = NULL;
+ int score, best_score = 0;
+
if (!matches || !node)
return NULL;
- while (matches->compatible) {
- if (of_device_is_compatible(node, matches->compatible) == 1)
- return matches;
- matches++;
+ for (; matches->compatible; matches++) {
+ score = of_device_is_compatible(node, matches->compatible);
+
+ if (score > best_score) {
+ best_match = matches;
+ best_score = score;
+ }
}
- return NULL;
+ return best_match;
}
/**