+// SPDX-License-Identifier: GPL-2.0+
/*
* Tests for the core driver model code
*
* Copyright (c) 2013 Google, Inc
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.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>
+#include <test/ut.h>
DECLARE_GLOBAL_DATA_PTR;
static struct driver_info driver_info_pre_reloc = {
.name = "test_pre_reloc_drv",
- .platdata = &test_pdata_manual,
+ .platdata = &test_pdata_pre_reloc,
+};
+
+static struct driver_info driver_info_act_dma = {
+ .name = "test_act_dma_drv",
};
-void dm_leak_check_start(struct dm_test_state *dms)
+void dm_leak_check_start(struct unit_test_state *uts)
{
- dms->start = mallinfo();
- if (!dms->start.uordblks)
+ uts->start = mallinfo();
+ if (!uts->start.uordblks)
puts("Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c\n");
}
-int dm_leak_check_end(struct dm_test_state *dms)
+int dm_leak_check_end(struct unit_test_state *uts)
{
struct mallinfo end;
- int id;
+ int id, diff;
/* Don't delete the root class, since we started with that */
for (id = UCLASS_ROOT + 1; id < UCLASS_COUNT; id++) {
}
end = mallinfo();
- ut_asserteq(dms->start.uordblks, end.uordblks);
+ diff = end.uordblks - uts->start.uordblks;
+ if (diff > 0)
+ printf("Leak: lost %#xd bytes\n", diff);
+ else if (diff < 0)
+ printf("Leak: gained %#xd bytes\n", -diff);
+ ut_asserteq(uts->start.uordblks, end.uordblks);
return 0;
}
/* Test that binding with platdata occurs correctly */
-static int dm_test_autobind(struct dm_test_state *dms)
+static int dm_test_autobind(struct unit_test_state *uts)
{
+ struct dm_test_state *dms = uts->priv;
struct udevice *dev;
/*
DM_TEST(dm_test_autobind, 0);
/* Test that binding with uclass platdata allocation occurs correctly */
-static int dm_test_autobind_uclass_pdata_alloc(struct dm_test_state *dms)
+static int dm_test_autobind_uclass_pdata_alloc(struct unit_test_state *uts)
{
struct dm_test_perdev_uc_pdata *uc_pdata;
struct udevice *dev;
DM_TEST(dm_test_autobind_uclass_pdata_alloc, DM_TESTF_SCAN_PDATA);
/* Test that binding with uclass platdata setting occurs correctly */
-static int dm_test_autobind_uclass_pdata_valid(struct dm_test_state *dms)
+static int dm_test_autobind_uclass_pdata_valid(struct unit_test_state *uts)
{
struct dm_test_perdev_uc_pdata *uc_pdata;
struct udevice *dev;
DM_TEST(dm_test_autobind_uclass_pdata_valid, DM_TESTF_SCAN_PDATA);
/* Test that autoprobe finds all the expected devices */
-static int dm_test_autoprobe(struct dm_test_state *dms)
+static int dm_test_autoprobe(struct unit_test_state *uts)
{
+ struct dm_test_state *dms = uts->priv;
int expected_base_add;
struct udevice *dev;
struct uclass *uc;
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)
+static int dm_test_platdata(struct unit_test_state *uts)
{
const struct dm_test_pdata *pdata;
struct udevice *dev;
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)
+static int dm_test_lifecycle(struct unit_test_state *uts)
{
+ struct dm_test_state *dms = uts->priv;
int op_count[DM_TEST_OP_COUNT];
struct udevice *dev, *test_dev;
int pingret;
/* Now remove device 3 */
ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]);
- ut_assertok(device_remove(dev));
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]);
ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
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)
+static int dm_test_ordering(struct unit_test_state *uts)
{
+ struct dm_test_state *dms = uts->priv;
struct udevice *dev, *dev_penultimate, *dev_last, *test_dev;
int pingret;
ut_assert(dev_last);
/* Now remove device 3 */
- ut_assertok(device_remove(dev));
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
ut_assertok(device_unbind(dev));
/* The device numbering should have shifted down one */
ut_assert(pingret == 102);
/* Remove 3 and 4 */
- ut_assertok(device_remove(dev_penultimate));
+ ut_assertok(device_remove(dev_penultimate, DM_REMOVE_NORMAL));
ut_assertok(device_unbind(dev_penultimate));
- ut_assertok(device_remove(dev_last));
+ ut_assertok(device_remove(dev_last, DM_REMOVE_NORMAL));
ut_assertok(device_unbind(dev_last));
/* Our device should now be in position 3 */
ut_assert(dev == test_dev);
/* Now remove device 3 */
- ut_assertok(device_remove(dev));
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
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 udevice *dev,
+int dm_check_operations(struct unit_test_state *uts, struct udevice *dev,
uint32_t base, struct dm_test_priv *priv)
{
int expected;
}
/* Check that we can perform operations on devices */
-static int dm_test_operations(struct dm_test_state *dms)
+static int dm_test_operations(struct unit_test_state *uts)
{
struct udevice *dev;
int i;
base = test_pdata[i].ping_add;
debug("dev=%d, base=%d\n", i, base);
- ut_assert(!dm_check_operations(dms, dev, base, dev->priv));
+ ut_assert(!dm_check_operations(uts, 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)
+static int dm_test_remove(struct unit_test_state *uts)
{
struct udevice *dev;
int i;
ut_assert(dev);
ut_assertf(dev->flags & DM_FLAG_ACTIVATED,
"Driver %d/%s not activated", i, dev->name);
- ut_assertok(device_remove(dev));
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
ut_assertf(!(dev->flags & DM_FLAG_ACTIVATED),
"Driver %d/%s should have deactivated", i,
dev->name);
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)
+static int dm_test_leak(struct unit_test_state *uts)
{
int i;
int ret;
int id;
- dm_leak_check_start(dms);
+ dm_leak_check_start(uts);
ut_assertok(dm_scan_platdata(false));
ut_assertok(dm_scan_fdt(gd->fdt_blob, false));
ut_assertok(ret);
}
- ut_assertok(dm_leak_check_end(dms));
+ ut_assertok(dm_leak_check_end(uts));
}
return 0;
DM_TEST(dm_test_leak, 0);
/* Test uclass init/destroy methods */
-static int dm_test_uclass(struct dm_test_state *dms)
+static int dm_test_uclass(struct unit_test_state *uts)
{
struct uclass *uc;
* this array.
* @return 0 if OK, -ve on error
*/
-static int create_children(struct dm_test_state *dms, struct udevice *parent,
+static int create_children(struct unit_test_state *uts, struct udevice *parent,
int count, int key, struct udevice *child[])
{
struct udevice *dev;
#define NODE_COUNT 10
-static int dm_test_children(struct dm_test_state *dms)
+static int dm_test_children(struct unit_test_state *uts)
{
+ struct dm_test_state *dms = uts->priv;
struct udevice *top[NODE_COUNT];
struct udevice *child[NODE_COUNT];
struct udevice *grandchild[NODE_COUNT];
ut_assert(NODE_COUNT > 5);
/* First create 10 top-level children */
- ut_assertok(create_children(dms, dms->root, NODE_COUNT, 0, top));
+ ut_assertok(create_children(uts, 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));
+ ut_assertok(create_children(uts, top[2], NODE_COUNT, 2, NULL));
+ ut_assertok(create_children(uts, 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,
+ ut_assertok(create_children(uts, child[i], NODE_COUNT, 50 * i,
i == 2 ? grandchild : NULL));
/* Check total number of devices */
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_assertok(device_remove(top[2], DM_REMOVE_NORMAL));
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_assertok(device_remove(dev, DM_REMOVE_NORMAL));
ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT),
dm_testdrv_op_count[DM_TEST_OP_REMOVE]);
DM_TEST(dm_test_children, 0);
/* Test that pre-relocation devices work as expected */
-static int dm_test_pre_reloc(struct dm_test_state *dms)
+static int dm_test_pre_reloc(struct unit_test_state *uts)
{
+ struct dm_test_state *dms = uts->priv;
struct udevice *dev;
/* The normal driver should refuse to bind before relocation */
}
DM_TEST(dm_test_pre_reloc, 0);
-static int dm_test_uclass_before_ready(struct dm_test_state *dms)
+/*
+ * Test that removal of devices, either via the "normal" device_remove()
+ * API or via the device driver selective flag works as expected
+ */
+static int dm_test_remove_active_dma(struct unit_test_state *uts)
+{
+ struct dm_test_state *dms = uts->priv;
+ struct udevice *dev;
+
+ ut_assertok(device_bind_by_name(dms->root, false, &driver_info_act_dma,
+ &dev));
+ ut_assert(dev);
+
+ /* Probe the device */
+ ut_assertok(device_probe(dev));
+
+ /* Test if device is active right now */
+ ut_asserteq(true, device_active(dev));
+
+ /* Remove the device via selective remove flag */
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+
+ /* Test if device is inactive right now */
+ ut_asserteq(false, device_active(dev));
+
+ /* Probe the device again */
+ ut_assertok(device_probe(dev));
+
+ /* Test if device is active right now */
+ ut_asserteq(true, device_active(dev));
+
+ /* Remove the device via "normal" remove API */
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+
+ /* Test if device is inactive right now */
+ ut_asserteq(false, device_active(dev));
+
+ /*
+ * Test if a device without the active DMA flags is not removed upon
+ * the active DMA remove call
+ */
+ ut_assertok(device_unbind(dev));
+ ut_assertok(device_bind_by_name(dms->root, false, &driver_info_manual,
+ &dev));
+ ut_assert(dev);
+
+ /* Probe the device */
+ ut_assertok(device_probe(dev));
+
+ /* Test if device is active right now */
+ ut_asserteq(true, device_active(dev));
+
+ /* Remove the device via selective remove flag */
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+
+ /* Test if device is still active right now */
+ ut_asserteq(true, device_active(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_remove_active_dma, 0);
+
+static int dm_test_uclass_before_ready(struct unit_test_state *uts)
{
struct uclass *uc;
}
DM_TEST(dm_test_uclass_before_ready, 0);
-static int dm_test_uclass_devices_find(struct dm_test_state *dms)
+static int dm_test_uclass_devices_find(struct unit_test_state *uts)
{
struct udevice *dev;
int ret;
ut_assert(dev);
}
+ ret = uclass_find_first_device(UCLASS_TEST_DUMMY, &dev);
+ ut_assert(ret == -ENODEV);
+ ut_assert(!dev);
+
return 0;
}
DM_TEST(dm_test_uclass_devices_find, DM_TESTF_SCAN_PDATA);
-static int dm_test_uclass_devices_find_by_name(struct dm_test_state *dms)
+static int dm_test_uclass_devices_find_by_name(struct unit_test_state *uts)
{
struct udevice *finddev;
struct udevice *testdev;
}
DM_TEST(dm_test_uclass_devices_find_by_name, DM_TESTF_SCAN_FDT);
-static int dm_test_uclass_devices_get(struct dm_test_state *dms)
+static int dm_test_uclass_devices_get(struct unit_test_state *uts)
{
struct udevice *dev;
int ret;
}
DM_TEST(dm_test_uclass_devices_get, DM_TESTF_SCAN_PDATA);
-static int dm_test_uclass_devices_get_by_name(struct dm_test_state *dms)
+static int dm_test_uclass_devices_get_by_name(struct unit_test_state *uts)
{
struct udevice *finddev;
struct udevice *testdev;
}
DM_TEST(dm_test_uclass_devices_get_by_name, DM_TESTF_SCAN_FDT);
-static int dm_test_device_get_uclass_id(struct dm_test_state *dms)
+static int dm_test_device_get_uclass_id(struct unit_test_state *uts)
{
struct udevice *dev;
return 0;
}
DM_TEST(dm_test_device_get_uclass_id, DM_TESTF_SCAN_PDATA);
+
+static int dm_test_uclass_names(struct unit_test_state *uts)
+{
+ ut_asserteq_str("test", uclass_get_name(UCLASS_TEST));
+ ut_asserteq(UCLASS_TEST, uclass_get_by_name("test"));
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_names, DM_TESTF_SCAN_PDATA);
+
+static int dm_test_inactive_child(struct unit_test_state *uts)
+{
+ struct dm_test_state *dms = uts->priv;
+ struct udevice *parent, *dev1, *dev2;
+
+ /* Skip the behaviour in test_post_probe() */
+ dms->skip_post_probe = 1;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST, &parent));
+
+ /*
+ * Create a child but do not activate it. Calling the function again
+ * should return the same child.
+ */
+ ut_asserteq(-ENODEV, device_find_first_inactive_child(parent,
+ UCLASS_TEST, &dev1));
+ ut_assertok(device_bind_ofnode(parent, DM_GET_DRIVER(test_drv),
+ "test_child", 0, ofnode_null(), &dev1));
+
+ ut_assertok(device_find_first_inactive_child(parent, UCLASS_TEST,
+ &dev2));
+ ut_asserteq_ptr(dev1, dev2);
+
+ ut_assertok(device_probe(dev1));
+ ut_asserteq(-ENODEV, device_find_first_inactive_child(parent,
+ UCLASS_TEST, &dev2));
+
+ return 0;
+}
+DM_TEST(dm_test_inactive_child, DM_TESTF_SCAN_PDATA);