diff options
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | include/configs/sandbox.h | 1 | ||||
| -rw-r--r-- | include/dm/test.h | 167 | ||||
| -rw-r--r-- | include/dm/ut.h | 95 | ||||
| -rw-r--r-- | test/dm/.gitignore | 1 | ||||
| -rw-r--r-- | test/dm/Makefile | 17 | ||||
| -rw-r--r-- | test/dm/core.c | 544 | ||||
| -rwxr-xr-x | test/dm/test-dm.sh | 7 | ||||
| -rw-r--r-- | test/dm/test-driver.c | 146 | ||||
| -rw-r--r-- | test/dm/test-fdt.c | 144 | ||||
| -rw-r--r-- | test/dm/test-main.c | 107 | ||||
| -rw-r--r-- | test/dm/test-uclass.c | 104 | ||||
| -rw-r--r-- | test/dm/test.dts | 59 | ||||
| -rw-r--r-- | test/dm/ut.c | 33 | 
14 files changed, 1426 insertions, 0 deletions
| @@ -626,6 +626,7 @@ libs-y += lib/libfdt/  libs-$(CONFIG_API) += api/  libs-$(CONFIG_HAS_POST) += post/  libs-y += test/ +libs-y += test/dm/  ifneq (,$(filter $(SOC), mx25 mx27 mx5 mx6 mx31 mx35 mxs vf610))  libs-y += arch/$(ARCH)/imx-common/ diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 9cdfcc74a..c9c850919 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -21,6 +21,7 @@  #define CONFIG_BOOTSTAGE  #define CONFIG_BOOTSTAGE_REPORT  #define CONFIG_DM +#define CONFIG_DM_TEST  /* Number of bits in a C 'long' on this architecture */  #define CONFIG_SANDBOX_BITS_PER_LONG	64 diff --git a/include/dm/test.h b/include/dm/test.h new file mode 100644 index 000000000..eeaa2eb2f --- /dev/null +++ b/include/dm/test.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2013 Google, Inc. + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef __DM_TEST_H +#define __DM_TEST_H + +#include <dm.h> + +/** + * struct dm_test_cdata - configuration data for test instance + * + * @ping_add: Amonut to add each time we get a ping + * @base: Base address of this device + */ +struct dm_test_pdata { +	int ping_add; +	uint32_t base; +}; + +/** + * struct test_ops - Operations supported by the test device + * + * @ping: Ping operation + *	@dev: Device to operate on + *	@pingval: Value to ping the device with + *	@pingret: Returns resulting value from driver + *	@return 0 if OK, -ve on error + */ +struct test_ops { +	int (*ping)(struct device *dev, int pingval, int *pingret); +}; + +/* Operations that our test driver supports */ +enum { +	DM_TEST_OP_BIND = 0, +	DM_TEST_OP_UNBIND, +	DM_TEST_OP_PROBE, +	DM_TEST_OP_REMOVE, + +	/* For uclass */ +	DM_TEST_OP_POST_BIND, +	DM_TEST_OP_PRE_UNBIND, +	DM_TEST_OP_POST_PROBE, +	DM_TEST_OP_PRE_REMOVE, +	DM_TEST_OP_INIT, +	DM_TEST_OP_DESTROY, + +	DM_TEST_OP_COUNT, +}; + +/* Test driver types */ +enum { +	DM_TEST_TYPE_FIRST = 0, +	DM_TEST_TYPE_SECOND, +}; + +/* The number added to the ping total on each probe */ +#define DM_TEST_START_TOTAL	5 + +/** + * struct dm_test_priv - private data for the test devices + */ +struct dm_test_priv { +	int ping_total; +	int op_count[DM_TEST_OP_COUNT]; +}; + +/** + * struct dm_test_perdev_class_priv - private per-device data for test uclass + */ +struct dm_test_uclass_perdev_priv { +	int base_add; +}; + +/** + * struct dm_test_uclass_priv - private data for test uclass + */ +struct dm_test_uclass_priv { +	int total_add; +}; + +/* + * Operation counts for the test driver, used to check that each method is + * called correctly + */ +extern int dm_testdrv_op_count[DM_TEST_OP_COUNT]; + +extern struct dm_test_state global_test_state; + +/* + * struct dm_test_state - Entire state of dm test system + * + * This is often abreviated to dms. + * + * @root: Root device + * @testdev: Test device + * @fail_count: Number of tests that failed + * @force_fail_alloc: Force all memory allocs to fail + * @skip_post_probe: Skip uclass post-probe processing + */ +struct dm_test_state { +	struct device *root; +	struct device *testdev; +	int fail_count; +	int force_fail_alloc; +	int skip_post_probe; +}; + +/* Test flags for each test */ +enum { +	DM_TESTF_SCAN_PDATA	= 1 << 0,	/* test needs platform data */ +	DM_TESTF_PROBE_TEST	= 1 << 1,	/* probe test uclass */ +	DM_TESTF_SCAN_FDT	= 1 << 2,	/* scan device tree */ +}; + +/** + * struct dm_test - Information about a driver model test + * + * @name: Name of test + * @func: Function to call to perform test + * @flags: Flags indicated pre-conditions for test + */ +struct dm_test { +	const char *name; +	int (*func)(struct dm_test_state *dms); +	int flags; +}; + +/* Declare a new driver model test */ +#define DM_TEST(_name, _flags)						\ +	ll_entry_declare(struct dm_test, _name, dm_test) = {		\ +		.name = #_name,						\ +		.flags = _flags,					\ +		.func = _name,						\ +	} + +/* Declare ping methods for the drivers */ +int test_ping(struct device *dev, int pingval, int *pingret); +int testfdt_ping(struct device *dev, int pingval, int *pingret); + +/** + * dm_check_operations() - Check that we can perform ping operations + * + * This checks that the ping operations work as expected for a device + * + * @dms: Overall test state + * @dev: Device to test + * @base: Base address, used to check ping return value + * @priv: Pointer to private test information + * @return 0 if OK, -ve on error + */ +int dm_check_operations(struct dm_test_state *dms, struct device *dev, +			uint32_t base, struct dm_test_priv *priv); + +/** + * dm_test_main() - Run all the tests + * + * This runs all available driver model tests + * + * @return 0 if OK, -ve on error + */ +int dm_test_main(void); + +#endif diff --git a/include/dm/ut.h b/include/dm/ut.h new file mode 100644 index 000000000..fa9eac022 --- /dev/null +++ b/include/dm/ut.h @@ -0,0 +1,95 @@ +/* + * Simple unit test library for driver model + * + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#ifndef __DM_UT_H +#define __DM_UT_H + +struct dm_test_state; + +/** + * ut_fail() - Record failure of a unit test + * + * @dms: Test state + * @fname: Filename where the error occured + * @line: Line number where the error occured + * @func: Function name where the error occured + * @cond: The condition that failed + */ +void ut_fail(struct dm_test_state *dms, const char *fname, int line, +	     const char *func, const char *cond); + +/** + * ut_failf() - Record failure of a unit test + * + * @dms: Test state + * @fname: Filename where the error occured + * @line: Line number where the error occured + * @func: Function name where the error occured + * @cond: The condition that failed + * @fmt: printf() format string for the error, followed by args + */ +void ut_failf(struct dm_test_state *dms, const char *fname, int line, +	      const char *func, const char *cond, const char *fmt, ...) +			__attribute__ ((format (__printf__, 6, 7))); + + +/* Assert that a condition is non-zero */ +#define ut_assert(cond)							\ +	if (!(cond)) {							\ +		ut_fail(dms, __FILE__, __LINE__, __func__, #cond);	\ +		return -1;						\ +	} + +/* Assert that a condition is non-zero, with printf() string */ +#define ut_assertf(cond, fmt, args...)					\ +	if (!(cond)) {							\ +		ut_failf(dms, __FILE__, __LINE__, __func__, #cond,	\ +			 fmt, ##args);					\ +		return -1;						\ +	} + +/* Assert that two int expressions are equal */ +#define ut_asserteq(expr1, expr2) {					\ +	unsigned int val1 = (expr1), val2 = (expr2);			\ +									\ +	if (val1 != val2) {						\ +		ut_failf(dms, __FILE__, __LINE__, __func__,		\ +			 #expr1 " == " #expr2,				\ +			 "Expected %d, got %d", val1, val2);		\ +		return -1;						\ +	}								\ +} + +/* Assert that two string expressions are equal */ +#define ut_asserteq_str(expr1, expr2) {					\ +	const char *val1 = (expr1), *val2 = (expr2);			\ +									\ +	if (strcmp(val1, val2)) {					\ +		ut_failf(dms, __FILE__, __LINE__, __func__,		\ +			 #expr1 " = " #expr2,				\ +			 "Expected \"%s\", got \"%s\"", val1, val2);	\ +		return -1;						\ +	}								\ +} + +/* Assert that two pointers are equal */ +#define ut_asserteq_ptr(expr1, expr2) {					\ +	const void *val1 = (expr1), *val2 = (expr2);			\ +									\ +	if (val1 != val2) {						\ +		ut_failf(dms, __FILE__, __LINE__, __func__,		\ +			 #expr1 " = " #expr2,				\ +			 "Expected %p, got %p", val1, val2);		\ +		return -1;						\ +	}								\ +} + +/* Assert that an operation succeeds (returns 0) */ +#define ut_assertok(cond)	ut_asserteq(0, cond) + +#endif diff --git a/test/dm/.gitignore b/test/dm/.gitignore new file mode 100644 index 000000000..b741b8ab0 --- /dev/null +++ b/test/dm/.gitignore @@ -0,0 +1 @@ +/test.dtb diff --git a/test/dm/Makefile b/test/dm/Makefile new file mode 100644 index 000000000..6af85b931 --- /dev/null +++ b/test/dm/Makefile @@ -0,0 +1,17 @@ +# +# Copyright (c) 2013 Google, Inc +# +# SPDX-License-Identifier:	GPL-2.0+ +# + +obj-$(CONFIG_DM_TEST) += test-driver.o +obj-$(CONFIG_DM_TEST) += test-fdt.o +obj-$(CONFIG_DM_TEST) += test-main.o +obj-$(CONFIG_DM_TEST) += test-uclass.o +obj-$(CONFIG_DM_TEST) += ut.o + +# Tests for particular subsystems - when enabling driver model for a new +# subsystem you must add sandbox tests here. +obj-$(CONFIG_DM_TEST) += core.o +obj-$(CONFIG_DM_TEST) += ut.o +obj-$(CONFIG_DM_GPIO) += gpio.o diff --git a/test/dm/core.c b/test/dm/core.c new file mode 100644 index 000000000..14a57c310 --- /dev/null +++ b/test/dm/core.c @@ -0,0 +1,544 @@ +/* + * Tests for the core driver model code + * + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/ut.h> +#include <dm/util.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { +	TEST_INTVAL1		= 0, +	TEST_INTVAL2		= 3, +	TEST_INTVAL3		= 6, +	TEST_INTVAL_MANUAL	= 101112, +}; + +static const struct dm_test_pdata test_pdata[] = { +	{ .ping_add		= TEST_INTVAL1, }, +	{ .ping_add		= TEST_INTVAL2, }, +	{ .ping_add		= TEST_INTVAL3, }, +}; + +static const struct dm_test_pdata test_pdata_manual = { +	.ping_add		= TEST_INTVAL_MANUAL, +}; + +U_BOOT_DEVICE(dm_test_info1) = { +	.name = "test_drv", +	.platdata = &test_pdata[0], +}; + +U_BOOT_DEVICE(dm_test_info2) = { +	.name = "test_drv", +	.platdata = &test_pdata[1], +}; + +U_BOOT_DEVICE(dm_test_info3) = { +	.name = "test_drv", +	.platdata = &test_pdata[2], +}; + +static struct driver_info driver_info_manual = { +	.name = "test_manual_drv", +	.platdata = &test_pdata_manual, +}; + +/* Test that binding with platdata occurs correctly */ +static int dm_test_autobind(struct dm_test_state *dms) +{ +	struct device *dev; + +	/* +	 * We should have a single class (UCLASS_ROOT) and a single root +	 * device with no children. +	 */ +	ut_assert(dms->root); +	ut_asserteq(1, list_count_items(&gd->uclass_root)); +	ut_asserteq(0, list_count_items(&gd->dm_root->child_head)); +	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]); + +	ut_assertok(dm_scan_platdata()); + +	/* We should have our test class now at least, plus more children */ +	ut_assert(1 < list_count_items(&gd->uclass_root)); +	ut_assert(0 < list_count_items(&gd->dm_root->child_head)); + +	/* Our 3 dm_test_infox children should be bound to the test uclass */ +	ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]); + +	/* No devices should be probed */ +	list_for_each_entry(dev, &gd->dm_root->child_head, sibling_node) +		ut_assert(!(dev->flags & DM_FLAG_ACTIVATED)); + +	/* Our test driver should have been bound 3 times */ +	ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] == 3); + +	return 0; +} +DM_TEST(dm_test_autobind, 0); + +/* Test that autoprobe finds all the expected devices */ +static int dm_test_autoprobe(struct dm_test_state *dms) +{ +	int expected_base_add; +	struct device *dev; +	struct uclass *uc; +	int i; + +	ut_assertok(uclass_get(UCLASS_TEST, &uc)); +	ut_assert(uc); + +	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); +	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]); + +	/* The root device should not be activated until needed */ +	ut_assert(!(dms->root->flags & DM_FLAG_ACTIVATED)); + +	/* +	 * We should be able to find the three test devices, and they should +	 * all be activated as they are used (lazy activation, required by +	 * U-Boot) +	 */ +	for (i = 0; i < 3; i++) { +		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); +		ut_assert(dev); +		ut_assertf(!(dev->flags & DM_FLAG_ACTIVATED), +			   "Driver %d/%s already activated", i, dev->name); + +		/* This should activate it */ +		ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev)); +		ut_assert(dev); +		ut_assert(dev->flags & DM_FLAG_ACTIVATED); + +		/* Activating a device should activate the root device */ +		if (!i) +			ut_assert(dms->root->flags & DM_FLAG_ACTIVATED); +	} + +	/* Our 3 dm_test_infox children should be passed to post_probe */ +	ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]); + +	/* Also we can check the per-device data */ +	expected_base_add = 0; +	for (i = 0; i < 3; i++) { +		struct dm_test_uclass_perdev_priv *priv; +		struct dm_test_pdata *pdata; + +		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); +		ut_assert(dev); + +		priv = dev->uclass_priv; +		ut_assert(priv); +		ut_asserteq(expected_base_add, priv->base_add); + +		pdata = dev->platdata; +		expected_base_add += pdata->ping_add; +	} + +	return 0; +} +DM_TEST(dm_test_autoprobe, DM_TESTF_SCAN_PDATA); + +/* Check that we see the correct platdata in each device */ +static int dm_test_platdata(struct dm_test_state *dms) +{ +	const struct dm_test_pdata *pdata; +	struct device *dev; +	int i; + +	for (i = 0; i < 3; i++) { +		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); +		ut_assert(dev); +		pdata = dev->platdata; +		ut_assert(pdata->ping_add == test_pdata[i].ping_add); +	} + +	return 0; +} +DM_TEST(dm_test_platdata, DM_TESTF_SCAN_PDATA); + +/* Test that we can bind, probe, remove, unbind a driver */ +static int dm_test_lifecycle(struct dm_test_state *dms) +{ +	int op_count[DM_TEST_OP_COUNT]; +	struct device *dev, *test_dev; +	int pingret; +	int ret; + +	memcpy(op_count, dm_testdrv_op_count, sizeof(op_count)); + +	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, +					&dev)); +	ut_assert(dev); +	ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] +			== op_count[DM_TEST_OP_BIND] + 1); +	ut_assert(!dev->priv); + +	/* Probe the device - it should fail allocating private data */ +	dms->force_fail_alloc = 1; +	ret = device_probe(dev); +	ut_assert(ret == -ENOMEM); +	ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE] +			== op_count[DM_TEST_OP_PROBE] + 1); +	ut_assert(!dev->priv); + +	/* Try again without the alloc failure */ +	dms->force_fail_alloc = 0; +	ut_assertok(device_probe(dev)); +	ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE] +			== op_count[DM_TEST_OP_PROBE] + 2); +	ut_assert(dev->priv); + +	/* This should be device 3 in the uclass */ +	ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); +	ut_assert(dev == test_dev); + +	/* Try ping */ +	ut_assertok(test_ping(dev, 100, &pingret)); +	ut_assert(pingret == 102); + +	/* Now remove device 3 */ +	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]); +	ut_assertok(device_remove(dev)); +	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]); + +	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); +	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]); +	ut_assertok(device_unbind(dev)); +	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); +	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]); + +	return 0; +} +DM_TEST(dm_test_lifecycle, DM_TESTF_SCAN_PDATA | DM_TESTF_PROBE_TEST); + +/* Test that we can bind/unbind and the lists update correctly */ +static int dm_test_ordering(struct dm_test_state *dms) +{ +	struct device *dev, *dev_penultimate, *dev_last, *test_dev; +	int pingret; + +	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, +					&dev)); +	ut_assert(dev); + +	/* Bind two new devices (numbers 4 and 5) */ +	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, +					&dev_penultimate)); +	ut_assert(dev_penultimate); +	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, +					&dev_last)); +	ut_assert(dev_last); + +	/* Now remove device 3 */ +	ut_assertok(device_remove(dev)); +	ut_assertok(device_unbind(dev)); + +	/* The device numbering should have shifted down one */ +	ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); +	ut_assert(dev_penultimate == test_dev); +	ut_assertok(uclass_find_device(UCLASS_TEST, 4, &test_dev)); +	ut_assert(dev_last == test_dev); + +	/* Add back the original device 3, now in position 5 */ +	ut_assertok(device_bind_by_name(dms->root, &driver_info_manual, &dev)); +	ut_assert(dev); + +	/* Try ping */ +	ut_assertok(test_ping(dev, 100, &pingret)); +	ut_assert(pingret == 102); + +	/* Remove 3 and 4 */ +	ut_assertok(device_remove(dev_penultimate)); +	ut_assertok(device_unbind(dev_penultimate)); +	ut_assertok(device_remove(dev_last)); +	ut_assertok(device_unbind(dev_last)); + +	/* Our device should now be in position 3 */ +	ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); +	ut_assert(dev == test_dev); + +	/* Now remove device 3 */ +	ut_assertok(device_remove(dev)); +	ut_assertok(device_unbind(dev)); + +	return 0; +} +DM_TEST(dm_test_ordering, DM_TESTF_SCAN_PDATA); + +/* Check that we can perform operations on a device (do a ping) */ +int dm_check_operations(struct dm_test_state *dms, struct device *dev, +			uint32_t base, struct dm_test_priv *priv) +{ +	int expected; +	int pingret; + +	/* Getting the child device should allocate platdata / priv */ +	ut_assertok(testfdt_ping(dev, 10, &pingret)); +	ut_assert(dev->priv); +	ut_assert(dev->platdata); + +	expected = 10 + base; +	ut_asserteq(expected, pingret); + +	/* Do another ping */ +	ut_assertok(testfdt_ping(dev, 20, &pingret)); +	expected = 20 + base; +	ut_asserteq(expected, pingret); + +	/* Now check the ping_total */ +	priv = dev->priv; +	ut_asserteq(DM_TEST_START_TOTAL + 10 + 20 + base * 2, +		    priv->ping_total); + +	return 0; +} + +/* Check that we can perform operations on devices */ +static int dm_test_operations(struct dm_test_state *dms) +{ +	struct device *dev; +	int i; + +	/* +	 * Now check that the ping adds are what we expect. This is using the +	 * ping-add property in each node. +	 */ +	for (i = 0; i < ARRAY_SIZE(test_pdata); i++) { +		uint32_t base; + +		ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev)); + +		/* +		 * Get the 'reg' property, which tells us what the ping add +		 * should be. We don't use the platdata because we want +		 * to test the code that sets that up (testfdt_drv_probe()). +		 */ +		base = test_pdata[i].ping_add; +		debug("dev=%d, base=%d\n", i, base); + +		ut_assert(!dm_check_operations(dms, dev, base, dev->priv)); +	} + +	return 0; +} +DM_TEST(dm_test_operations, DM_TESTF_SCAN_PDATA); + +/* Remove all drivers and check that things work */ +static int dm_test_remove(struct dm_test_state *dms) +{ +	struct device *dev; +	int i; + +	for (i = 0; i < 3; i++) { +		ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); +		ut_assert(dev); +		ut_assertf(dev->flags & DM_FLAG_ACTIVATED, +			   "Driver %d/%s not activated", i, dev->name); +		ut_assertok(device_remove(dev)); +		ut_assertf(!(dev->flags & DM_FLAG_ACTIVATED), +			   "Driver %d/%s should have deactivated", i, +			   dev->name); +		ut_assert(!dev->priv); +	} + +	return 0; +} +DM_TEST(dm_test_remove, DM_TESTF_SCAN_PDATA | DM_TESTF_PROBE_TEST); + +/* Remove and recreate everything, check for memory leaks */ +static int dm_test_leak(struct dm_test_state *dms) +{ +	int i; + +	for (i = 0; i < 2; i++) { +		struct mallinfo start, end; +		struct device *dev; +		int ret; +		int id; + +		start = mallinfo(); +		if (!start.uordblks) +			puts("Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c\n"); + +		ut_assertok(dm_scan_platdata()); +		ut_assertok(dm_scan_fdt(gd->fdt_blob)); + +		/* Scanning the uclass is enough to probe all the devices */ +		for (id = UCLASS_ROOT; id < UCLASS_COUNT; id++) { +			for (ret = uclass_first_device(UCLASS_TEST, &dev); +			     dev; +			     ret = uclass_next_device(&dev)) +				; +			ut_assertok(ret); +		} + +		/* Don't delete the root class, since we started with that */ +		for (id = UCLASS_ROOT + 1; id < UCLASS_COUNT; id++) { +			struct uclass *uc; + +			uc = uclass_find(id); +			if (!uc) +				continue; +			ut_assertok(uclass_destroy(uc)); +		} + +		end = mallinfo(); +		ut_asserteq(start.uordblks, end.uordblks); +	} + +	return 0; +} +DM_TEST(dm_test_leak, 0); + +/* Test uclass init/destroy methods */ +static int dm_test_uclass(struct dm_test_state *dms) +{ +	struct uclass *uc; + +	ut_assertok(uclass_get(UCLASS_TEST, &uc)); +	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); +	ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); +	ut_assert(uc->priv); + +	ut_assertok(uclass_destroy(uc)); +	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); +	ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); + +	return 0; +} +DM_TEST(dm_test_uclass, 0); + +/** + * create_children() - Create children of a parent node + * + * @dms:	Test system state + * @parent:	Parent device + * @count:	Number of children to create + * @key:	Key value to put in first child. Subsequence children + *		receive an incrementing value + * @child:	If not NULL, then the child device pointers are written into + *		this array. + * @return 0 if OK, -ve on error + */ +static int create_children(struct dm_test_state *dms, struct device *parent, +			   int count, int key, struct device *child[]) +{ +	struct device *dev; +	int i; + +	for (i = 0; i < count; i++) { +		struct dm_test_pdata *pdata; + +		ut_assertok(device_bind_by_name(parent, &driver_info_manual, +						&dev)); +		pdata = calloc(1, sizeof(*pdata)); +		pdata->ping_add = key + i; +		dev->platdata = pdata; +		if (child) +			child[i] = dev; +	} + +	return 0; +} + +#define NODE_COUNT	10 + +static int dm_test_children(struct dm_test_state *dms) +{ +	struct device *top[NODE_COUNT]; +	struct device *child[NODE_COUNT]; +	struct device *grandchild[NODE_COUNT]; +	struct device *dev; +	int total; +	int ret; +	int i; + +	/* We don't care about the numbering for this test */ +	dms->skip_post_probe = 1; + +	ut_assert(NODE_COUNT > 5); + +	/* First create 10 top-level children */ +	ut_assertok(create_children(dms, dms->root, NODE_COUNT, 0, top)); + +	/* Now a few have their own children */ +	ut_assertok(create_children(dms, top[2], NODE_COUNT, 2, NULL)); +	ut_assertok(create_children(dms, top[5], NODE_COUNT, 5, child)); + +	/* And grandchildren */ +	for (i = 0; i < NODE_COUNT; i++) +		ut_assertok(create_children(dms, child[i], NODE_COUNT, 50 * i, +					    i == 2 ? grandchild : NULL)); + +	/* Check total number of devices */ +	total = NODE_COUNT * (3 + NODE_COUNT); +	ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]); + +	/* Try probing one of the grandchildren */ +	ut_assertok(uclass_get_device(UCLASS_TEST, +				      NODE_COUNT * 3 + 2 * NODE_COUNT, &dev)); +	ut_asserteq_ptr(grandchild[0], dev); + +	/* +	 * This should have probed the child and top node also, for a total +	 * of 3 nodes. +	 */ +	ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + +	/* Probe the other grandchildren */ +	for (i = 1; i < NODE_COUNT; i++) +		ut_assertok(device_probe(grandchild[i])); + +	ut_asserteq(2 + NODE_COUNT, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + +	/* Probe everything */ +	for (ret = uclass_first_device(UCLASS_TEST, &dev); +	     dev; +	     ret = uclass_next_device(&dev)) +		; +	ut_assertok(ret); + +	ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + +	/* Remove a top-level child and check that the children are removed */ +	ut_assertok(device_remove(top[2])); +	ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_REMOVE]); +	dm_testdrv_op_count[DM_TEST_OP_REMOVE] = 0; + +	/* Try one with grandchildren */ +	ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev)); +	ut_asserteq_ptr(dev, top[5]); +	ut_assertok(device_remove(dev)); +	ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT), +		    dm_testdrv_op_count[DM_TEST_OP_REMOVE]); + +	/* Try the same with unbind */ +	ut_assertok(device_unbind(top[2])); +	ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); +	dm_testdrv_op_count[DM_TEST_OP_UNBIND] = 0; + +	/* Try one with grandchildren */ +	ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev)); +	ut_asserteq_ptr(dev, top[6]); +	ut_assertok(device_unbind(top[5])); +	ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT), +		    dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + +	return 0; +} +DM_TEST(dm_test_children, 0); diff --git a/test/dm/test-dm.sh b/test/dm/test-dm.sh new file mode 100755 index 000000000..ef5aca5ac --- /dev/null +++ b/test/dm/test-dm.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +NUM_CPUS=$(cat /proc/cpuinfo |grep -c processor) +dtc -I dts -O dtb test/dm/test.dts -o test/dm/test.dtb +make O=sandbox sandbox_config +make O=sandbox -s -j${NUM_CPUS} +./sandbox/u-boot -d test/dm/test.dtb -c "dm test" diff --git a/test/dm/test-driver.c b/test/dm/test-driver.c new file mode 100644 index 000000000..c4be8a12d --- /dev/null +++ b/test/dm/test-driver.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <dm/test.h> +#include <dm/ut.h> +#include <asm/io.h> + +int dm_testdrv_op_count[DM_TEST_OP_COUNT]; +static struct dm_test_state *dms = &global_test_state; + +static int testdrv_ping(struct device *dev, int pingval, int *pingret) +{ +	const struct dm_test_pdata *pdata = dev_get_platdata(dev); +	struct dm_test_priv *priv = dev_get_priv(dev); + +	*pingret = pingval + pdata->ping_add; +	priv->ping_total += *pingret; + +	return 0; +} + +static const struct test_ops test_ops = { +	.ping = testdrv_ping, +}; + +static int test_bind(struct device *dev) +{ +	/* Private data should not be allocated */ +	ut_assert(!dev_get_priv(dev)); + +	dm_testdrv_op_count[DM_TEST_OP_BIND]++; +	return 0; +} + +static int test_probe(struct device *dev) +{ +	struct dm_test_priv *priv = dev_get_priv(dev); + +	/* Private data should be allocated */ +	ut_assert(priv); + +	dm_testdrv_op_count[DM_TEST_OP_PROBE]++; +	priv->ping_total += DM_TEST_START_TOTAL; +	return 0; +} + +static int test_remove(struct device *dev) +{ +	/* Private data should still be allocated */ +	ut_assert(dev_get_priv(dev)); + +	dm_testdrv_op_count[DM_TEST_OP_REMOVE]++; +	return 0; +} + +static int test_unbind(struct device *dev) +{ +	/* Private data should not be allocated */ +	ut_assert(!dev->priv); + +	dm_testdrv_op_count[DM_TEST_OP_UNBIND]++; +	return 0; +} + +U_BOOT_DRIVER(test_drv) = { +	.name	= "test_drv", +	.id	= UCLASS_TEST, +	.ops	= &test_ops, +	.bind	= test_bind, +	.probe	= test_probe, +	.remove	= test_remove, +	.unbind	= test_unbind, +	.priv_auto_alloc_size = sizeof(struct dm_test_priv), +}; + +U_BOOT_DRIVER(test2_drv) = { +	.name	= "test2_drv", +	.id	= UCLASS_TEST, +	.ops	= &test_ops, +	.bind	= test_bind, +	.probe	= test_probe, +	.remove	= test_remove, +	.unbind	= test_unbind, +	.priv_auto_alloc_size = sizeof(struct dm_test_priv), +}; + +static int test_manual_drv_ping(struct device *dev, int pingval, int *pingret) +{ +	*pingret = pingval + 2; + +	return 0; +} + +static const struct test_ops test_manual_ops = { +	.ping = test_manual_drv_ping, +}; + +static int test_manual_bind(struct device *dev) +{ +	dm_testdrv_op_count[DM_TEST_OP_BIND]++; + +	return 0; +} + +static int test_manual_probe(struct device *dev) +{ +	dm_testdrv_op_count[DM_TEST_OP_PROBE]++; +	if (!dms->force_fail_alloc) +		dev->priv = calloc(1, sizeof(struct dm_test_priv)); +	if (!dev->priv) +		return -ENOMEM; + +	return 0; +} + +static int test_manual_remove(struct device *dev) +{ +	dm_testdrv_op_count[DM_TEST_OP_REMOVE]++; +	return 0; +} + +static int test_manual_unbind(struct device *dev) +{ +	dm_testdrv_op_count[DM_TEST_OP_UNBIND]++; +	return 0; +} + +U_BOOT_DRIVER(test_manual_drv) = { +	.name	= "test_manual_drv", +	.id	= UCLASS_TEST, +	.ops	= &test_manual_ops, +	.bind	= test_manual_bind, +	.probe	= test_manual_probe, +	.remove	= test_manual_remove, +	.unbind	= test_manual_unbind, +}; diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c new file mode 100644 index 000000000..e1d982fd7 --- /dev/null +++ b/test/dm/test-fdt.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <malloc.h> +#include <asm/io.h> +#include <dm/test.h> +#include <dm/root.h> +#include <dm/ut.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int testfdt_drv_ping(struct device *dev, int pingval, int *pingret) +{ +	const struct dm_test_pdata *pdata = dev->platdata; +	struct dm_test_priv *priv = dev_get_priv(dev); + +	*pingret = pingval + pdata->ping_add; +	priv->ping_total += *pingret; + +	return 0; +} + +static const struct test_ops test_ops = { +	.ping = testfdt_drv_ping, +}; + +static int testfdt_ofdata_to_platdata(struct device *dev) +{ +	struct dm_test_pdata *pdata = dev_get_platdata(dev); + +	pdata->ping_add = fdtdec_get_int(gd->fdt_blob, dev->of_offset, +					"ping-add", -1); +	pdata->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + +	return 0; +} + +static int testfdt_drv_probe(struct device *dev) +{ +	struct dm_test_priv *priv = dev_get_priv(dev); + +	priv->ping_total += DM_TEST_START_TOTAL; + +	return 0; +} + +static const struct device_id testfdt_ids[] = { +	{ +		.compatible = "denx,u-boot-fdt-test", +		.data = DM_TEST_TYPE_FIRST }, +	{ +		.compatible = "google,another-fdt-test", +		.data = DM_TEST_TYPE_SECOND }, +	{ } +}; + +U_BOOT_DRIVER(testfdt_drv) = { +	.name	= "testfdt_drv", +	.of_match	= testfdt_ids, +	.id	= UCLASS_TEST_FDT, +	.ofdata_to_platdata = testfdt_ofdata_to_platdata, +	.probe	= testfdt_drv_probe, +	.ops	= &test_ops, +	.priv_auto_alloc_size = sizeof(struct dm_test_priv), +	.platdata_auto_alloc_size = sizeof(struct dm_test_pdata), +}; + +/* From here is the testfdt uclass code */ +int testfdt_ping(struct device *dev, int pingval, int *pingret) +{ +	const struct test_ops *ops = device_get_ops(dev); + +	if (!ops->ping) +		return -ENOSYS; + +	return ops->ping(dev, pingval, pingret); +} + +UCLASS_DRIVER(testfdt) = { +	.name		= "testfdt", +	.id		= UCLASS_TEST_FDT, +}; + +/* Test that FDT-based binding works correctly */ +static int dm_test_fdt(struct dm_test_state *dms) +{ +	const int num_drivers = 3; +	struct device *dev; +	struct uclass *uc; +	int ret; +	int i; + +	ret = dm_scan_fdt(gd->fdt_blob); +	ut_assert(!ret); + +	ret = uclass_get(UCLASS_TEST_FDT, &uc); +	ut_assert(!ret); + +	/* These are num_drivers compatible root-level device tree nodes */ +	ut_asserteq(num_drivers, list_count_items(&uc->dev_head)); + +	/* Each should have no platdata / priv */ +	for (i = 0; i < num_drivers; i++) { +		ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev); +		ut_assert(!ret); +		ut_assert(!dev_get_priv(dev)); +		ut_assert(!dev->platdata); +	} + +	/* +	 * Now check that the ping adds are what we expect. This is using the +	 * ping-add property in each node. +	 */ +	for (i = 0; i < num_drivers; i++) { +		uint32_t base; + +		ret = uclass_get_device(UCLASS_TEST_FDT, i, &dev); +		ut_assert(!ret); + +		/* +		 * Get the 'reg' property, which tells us what the ping add +		 * should be. We don't use the platdata because we want +		 * to test the code that sets that up (testfdt_drv_probe()). +		 */ +		base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); +		debug("dev=%d, base=%d: %s\n", i, base, +		      fdt_get_name(gd->fdt_blob, dev->of_offset, NULL)); + +		ut_assert(!dm_check_operations(dms, dev, base, +					       dev_get_priv(dev))); +	} + +	return 0; +} +DM_TEST(dm_test_fdt, 0); diff --git a/test/dm/test-main.c b/test/dm/test-main.c new file mode 100644 index 000000000..828ed46f8 --- /dev/null +++ b/test/dm/test-main.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <dm/test.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> +#include <dm/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dm_test_state global_test_state; + +/* Get ready for testing */ +static int dm_test_init(struct dm_test_state *dms) +{ +	memset(dms, '\0', sizeof(*dms)); +	gd->dm_root = NULL; +	memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count)); + +	ut_assertok(dm_init()); +	dms->root = dm_root(); + +	return 0; +} + +/* Ensure all the test devices are probed */ +static int do_autoprobe(struct dm_test_state *dms) +{ +	struct device *dev; +	int ret; + +	/* Scanning the uclass is enough to probe all the devices */ +	for (ret = uclass_first_device(UCLASS_TEST, &dev); +	     dev; +	     ret = uclass_next_device(&dev)) +		; + +	return ret; +} + +static int dm_test_destroy(struct dm_test_state *dms) +{ +	int id; + +	for (id = 0; id < UCLASS_COUNT; id++) { +		struct uclass *uc; + +		/* +		 * If the uclass doesn't exist we don't want to create it. So +		 * check that here before we call uclass_find_device()/ +		 */ +		uc = uclass_find(id); +		if (!uc) +			continue; +		ut_assertok(uclass_destroy(uc)); +	} + +	return 0; +} + +int dm_test_main(void) +{ +	struct dm_test *tests = ll_entry_start(struct dm_test, dm_test); +	const int n_ents = ll_entry_count(struct dm_test, dm_test); +	struct dm_test_state *dms = &global_test_state; +	struct dm_test *test; + +	/* +	 * If we have no device tree, or it only has a root node, then these +	 * tests clearly aren't going to work... +	 */ +	if (!gd->fdt_blob || fdt_next_node(gd->fdt_blob, 0, NULL) < 0) { +		puts("Please run with test device tree:\n" +		     "     dtc -I dts -O dtb test/dm/test.dts  -o test/dm/test.dtb\n" +		     "    ./u-boot -d test/dm/test.dtb\n"); +		ut_assert(gd->fdt_blob); +	} + +	printf("Running %d driver model tests\n", n_ents); + +	for (test = tests; test < tests + n_ents; test++) { +		printf("Test: %s\n", test->name); +		ut_assertok(dm_test_init(dms)); + +		if (test->flags & DM_TESTF_SCAN_PDATA) +			ut_assertok(dm_scan_platdata()); +		if (test->flags & DM_TESTF_PROBE_TEST) +			ut_assertok(do_autoprobe(dms)); +		if (test->flags & DM_TESTF_SCAN_FDT) +			ut_assertok(dm_scan_fdt(gd->fdt_blob)); + +		if (test->func(dms)) +			break; + +		ut_assertok(dm_test_destroy(dms)); +	} + +	printf("Failures: %d\n", dms->fail_count); + +	return 0; +} diff --git a/test/dm/test-uclass.c b/test/dm/test-uclass.c new file mode 100644 index 000000000..8b564b89d --- /dev/null +++ b/test/dm/test-uclass.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <dm.h> +#include <errno.h> +#include <dm/test.h> +#include <dm/ut.h> +#include <asm/io.h> +#include <linux/list.h> + +static struct dm_test_state *dms = &global_test_state; + +int test_ping(struct device *dev, int pingval, int *pingret) +{ +	const struct test_ops *ops = device_get_ops(dev); + +	if (!ops->ping) +		return -ENOSYS; + +	return ops->ping(dev, pingval, pingret); +} + +static int test_post_bind(struct device *dev) +{ +	dm_testdrv_op_count[DM_TEST_OP_POST_BIND]++; + +	return 0; +} + +static int test_pre_unbind(struct device *dev) +{ +	dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]++; + +	return 0; +} + +static int test_post_probe(struct device *dev) +{ +	struct device *prev = list_entry(dev->uclass_node.prev, struct device, +					 uclass_node); +	struct dm_test_uclass_perdev_priv *priv = dev->uclass_priv; +	struct uclass *uc = dev->uclass; + +	dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]++; +	ut_assert(priv); +	ut_assert(device_active(dev)); +	priv->base_add = 0; +	if (dms->skip_post_probe) +		return 0; +	if (&prev->uclass_node != &uc->dev_head) { +		struct dm_test_uclass_perdev_priv *prev_uc_priv +				= prev->uclass_priv; +		struct dm_test_pdata *pdata = prev->platdata; + +		ut_assert(pdata); +		ut_assert(prev_uc_priv); +		priv->base_add = prev_uc_priv->base_add + pdata->ping_add; +	} + +	return 0; +} + +static int test_pre_remove(struct device *dev) +{ +	dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]++; + +	return 0; +} + +static int test_init(struct uclass *uc) +{ +	dm_testdrv_op_count[DM_TEST_OP_INIT]++; +	ut_assert(uc->priv); + +	return 0; +} + +static int test_destroy(struct uclass *uc) +{ +	dm_testdrv_op_count[DM_TEST_OP_DESTROY]++; + +	return 0; +} + +UCLASS_DRIVER(test) = { +	.name		= "test", +	.id		= UCLASS_TEST, +	.post_bind	= test_post_bind, +	.pre_unbind	= test_pre_unbind, +	.post_probe	= test_post_probe, +	.pre_remove	= test_pre_remove, +	.init		= test_init, +	.destroy	= test_destroy, +	.priv_auto_alloc_size	= sizeof(struct dm_test_uclass_priv), +	.per_device_auto_alloc_size = sizeof(struct dm_test_uclass_perdev_priv), +}; diff --git a/test/dm/test.dts b/test/dm/test.dts new file mode 100644 index 000000000..ec5364f7c --- /dev/null +++ b/test/dm/test.dts @@ -0,0 +1,59 @@ +/dts-v1/; + +/ { +	model = "sandbox"; +	compatible = "sandbox"; +	#address-cells = <1>; +	#size-cells = <0>; + +	a-test { +		reg = <0>; +		compatible = "denx,u-boot-fdt-test"; +		ping-add = <0>; +	}; + +	junk { +		reg = <1>; +		compatible = "not,compatible"; +	}; + +	no-compatible { +		reg = <2>; +	}; + +	b-test { +		reg = <3>; +		compatible = "denx,u-boot-fdt-test"; +		ping-add = <3>; +	}; + +	some-bus { +		#address-cells = <1>; +		#size-cells = <0>; +		reg = <4>; +		ping-add = <4>; +		c-test { +			compatible = "denx,u-boot-fdt-test"; +			reg = <5>; +			ping-add = <5>; +		}; +	}; + +	d-test { +		reg = <6>; +		ping-add = <6>; +		compatible = "google,another-fdt-test"; +	}; + +	base-gpios { +		compatible = "sandbox,gpio"; +		gpio-bank-name = "a"; +		num-gpios = <20>; +	}; + +	extra-gpios { +		compatible = "sandbox,gpio"; +		gpio-bank-name = "b"; +		num-gpios = <10>; +	}; +}; diff --git a/test/dm/ut.c b/test/dm/ut.c new file mode 100644 index 000000000..8b69bc2ab --- /dev/null +++ b/test/dm/ut.c @@ -0,0 +1,33 @@ +/* + * Simple unit test library for driver model + * + * Copyright (c) 2013 Google, Inc + * + * SPDX-License-Identifier:	GPL-2.0+ + */ + +#include <common.h> +#include <dm/test.h> +#include <dm/ut.h> + +struct dm_test_state; + +void ut_fail(struct dm_test_state *dms, const char *fname, int line, +	     const char *func, const char *cond) +{ +	printf("%s:%d, %s(): %s\n", fname, line, func, cond); +	dms->fail_count++; +} + +void ut_failf(struct dm_test_state *dms, const char *fname, int line, +	      const char *func, const char *cond, const char *fmt, ...) +{ +	va_list args; + +	printf("%s:%d, %s(): %s: ", fname, line, func, cond); +	va_start(args, fmt); +	vprintf(fmt, args); +	va_end(args); +	putc('\n'); +	dms->fail_count++; +} |