diff options
| -rw-r--r-- | arch/arm/boot/dts/testcases/tests.dtsi | 2 | ||||
| -rw-r--r-- | arch/arm/boot/dts/versatile-pb.dts | 4 | ||||
| -rw-r--r-- | drivers/of/base.c | 150 | ||||
| -rw-r--r-- | drivers/of/selftest.c | 67 | ||||
| -rw-r--r-- | drivers/of/testcase-data/testcases.dtsi | 3 | ||||
| -rw-r--r-- | drivers/of/testcase-data/tests-interrupts.dtsi (renamed from arch/arm/boot/dts/testcases/tests-interrupts.dtsi) | 0 | ||||
| -rw-r--r-- | drivers/of/testcase-data/tests-match.dtsi | 19 | ||||
| -rw-r--r-- | drivers/of/testcase-data/tests-phandle.dtsi (renamed from arch/arm/boot/dts/testcases/tests-phandle.dtsi) | 0 | ||||
| -rw-r--r-- | scripts/Makefile.lib | 1 |
9 files changed, 166 insertions, 80 deletions
diff --git a/arch/arm/boot/dts/testcases/tests.dtsi b/arch/arm/boot/dts/testcases/tests.dtsi deleted file mode 100644 index 3f123ecc9dd7..000000000000 --- a/arch/arm/boot/dts/testcases/tests.dtsi +++ /dev/null | |||
| @@ -1,2 +0,0 @@ | |||
| 1 | /include/ "tests-phandle.dtsi" | ||
| 2 | /include/ "tests-interrupts.dtsi" | ||
diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts index f43907c40c93..65f657711323 100644 --- a/arch/arm/boot/dts/versatile-pb.dts +++ b/arch/arm/boot/dts/versatile-pb.dts | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | /include/ "versatile-ab.dts" | 1 | #include <versatile-ab.dts> |
| 2 | 2 | ||
| 3 | / { | 3 | / { |
| 4 | model = "ARM Versatile PB"; | 4 | model = "ARM Versatile PB"; |
| @@ -47,4 +47,4 @@ | |||
| 47 | }; | 47 | }; |
| 48 | }; | 48 | }; |
| 49 | 49 | ||
| 50 | /include/ "testcases/tests.dtsi" | 50 | #include <testcases.dtsi> |
diff --git a/drivers/of/base.c b/drivers/of/base.c index 10b51106c854..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) | |||
| 342 | } | 342 | } |
| 343 | EXPORT_SYMBOL(of_get_cpu_node); | 343 | EXPORT_SYMBOL(of_get_cpu_node); |
| 344 | 344 | ||
| 345 | /** Checks if the given "compat" string matches one of the strings in | 345 | /** |
| 346 | * the device's "compatible" property | 346 | * __of_device_is_compatible() - Check if the node matches given constraints |
| 347 | * @device: pointer to node | ||
| 348 | * @compat: required compatible string, NULL or "" for any match | ||
| 349 | * @type: required device_type value, NULL or "" for any match | ||
| 350 | * @name: required node name, NULL or "" for any match | ||
| 351 | * | ||
| 352 | * Checks if the given @compat, @type and @name strings match the | ||
| 353 | * properties of the given @device. A constraints can be skipped by | ||
| 354 | * passing NULL or an empty string as the constraint. | ||
| 355 | * | ||
| 356 | * Returns 0 for no match, and a positive integer on match. The return | ||
| 357 | * value is a relative score with larger values indicating better | ||
| 358 | * matches. The score is weighted for the most specific compatible value | ||
| 359 | * to get the highest score. Matching type is next, followed by matching | ||
| 360 | * name. Practically speaking, this results in the following priority | ||
| 361 | * order for matches: | ||
| 362 | * | ||
| 363 | * 1. specific compatible && type && name | ||
| 364 | * 2. specific compatible && type | ||
| 365 | * 3. specific compatible && name | ||
| 366 | * 4. specific compatible | ||
| 367 | * 5. general compatible && type && name | ||
| 368 | * 6. general compatible && type | ||
| 369 | * 7. general compatible && name | ||
| 370 | * 8. general compatible | ||
| 371 | * 9. type && name | ||
| 372 | * 10. type | ||
| 373 | * 11. name | ||
| 347 | */ | 374 | */ |
| 348 | static int __of_device_is_compatible(const struct device_node *device, | 375 | static int __of_device_is_compatible(const struct device_node *device, |
| 349 | const char *compat) | 376 | const char *compat, const char *type, const char *name) |
| 350 | { | 377 | { |
| 351 | const char* cp; | 378 | struct property *prop; |
| 352 | int cplen, l; | 379 | const char *cp; |
| 380 | int index = 0, score = 0; | ||
| 381 | |||
| 382 | /* Compatible match has highest priority */ | ||
| 383 | if (compat && compat[0]) { | ||
| 384 | prop = __of_find_property(device, "compatible", NULL); | ||
| 385 | for (cp = of_prop_next_string(prop, NULL); cp; | ||
| 386 | cp = of_prop_next_string(prop, cp), index++) { | ||
| 387 | if (of_compat_cmp(cp, compat, strlen(compat)) == 0) { | ||
| 388 | score = INT_MAX/2 - (index << 2); | ||
| 389 | break; | ||
| 390 | } | ||
| 391 | } | ||
| 392 | if (!score) | ||
| 393 | return 0; | ||
| 394 | } | ||
| 353 | 395 | ||
| 354 | cp = __of_get_property(device, "compatible", &cplen); | 396 | /* Matching type is better than matching name */ |
| 355 | if (cp == NULL) | 397 | if (type && type[0]) { |
| 356 | return 0; | 398 | if (!device->type || of_node_cmp(type, device->type)) |
| 357 | while (cplen > 0) { | 399 | return 0; |
| 358 | if (of_compat_cmp(cp, compat, strlen(compat)) == 0) | 400 | score += 2; |
| 359 | return 1; | ||
| 360 | l = strlen(cp) + 1; | ||
| 361 | cp += l; | ||
| 362 | cplen -= l; | ||
| 363 | } | 401 | } |
| 364 | 402 | ||
| 365 | return 0; | 403 | /* Matching name is a bit better than not */ |
| 404 | if (name && name[0]) { | ||
| 405 | if (!device->name || of_node_cmp(name, device->name)) | ||
| 406 | return 0; | ||
| 407 | score++; | ||
| 408 | } | ||
| 409 | |||
| 410 | return score; | ||
| 366 | } | 411 | } |
| 367 | 412 | ||
| 368 | /** Checks if the given "compat" string matches one of the strings in | 413 | /** 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, | |||
| 375 | int res; | 420 | int res; |
| 376 | 421 | ||
| 377 | raw_spin_lock_irqsave(&devtree_lock, flags); | 422 | raw_spin_lock_irqsave(&devtree_lock, flags); |
| 378 | res = __of_device_is_compatible(device, compat); | 423 | res = __of_device_is_compatible(device, compat, NULL, NULL); |
| 379 | raw_spin_unlock_irqrestore(&devtree_lock, flags); | 424 | raw_spin_unlock_irqrestore(&devtree_lock, flags); |
| 380 | return res; | 425 | return res; |
| 381 | } | 426 | } |
| @@ -681,10 +726,7 @@ struct device_node *of_find_compatible_node(struct device_node *from, | |||
| 681 | raw_spin_lock_irqsave(&devtree_lock, flags); | 726 | raw_spin_lock_irqsave(&devtree_lock, flags); |
| 682 | np = from ? from->allnext : of_allnodes; | 727 | np = from ? from->allnext : of_allnodes; |
| 683 | for (; np; np = np->allnext) { | 728 | for (; np; np = np->allnext) { |
| 684 | if (type | 729 | if (__of_device_is_compatible(np, compatible, type, NULL) && |
| 685 | && !(np->type && (of_node_cmp(np->type, type) == 0))) | ||
| 686 | continue; | ||
| 687 | if (__of_device_is_compatible(np, compatible) && | ||
| 688 | of_node_get(np)) | 730 | of_node_get(np)) |
| 689 | break; | 731 | break; |
| 690 | } | 732 | } |
| @@ -730,65 +772,26 @@ out: | |||
| 730 | } | 772 | } |
| 731 | EXPORT_SYMBOL(of_find_node_with_property); | 773 | EXPORT_SYMBOL(of_find_node_with_property); |
| 732 | 774 | ||
| 733 | static const struct of_device_id * | ||
| 734 | of_match_compatible(const struct of_device_id *matches, | ||
| 735 | const struct device_node *node) | ||
| 736 | { | ||
| 737 | const char *cp; | ||
| 738 | int cplen, l; | ||
| 739 | const struct of_device_id *m; | ||
| 740 | |||
| 741 | cp = __of_get_property(node, "compatible", &cplen); | ||
| 742 | while (cp && (cplen > 0)) { | ||
| 743 | m = matches; | ||
| 744 | while (m->name[0] || m->type[0] || m->compatible[0]) { | ||
| 745 | /* Only match for the entries without type and name */ | ||
| 746 | if (m->name[0] || m->type[0] || | ||
| 747 | of_compat_cmp(m->compatible, cp, | ||
| 748 | strlen(m->compatible))) | ||
| 749 | m++; | ||
| 750 | else | ||
| 751 | return m; | ||
| 752 | } | ||
| 753 | |||
| 754 | /* Get node's next compatible string */ | ||
| 755 | l = strlen(cp) + 1; | ||
| 756 | cp += l; | ||
| 757 | cplen -= l; | ||
| 758 | } | ||
| 759 | |||
| 760 | return NULL; | ||
| 761 | } | ||
| 762 | |||
| 763 | static | 775 | static |
| 764 | const struct of_device_id *__of_match_node(const struct of_device_id *matches, | 776 | const struct of_device_id *__of_match_node(const struct of_device_id *matches, |
| 765 | const struct device_node *node) | 777 | const struct device_node *node) |
| 766 | { | 778 | { |
| 767 | const struct of_device_id *m; | 779 | const struct of_device_id *best_match = NULL; |
| 780 | int score, best_score = 0; | ||
| 768 | 781 | ||
| 769 | if (!matches) | 782 | if (!matches) |
| 770 | return NULL; | 783 | return NULL; |
| 771 | 784 | ||
| 772 | m = of_match_compatible(matches, node); | 785 | for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) { |
| 773 | if (m) | 786 | score = __of_device_is_compatible(node, matches->compatible, |
| 774 | return m; | 787 | matches->type, matches->name); |
| 775 | 788 | if (score > best_score) { | |
| 776 | while (matches->name[0] || matches->type[0] || matches->compatible[0]) { | 789 | best_match = matches; |
| 777 | int match = 1; | 790 | best_score = score; |
| 778 | if (matches->name[0]) | 791 | } |
| 779 | match &= node->name | ||
| 780 | && !strcmp(matches->name, node->name); | ||
| 781 | if (matches->type[0]) | ||
| 782 | match &= node->type | ||
| 783 | && !strcmp(matches->type, node->type); | ||
| 784 | if (matches->compatible[0]) | ||
| 785 | match &= __of_device_is_compatible(node, | ||
| 786 | matches->compatible); | ||
| 787 | if (match) | ||
| 788 | return matches; | ||
| 789 | matches++; | ||
| 790 | } | 792 | } |
| 791 | return NULL; | 793 | |
| 794 | return best_match; | ||
| 792 | } | 795 | } |
| 793 | 796 | ||
| 794 | /** | 797 | /** |
| @@ -796,12 +799,7 @@ const struct of_device_id *__of_match_node(const struct of_device_id *matches, | |||
| 796 | * @matches: array of of device match structures to search in | 799 | * @matches: array of of device match structures to search in |
| 797 | * @node: the of device structure to match against | 800 | * @node: the of device structure to match against |
| 798 | * | 801 | * |
| 799 | * Low level utility function used by device matching. We have two ways | 802 | * Low level utility function used by device matching. |
| 800 | * of matching: | ||
| 801 | * - Try to find the best compatible match by comparing each compatible | ||
| 802 | * string of device node with all the given matches respectively. | ||
| 803 | * - If the above method failed, then try to match the compatible by using | ||
| 804 | * __of_device_is_compatible() besides the match in type and name. | ||
| 805 | */ | 803 | */ |
| 806 | const struct of_device_id *of_match_node(const struct of_device_id *matches, | 804 | const struct of_device_id *of_match_node(const struct of_device_id *matches, |
| 807 | const struct device_node *node) | 805 | const struct device_node *node) |
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index e21012bde639..6643d1920985 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c | |||
| @@ -300,6 +300,72 @@ static void __init of_selftest_parse_interrupts_extended(void) | |||
| 300 | of_node_put(np); | 300 | of_node_put(np); |
| 301 | } | 301 | } |
| 302 | 302 | ||
| 303 | static struct of_device_id match_node_table[] = { | ||
| 304 | { .data = "A", .name = "name0", }, /* Name alone is lowest priority */ | ||
| 305 | { .data = "B", .type = "type1", }, /* followed by type alone */ | ||
| 306 | |||
| 307 | { .data = "Ca", .name = "name2", .type = "type1", }, /* followed by both together */ | ||
| 308 | { .data = "Cb", .name = "name2", }, /* Only match when type doesn't match */ | ||
| 309 | { .data = "Cc", .name = "name2", .type = "type2", }, | ||
| 310 | |||
| 311 | { .data = "E", .compatible = "compat3" }, | ||
| 312 | { .data = "G", .compatible = "compat2", }, | ||
| 313 | { .data = "H", .compatible = "compat2", .name = "name5", }, | ||
| 314 | { .data = "I", .compatible = "compat2", .type = "type1", }, | ||
| 315 | { .data = "J", .compatible = "compat2", .type = "type1", .name = "name8", }, | ||
| 316 | { .data = "K", .compatible = "compat2", .name = "name9", }, | ||
| 317 | {} | ||
| 318 | }; | ||
| 319 | |||
| 320 | static struct { | ||
| 321 | const char *path; | ||
| 322 | const char *data; | ||
| 323 | } match_node_tests[] = { | ||
| 324 | { .path = "/testcase-data/match-node/name0", .data = "A", }, | ||
| 325 | { .path = "/testcase-data/match-node/name1", .data = "B", }, | ||
| 326 | { .path = "/testcase-data/match-node/a/name2", .data = "Ca", }, | ||
| 327 | { .path = "/testcase-data/match-node/b/name2", .data = "Cb", }, | ||
| 328 | { .path = "/testcase-data/match-node/c/name2", .data = "Cc", }, | ||
| 329 | { .path = "/testcase-data/match-node/name3", .data = "E", }, | ||
| 330 | { .path = "/testcase-data/match-node/name4", .data = "G", }, | ||
| 331 | { .path = "/testcase-data/match-node/name5", .data = "H", }, | ||
| 332 | { .path = "/testcase-data/match-node/name6", .data = "G", }, | ||
| 333 | { .path = "/testcase-data/match-node/name7", .data = "I", }, | ||
| 334 | { .path = "/testcase-data/match-node/name8", .data = "J", }, | ||
| 335 | { .path = "/testcase-data/match-node/name9", .data = "K", }, | ||
| 336 | }; | ||
| 337 | |||
| 338 | static void __init of_selftest_match_node(void) | ||
| 339 | { | ||
| 340 | struct device_node *np; | ||
| 341 | const struct of_device_id *match; | ||
| 342 | int i; | ||
| 343 | |||
| 344 | for (i = 0; i < ARRAY_SIZE(match_node_tests); i++) { | ||
| 345 | np = of_find_node_by_path(match_node_tests[i].path); | ||
| 346 | if (!np) { | ||
| 347 | selftest(0, "missing testcase node %s\n", | ||
| 348 | match_node_tests[i].path); | ||
| 349 | continue; | ||
| 350 | } | ||
| 351 | |||
| 352 | match = of_match_node(match_node_table, np); | ||
| 353 | if (!match) { | ||
| 354 | selftest(0, "%s didn't match anything\n", | ||
| 355 | match_node_tests[i].path); | ||
| 356 | continue; | ||
| 357 | } | ||
| 358 | |||
| 359 | if (strcmp(match->data, match_node_tests[i].data) != 0) { | ||
| 360 | selftest(0, "%s got wrong match. expected %s, got %s\n", | ||
| 361 | match_node_tests[i].path, match_node_tests[i].data, | ||
| 362 | (const char *)match->data); | ||
| 363 | continue; | ||
| 364 | } | ||
| 365 | selftest(1, "passed"); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 303 | static int __init of_selftest(void) | 369 | static int __init of_selftest(void) |
| 304 | { | 370 | { |
| 305 | struct device_node *np; | 371 | struct device_node *np; |
| @@ -316,6 +382,7 @@ static int __init of_selftest(void) | |||
| 316 | of_selftest_property_match_string(); | 382 | of_selftest_property_match_string(); |
| 317 | of_selftest_parse_interrupts(); | 383 | of_selftest_parse_interrupts(); |
| 318 | of_selftest_parse_interrupts_extended(); | 384 | of_selftest_parse_interrupts_extended(); |
| 385 | of_selftest_match_node(); | ||
| 319 | pr_info("end of selftest - %i passed, %i failed\n", | 386 | pr_info("end of selftest - %i passed, %i failed\n", |
| 320 | selftest_results.passed, selftest_results.failed); | 387 | selftest_results.passed, selftest_results.failed); |
| 321 | return 0; | 388 | return 0; |
diff --git a/drivers/of/testcase-data/testcases.dtsi b/drivers/of/testcase-data/testcases.dtsi new file mode 100644 index 000000000000..3a5b75a8e4d7 --- /dev/null +++ b/drivers/of/testcase-data/testcases.dtsi | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #include "tests-phandle.dtsi" | ||
| 2 | #include "tests-interrupts.dtsi" | ||
| 3 | #include "tests-match.dtsi" | ||
diff --git a/arch/arm/boot/dts/testcases/tests-interrupts.dtsi b/drivers/of/testcase-data/tests-interrupts.dtsi index c843720bd3e5..c843720bd3e5 100644 --- a/arch/arm/boot/dts/testcases/tests-interrupts.dtsi +++ b/drivers/of/testcase-data/tests-interrupts.dtsi | |||
diff --git a/drivers/of/testcase-data/tests-match.dtsi b/drivers/of/testcase-data/tests-match.dtsi new file mode 100644 index 000000000000..c9e541129534 --- /dev/null +++ b/drivers/of/testcase-data/tests-match.dtsi | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | |||
| 2 | / { | ||
| 3 | testcase-data { | ||
| 4 | match-node { | ||
| 5 | name0 { }; | ||
| 6 | name1 { device_type = "type1"; }; | ||
| 7 | a { name2 { device_type = "type1"; }; }; | ||
| 8 | b { name2 { }; }; | ||
| 9 | c { name2 { device_type = "type2"; }; }; | ||
| 10 | name3 { compatible = "compat3"; }; | ||
| 11 | name4 { compatible = "compat2", "compat3"; }; | ||
| 12 | name5 { compatible = "compat2", "compat3"; }; | ||
| 13 | name6 { compatible = "compat1", "compat2", "compat3"; }; | ||
| 14 | name7 { compatible = "compat2"; device_type = "type1"; }; | ||
| 15 | name8 { compatible = "compat2"; device_type = "type1"; }; | ||
| 16 | name9 { compatible = "compat2"; }; | ||
| 17 | }; | ||
| 18 | }; | ||
| 19 | }; | ||
diff --git a/arch/arm/boot/dts/testcases/tests-phandle.dtsi b/drivers/of/testcase-data/tests-phandle.dtsi index 0007d3cd7dc2..0007d3cd7dc2 100644 --- a/arch/arm/boot/dts/testcases/tests-phandle.dtsi +++ b/drivers/of/testcase-data/tests-phandle.dtsi | |||
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 49392ecbef17..79c059e70860 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib | |||
| @@ -152,6 +152,7 @@ ld_flags = $(LDFLAGS) $(ldflags-y) | |||
| 152 | dtc_cpp_flags = -Wp,-MD,$(depfile).pre.tmp -nostdinc \ | 152 | dtc_cpp_flags = -Wp,-MD,$(depfile).pre.tmp -nostdinc \ |
| 153 | -I$(srctree)/arch/$(SRCARCH)/boot/dts \ | 153 | -I$(srctree)/arch/$(SRCARCH)/boot/dts \ |
| 154 | -I$(srctree)/arch/$(SRCARCH)/boot/dts/include \ | 154 | -I$(srctree)/arch/$(SRCARCH)/boot/dts/include \ |
| 155 | -I$(srctree)/drivers/of/testcase-data \ | ||
| 155 | -undef -D__DTS__ | 156 | -undef -D__DTS__ |
| 156 | 157 | ||
| 157 | # Finds the multi-part object the current object will be linked into | 158 | # Finds the multi-part object the current object will be linked into |
