From patchwork Wed Jun 19 03:52:33 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 252472 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 8869D2C0092 for ; Wed, 19 Jun 2013 13:54:47 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id EDE5B4A01F; Wed, 19 Jun 2013 05:54:34 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 6abTjR8B4w4w; Wed, 19 Jun 2013 05:54:34 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2D8564A028; Wed, 19 Jun 2013 05:53:37 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 549434A04C for ; Wed, 19 Jun 2013 05:53:31 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id BCaKFu9xEYzM for ; Wed, 19 Jun 2013 05:53:26 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-qc0-f201.google.com (mail-qc0-f201.google.com [209.85.216.201]) by theia.denx.de (Postfix) with ESMTPS id 1ACB24A021 for ; Wed, 19 Jun 2013 05:53:05 +0200 (CEST) Received: by mail-qc0-f201.google.com with SMTP id u11so550443qcx.2 for ; Tue, 18 Jun 2013 20:53:04 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-gm-message-state; bh=IqykMni8Sv71hZ/vrwhVRhsnfDfl/cAdoxNPICfXSxo=; b=iRTVGR2q/yXtaklcBCh17oZe+UfXPYVee3O+qR9x8ik6j+9lXRn4K8Ic6X+YS1VVCH AtdeYGb5M9jamlvnU1M1Wcu5YFHV37quhA/J9VRkvCq87oYEBPXnySI6pOg28asiFjxv w8ShuXFMUk5qcBdZprzODfEQgKfbWln41ltGWPTAWMOVFW/zABEpNdMFbXj1bj5ykPBn +j09+xa4OvghS2UOb1wqvOZLJ/7DQjWf7q5jJZtYpdg1Mt/JTOvfW60ZbIUWCDp4YQSv n65/rwf5S1yKeiO7WO5KbjZ1nOyKi9yb4ZODBMBdKi72xF36zLx7FXFi2uNt2Z3GVvXf mp4w== X-Received: by 10.236.135.66 with SMTP id t42mr556536yhi.36.1371613983976; Tue, 18 Jun 2013 20:53:03 -0700 (PDT) Received: from corp2gmr1-1.hot.corp.google.com (corp2gmr1-1.hot.corp.google.com [172.24.189.92]) by gmr-mx.google.com with ESMTPS id t25si183940yhg.6.2013.06.18.20.53.03 for (version=TLSv1.1 cipher=AES128-SHA bits=128/128); Tue, 18 Jun 2013 20:53:03 -0700 (PDT) Received: from kaka.mtv.corp.google.com (kaka.mtv.corp.google.com [172.22.83.1]) by corp2gmr1-1.hot.corp.google.com (Postfix) with ESMTP id 9782531C1EF; Tue, 18 Jun 2013 20:53:03 -0700 (PDT) Received: by kaka.mtv.corp.google.com (Postfix, from userid 121222) id 738B216099F; Tue, 18 Jun 2013 20:53:03 -0700 (PDT) From: Simon Glass To: U-Boot Mailing List Date: Tue, 18 Jun 2013 20:52:33 -0700 Message-Id: <1371613960-28678-10-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 1.8.3 In-Reply-To: <1371613960-28678-1-git-send-email-sjg@chromium.org> References: <1371613960-28678-1-git-send-email-sjg@chromium.org> X-Gm-Message-State: ALoCoQlBFSnziayoLoiu7/2WpP7YoHwOfb3AjPWU59pZ7Qbbd3/RmyKrriN7VXiInUO0YmvXF+kGqa8pCYQX/WB0UCW1NFWgZgPKd8EeYgVDrIWr1WeMnH0jofoKQVirbbk0RiOZGnvxm14el/ji1kjF3IEcgd5AEidIk8NwB86edGV8Kj8VcNajxJ9Fmoj2p+3J1q53mfXm Cc: Tom Rini , u-boot-review@google.com Subject: [U-Boot] [PATCH v3 09/16] dm: Add basic tests X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de 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 --- Changes in v3: - Add function/struct comments to tests Changes in v2: None Makefile | 1 + include/configs/sandbox.h | 1 + include/dm/test.h | 180 +++++++++++++++ include/dm/ut.h | 108 +++++++++ test/dm/Makefile | 52 +++++ test/dm/core.c | 557 ++++++++++++++++++++++++++++++++++++++++++++++ test/dm/test-driver.c | 159 +++++++++++++ test/dm/test-fdt.c | 148 ++++++++++++ test/dm/test-main.c | 120 ++++++++++ test/dm/test-uclass.c | 117 ++++++++++ test/dm/test.dts | 59 +++++ test/dm/ut.c | 46 ++++ 12 files changed, 1548 insertions(+) create mode 100644 include/dm/test.h create mode 100644 include/dm/ut.h create mode 100644 test/dm/Makefile create mode 100644 test/dm/core.c create mode 100644 test/dm/test-driver.c create mode 100644 test/dm/test-fdt.c create mode 100644 test/dm/test-main.c create mode 100644 test/dm/test-uclass.c create mode 100644 test/dm/test.dts create mode 100644 test/dm/ut.c diff --git a/Makefile b/Makefile index c1c13c8..0655c9c 100644 --- a/Makefile +++ b/Makefile @@ -336,6 +336,7 @@ LIBS-y += lib/libfdt/libfdt.o LIBS-y += api/libapi.o LIBS-y += post/libpost.o LIBS-y += test/libtest.o +LIBS-y += test/dm/libtestdm.o LIBS-$(CONFIG_DM) += common/dm/libdm.o diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 162c26c..38e0384 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -23,6 +23,7 @@ #define __CONFIG_H #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 0000000..fa514d8 --- /dev/null +++ b/include/dm/test.h @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013 Google, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __DM_TEST_H +#define __DM_TEST_H + +#include + +/** + * 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 0000000..aa849a7 --- /dev/null +++ b/include/dm/ut.h @@ -0,0 +1,108 @@ +/* + * Simple unit test library for driver model + * + * Copyright (c) 2013 Google, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#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/Makefile b/test/dm/Makefile new file mode 100644 index 0000000..310aa05 --- /dev/null +++ b/test/dm/Makefile @@ -0,0 +1,52 @@ +# +# Copyright (c) 2013 Google, Inc +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)libtestdm.o + +COBJS-$(CONFIG_DM_TEST) += test-driver.o +COBJS-$(CONFIG_DM_TEST) += test-fdt.o +COBJS-$(CONFIG_DM_TEST) += test-main.o +COBJS-$(CONFIG_DM_TEST) += test-uclass.o +COBJS-$(CONFIG_DM_TEST) += ut.o + +# Tests for particular subsystems - when enabling driver model for a new +# subsystem you must add sandbox tests here. +COBJS-$(CONFIG_DM_TEST) += core.o +COBJS-$(CONFIG_DM_TEST) += ut.o +COBJS-$(CONFIG_DM_GPIO) += gpio.o + +COBJS := $(sort $(COBJS-y)) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) $(XOBJS) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/test/dm/core.c b/test/dm/core.c new file mode 100644 index 0000000..c5984f8 --- /dev/null +++ b/test/dm/core.c @@ -0,0 +1,557 @@ +/* + * Tests for the core driver model code + * + * Copyright (c) 2013 Google, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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", + .platform_data = &test_pdata[0], +}; + +U_BOOT_DEVICE(dm_test_info2) = { + .name = "test_drv", + .platform_data = &test_pdata[1], +}; + +U_BOOT_DEVICE(dm_test_info3) = { + .name = "test_drv", + .platform_data = &test_pdata[2], +}; + +static struct driver_info driver_info_manual = { + .name = "test_manual_drv", + .platform_data = &test_pdata_manual, +}; + +/* Test that binding with platform_data 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_platform_data()); + + /* 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->platform_data; + expected_base_add += pdata->ping_add; + } + + return 0; +} +DM_TEST(dm_test_autoprobe, DM_TESTF_SCAN_PDATA); + +/* Check that we see the correct platform_data in each device */ +static int dm_test_platform_data(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->platform_data; + ut_assert(pdata->ping_add == test_pdata[i].ping_add); + } + + return 0; +} +DM_TEST(dm_test_platform_data, 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 platform_data / priv */ + ut_assertok(testfdt_ping(dev, 10, &pingret)); + ut_assert(dev->priv); + ut_assert(dev->platform_data); + + 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 platform_data 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_platform_data()); + 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->platform_data = 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-driver.c b/test/dm/test-driver.c new file mode 100644 index 0000000..3864b31 --- /dev/null +++ b/test/dm/test-driver.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +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->platform_data; + struct dm_test_priv *priv = dev->priv; + + *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->priv); + + dm_testdrv_op_count[DM_TEST_OP_BIND]++; + return 0; +} + +static int test_probe(struct device *dev) +{ + struct dm_test_priv *priv = dev->priv; + + /* 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->priv); + + 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 0000000..33ba599 --- /dev/null +++ b/test/dm/test-fdt.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +static int testfdt_drv_ping(struct device *dev, int pingval, int *pingret) +{ + const struct dm_test_pdata *pdata = dev->platform_data; + struct dm_test_priv *priv = dev->priv; + + *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_drv_probe(struct device *dev) +{ + struct dm_test_pdata *pdata = dev->platform_data; + struct dm_test_priv *priv = dev->priv; + + 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"); + 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, + .probe = testfdt_drv_probe, + .ops = &test_ops, + .priv_auto_alloc_size = sizeof(struct dm_test_priv), + .platform_data_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 platform_data / priv */ + for (i = 0; i < num_drivers; i++) { + ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev); + ut_assert(!ret); + ut_assert(!dev->priv); + ut_assert(!dev->platform_data); + } + + /* + * 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 platform_data 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->priv)); + } + + 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 0000000..8d4ef01 --- /dev/null +++ b/test/dm/test-main.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +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/out.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_platform_data()); + 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 0000000..fb1ecc4 --- /dev/null +++ b/test/dm/test-uclass.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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->platform_data; + + 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 0000000..ec5364f --- /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 0000000..9b12a0c --- /dev/null +++ b/test/dm/ut.c @@ -0,0 +1,46 @@ +/* + * Simple unit test library for driver model + * + * Copyright (c) 2013 Google, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include + +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++; +}