diff options
Diffstat (limited to 'drivers/of/selftest.c')
| -rw-r--r-- | drivers/of/selftest.c | 382 | 
1 files changed, 376 insertions, 6 deletions
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 0eb5c38b4e0..077314eebb9 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -9,21 +9,134 @@  #include <linux/errno.h>  #include <linux/module.h>  #include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h>  #include <linux/list.h>  #include <linux/mutex.h>  #include <linux/slab.h>  #include <linux/device.h> -static bool selftest_passed = true; +static struct selftest_results { +	int passed; +	int failed; +} selftest_results; +  #define selftest(result, fmt, ...) { \  	if (!(result)) { \ -		pr_err("FAIL %s:%i " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ -		selftest_passed = false; \ +		selftest_results.failed++; \ +		pr_err("FAIL %s():%i " fmt, __func__, __LINE__, ##__VA_ARGS__); \  	} else { \ -		pr_info("pass %s:%i\n", __FILE__, __LINE__); \ +		selftest_results.passed++; \ +		pr_debug("pass %s():%i\n", __func__, __LINE__); \  	} \  } +static void __init of_selftest_find_node_by_name(void) +{ +	struct device_node *np; + +	np = of_find_node_by_path("/testcase-data"); +	selftest(np && !strcmp("/testcase-data", np->full_name), +		"find /testcase-data failed\n"); +	of_node_put(np); + +	/* Test if trailing '/' works */ +	np = of_find_node_by_path("/testcase-data/"); +	selftest(!np, "trailing '/' on /testcase-data/ should fail\n"); + +	np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); +	selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name), +		"find /testcase-data/phandle-tests/consumer-a failed\n"); +	of_node_put(np); + +	np = of_find_node_by_path("testcase-alias"); +	selftest(np && !strcmp("/testcase-data", np->full_name), +		"find testcase-alias failed\n"); +	of_node_put(np); + +	/* Test if trailing '/' works on aliases */ +	np = of_find_node_by_path("testcase-alias/"); +	selftest(!np, "trailing '/' on testcase-alias/ should fail\n"); + +	np = of_find_node_by_path("testcase-alias/phandle-tests/consumer-a"); +	selftest(np && !strcmp("/testcase-data/phandle-tests/consumer-a", np->full_name), +		"find testcase-alias/phandle-tests/consumer-a failed\n"); +	of_node_put(np); + +	np = of_find_node_by_path("/testcase-data/missing-path"); +	selftest(!np, "non-existent path returned node %s\n", np->full_name); +	of_node_put(np); + +	np = of_find_node_by_path("missing-alias"); +	selftest(!np, "non-existent alias returned node %s\n", np->full_name); +	of_node_put(np); + +	np = of_find_node_by_path("testcase-alias/missing-path"); +	selftest(!np, "non-existent alias with relative path returned node %s\n", np->full_name); +	of_node_put(np); +} + +static void __init of_selftest_dynamic(void) +{ +	struct device_node *np; +	struct property *prop; + +	np = of_find_node_by_path("/testcase-data"); +	if (!np) { +		pr_err("missing testcase data\n"); +		return; +	} + +	/* Array of 4 properties for the purpose of testing */ +	prop = kzalloc(sizeof(*prop) * 4, GFP_KERNEL); +	if (!prop) { +		selftest(0, "kzalloc() failed\n"); +		return; +	} + +	/* Add a new property - should pass*/ +	prop->name = "new-property"; +	prop->value = "new-property-data"; +	prop->length = strlen(prop->value); +	selftest(of_add_property(np, prop) == 0, "Adding a new property failed\n"); + +	/* Try to add an existing property - should fail */ +	prop++; +	prop->name = "new-property"; +	prop->value = "new-property-data-should-fail"; +	prop->length = strlen(prop->value); +	selftest(of_add_property(np, prop) != 0, +		 "Adding an existing property should have failed\n"); + +	/* Try to modify an existing property - should pass */ +	prop->value = "modify-property-data-should-pass"; +	prop->length = strlen(prop->value); +	selftest(of_update_property(np, prop) == 0, +		 "Updating an existing property should have passed\n"); + +	/* Try to modify non-existent property - should pass*/ +	prop++; +	prop->name = "modify-property"; +	prop->value = "modify-missing-property-data-should-pass"; +	prop->length = strlen(prop->value); +	selftest(of_update_property(np, prop) == 0, +		 "Updating a missing property should have passed\n"); + +	/* Remove property - should pass */ +	selftest(of_remove_property(np, prop) == 0, +		 "Removing a property should have passed\n"); + +	/* Adding very large property - should pass */ +	prop++; +	prop->name = "large-property-PAGE_SIZEx8"; +	prop->length = PAGE_SIZE * 8; +	prop->value = kzalloc(prop->length, GFP_KERNEL); +	selftest(prop->value != NULL, "Unable to allocate large buffer\n"); +	if (prop->value) +		selftest(of_add_property(np, prop) == 0, +			 "Adding a large property should have passed\n"); +} +  static void __init of_selftest_parse_phandle_with_args(void)  {  	struct device_node *np; @@ -131,7 +244,6 @@ static void __init of_selftest_property_match_string(void)  	struct device_node *np;  	int rc; -	pr_info("start\n");  	np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a");  	if (!np) {  		pr_err("No testcase data in device tree\n"); @@ -154,6 +266,257 @@ static void __init of_selftest_property_match_string(void)  	selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);  } +static void __init of_selftest_parse_interrupts(void) +{ +	struct device_node *np; +	struct of_phandle_args args; +	int i, rc; + +	np = of_find_node_by_path("/testcase-data/interrupts/interrupts0"); +	if (!np) { +		pr_err("missing testcase data\n"); +		return; +	} + +	for (i = 0; i < 4; i++) { +		bool passed = true; +		args.args_count = 0; +		rc = of_irq_parse_one(np, i, &args); + +		passed &= !rc; +		passed &= (args.args_count == 1); +		passed &= (args.args[0] == (i + 1)); + +		selftest(passed, "index %i - data error on node %s rc=%i\n", +			 i, args.np->full_name, rc); +	} +	of_node_put(np); + +	np = of_find_node_by_path("/testcase-data/interrupts/interrupts1"); +	if (!np) { +		pr_err("missing testcase data\n"); +		return; +	} + +	for (i = 0; i < 4; i++) { +		bool passed = true; +		args.args_count = 0; +		rc = of_irq_parse_one(np, i, &args); + +		/* Test the values from tests-phandle.dtsi */ +		switch (i) { +		case 0: +			passed &= !rc; +			passed &= (args.args_count == 1); +			passed &= (args.args[0] == 9); +			break; +		case 1: +			passed &= !rc; +			passed &= (args.args_count == 3); +			passed &= (args.args[0] == 10); +			passed &= (args.args[1] == 11); +			passed &= (args.args[2] == 12); +			break; +		case 2: +			passed &= !rc; +			passed &= (args.args_count == 2); +			passed &= (args.args[0] == 13); +			passed &= (args.args[1] == 14); +			break; +		case 3: +			passed &= !rc; +			passed &= (args.args_count == 2); +			passed &= (args.args[0] == 15); +			passed &= (args.args[1] == 16); +			break; +		default: +			passed = false; +		} +		selftest(passed, "index %i - data error on node %s rc=%i\n", +			 i, args.np->full_name, rc); +	} +	of_node_put(np); +} + +static void __init of_selftest_parse_interrupts_extended(void) +{ +	struct device_node *np; +	struct of_phandle_args args; +	int i, rc; + +	np = of_find_node_by_path("/testcase-data/interrupts/interrupts-extended0"); +	if (!np) { +		pr_err("missing testcase data\n"); +		return; +	} + +	for (i = 0; i < 7; i++) { +		bool passed = true; +		rc = of_irq_parse_one(np, i, &args); + +		/* Test the values from tests-phandle.dtsi */ +		switch (i) { +		case 0: +			passed &= !rc; +			passed &= (args.args_count == 1); +			passed &= (args.args[0] == 1); +			break; +		case 1: +			passed &= !rc; +			passed &= (args.args_count == 3); +			passed &= (args.args[0] == 2); +			passed &= (args.args[1] == 3); +			passed &= (args.args[2] == 4); +			break; +		case 2: +			passed &= !rc; +			passed &= (args.args_count == 2); +			passed &= (args.args[0] == 5); +			passed &= (args.args[1] == 6); +			break; +		case 3: +			passed &= !rc; +			passed &= (args.args_count == 1); +			passed &= (args.args[0] == 9); +			break; +		case 4: +			passed &= !rc; +			passed &= (args.args_count == 3); +			passed &= (args.args[0] == 10); +			passed &= (args.args[1] == 11); +			passed &= (args.args[2] == 12); +			break; +		case 5: +			passed &= !rc; +			passed &= (args.args_count == 2); +			passed &= (args.args[0] == 13); +			passed &= (args.args[1] == 14); +			break; +		case 6: +			passed &= !rc; +			passed &= (args.args_count == 1); +			passed &= (args.args[0] == 15); +			break; +		default: +			passed = false; +		} + +		selftest(passed, "index %i - data error on node %s rc=%i\n", +			 i, args.np->full_name, rc); +	} +	of_node_put(np); +} + +static struct of_device_id match_node_table[] = { +	{ .data = "A", .name = "name0", }, /* Name alone is lowest priority */ +	{ .data = "B", .type = "type1", }, /* followed by type alone */ + +	{ .data = "Ca", .name = "name2", .type = "type1", }, /* followed by both together */ +	{ .data = "Cb", .name = "name2", }, /* Only match when type doesn't match */ +	{ .data = "Cc", .name = "name2", .type = "type2", }, + +	{ .data = "E", .compatible = "compat3" }, +	{ .data = "G", .compatible = "compat2", }, +	{ .data = "H", .compatible = "compat2", .name = "name5", }, +	{ .data = "I", .compatible = "compat2", .type = "type1", }, +	{ .data = "J", .compatible = "compat2", .type = "type1", .name = "name8", }, +	{ .data = "K", .compatible = "compat2", .name = "name9", }, +	{} +}; + +static struct { +	const char *path; +	const char *data; +} match_node_tests[] = { +	{ .path = "/testcase-data/match-node/name0", .data = "A", }, +	{ .path = "/testcase-data/match-node/name1", .data = "B", }, +	{ .path = "/testcase-data/match-node/a/name2", .data = "Ca", }, +	{ .path = "/testcase-data/match-node/b/name2", .data = "Cb", }, +	{ .path = "/testcase-data/match-node/c/name2", .data = "Cc", }, +	{ .path = "/testcase-data/match-node/name3", .data = "E", }, +	{ .path = "/testcase-data/match-node/name4", .data = "G", }, +	{ .path = "/testcase-data/match-node/name5", .data = "H", }, +	{ .path = "/testcase-data/match-node/name6", .data = "G", }, +	{ .path = "/testcase-data/match-node/name7", .data = "I", }, +	{ .path = "/testcase-data/match-node/name8", .data = "J", }, +	{ .path = "/testcase-data/match-node/name9", .data = "K", }, +}; + +static void __init of_selftest_match_node(void) +{ +	struct device_node *np; +	const struct of_device_id *match; +	int i; + +	for (i = 0; i < ARRAY_SIZE(match_node_tests); i++) { +		np = of_find_node_by_path(match_node_tests[i].path); +		if (!np) { +			selftest(0, "missing testcase node %s\n", +				match_node_tests[i].path); +			continue; +		} + +		match = of_match_node(match_node_table, np); +		if (!match) { +			selftest(0, "%s didn't match anything\n", +				match_node_tests[i].path); +			continue; +		} + +		if (strcmp(match->data, match_node_tests[i].data) != 0) { +			selftest(0, "%s got wrong match. expected %s, got %s\n", +				match_node_tests[i].path, match_node_tests[i].data, +				(const char *)match->data); +			continue; +		} +		selftest(1, "passed"); +	} +} + +static void __init of_selftest_platform_populate(void) +{ +	int irq; +	struct device_node *np, *child; +	struct platform_device *pdev; +	struct of_device_id match[] = { +		{ .compatible = "test-device", }, +		{} +	}; + +	np = of_find_node_by_path("/testcase-data"); +	of_platform_populate(np, of_default_bus_match_table, NULL, NULL); + +	/* Test that a missing irq domain returns -EPROBE_DEFER */ +	np = of_find_node_by_path("/testcase-data/testcase-device1"); +	pdev = of_find_device_by_node(np); +	selftest(pdev, "device 1 creation failed\n"); + +	irq = platform_get_irq(pdev, 0); +	selftest(irq == -EPROBE_DEFER, "device deferred probe failed - %d\n", irq); + +	/* Test that a parsing failure does not return -EPROBE_DEFER */ +	np = of_find_node_by_path("/testcase-data/testcase-device2"); +	pdev = of_find_device_by_node(np); +	selftest(pdev, "device 2 creation failed\n"); +	irq = platform_get_irq(pdev, 0); +	selftest(irq < 0 && irq != -EPROBE_DEFER, "device parsing error failed - %d\n", irq); + +	np = of_find_node_by_path("/testcase-data/platform-tests"); +	if (!np) { +		pr_err("No testcase data in device tree\n"); +		return; +	} + +	for_each_child_of_node(np, child) { +		struct device_node *grandchild; +		of_platform_populate(child, match, NULL, NULL); +		for_each_child_of_node(child, grandchild) +			selftest(of_find_device_by_node(grandchild), +				 "Could not create device for node '%s'\n", +				 grandchild->name); +	} +} +  static int __init of_selftest(void)  {  	struct device_node *np; @@ -166,9 +529,16 @@ static int __init of_selftest(void)  	of_node_put(np);  	pr_info("start of selftest - you will see error messages\n"); +	of_selftest_find_node_by_name(); +	of_selftest_dynamic();  	of_selftest_parse_phandle_with_args();  	of_selftest_property_match_string(); -	pr_info("end of selftest - %s\n", selftest_passed ? "PASS" : "FAIL"); +	of_selftest_parse_interrupts(); +	of_selftest_parse_interrupts_extended(); +	of_selftest_match_node(); +	of_selftest_platform_populate(); +	pr_info("end of selftest - %i passed, %i failed\n", +		selftest_results.passed, selftest_results.failed);  	return 0;  }  late_initcall(of_selftest);  | 
