diff options
| author | Simon Glass <sjg@chromium.org> | 2014-02-26 15:59:21 -0700 | 
|---|---|---|
| committer | Tom Rini <trini@ti.com> | 2014-03-04 12:15:29 -0500 | 
| commit | 2e7d35d2a60339cfa54e26a07326bc75e1060bb3 (patch) | |
| tree | 0c49d6f7fb9bdaf9cb2b553e6cbd839c7c837ed5 /test | |
| parent | 1ce60176799ae04d508b14e9caa7f3bd3a170f0f (diff) | |
| download | olio-uboot-2014.01-2e7d35d2a60339cfa54e26a07326bc75e1060bb3.tar.xz olio-uboot-2014.01-2e7d35d2a60339cfa54e26a07326bc75e1060bb3.zip | |
dm: Add basic tests
Add some tests of driver model functionality. Coverage includes:
- basic init
- binding of drivers to devices using platform_data
- automatic probing of devices when referenced
- availability of platform data to devices
- lifecycle from bind to probe to remove to unbind
- renumbering within a uclass when devices are probed/removed
- calling driver-defined operations
- deactivation of drivers when removed
- memory leak across creation and destruction of drivers/uclasses
- uclass init/destroy methods
- automatic probe/remove of children/parents when needed
This function is enabled for sandbox, using CONFIG_DM_TEST.
Signed-off-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'test')
| -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 | 
10 files changed, 1162 insertions, 0 deletions
| 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++; +} |