#include <common.h>
#include <dm.h>
+#include <dm/device-internal.h>
#include <dm/root.h>
#include <dm/test.h>
#include <dm/ut.h>
DECLARE_GLOBAL_DATA_PTR;
+enum {
+ FLAG_CHILD_PROBED = 10,
+ FLAG_CHILD_REMOVED = -7,
+};
+
+static struct dm_test_state *test_state;
+
static int testbus_drv_probe(struct udevice *dev)
{
return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
}
+static int testbus_child_pre_probe(struct udevice *dev)
+{
+ struct dm_test_parent_data *parent_data = dev_get_parentdata(dev);
+
+ parent_data->flag += FLAG_CHILD_PROBED;
+
+ return 0;
+}
+
+static int testbus_child_post_remove(struct udevice *dev)
+{
+ struct dm_test_parent_data *parent_data = dev_get_parentdata(dev);
+ struct dm_test_state *dms = test_state;
+
+ parent_data->flag += FLAG_CHILD_REMOVED;
+ if (dms)
+ dms->removed = dev;
+
+ return 0;
+}
+
static const struct udevice_id testbus_ids[] = {
{
.compatible = "denx,u-boot-test-bus",
.probe = testbus_drv_probe,
.priv_auto_alloc_size = sizeof(struct dm_test_priv),
.platdata_auto_alloc_size = sizeof(struct dm_test_pdata),
+ .per_child_auto_alloc_size = sizeof(struct dm_test_parent_data),
+ .child_pre_probe = testbus_child_pre_probe,
+ .child_post_remove = testbus_child_post_remove,
};
UCLASS_DRIVER(testbus) = {
return 0;
}
DM_TEST(dm_test_bus_children_funcs, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test that we can iterate through children */
+static int dm_test_bus_children_iterators(struct dm_test_state *dms)
+{
+ struct udevice *bus, *dev, *child;
+
+ /* Walk through the children one by one */
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_asserteq_str("c-test@5", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_str("c-test@0", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_str("c-test@1", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_ptr(dev, NULL);
+
+ /* Move to the next child without using device_find_first_child() */
+ ut_assertok(device_find_child_by_seq(bus, 5, true, &dev));
+ ut_asserteq_str("c-test@5", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_str("c-test@0", dev->name);
+
+ /* Try a device with no children */
+ ut_assertok(device_find_first_child(dev, &child));
+ ut_asserteq_ptr(child, NULL);
+
+ return 0;
+}
+DM_TEST(dm_test_bus_children_iterators,
+ DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test that the bus can store data about each child */
+static int dm_test_bus_parent_data(struct dm_test_state *dms)
+{
+ struct dm_test_parent_data *parent_data;
+ struct udevice *bus, *dev;
+ struct uclass *uc;
+ int value;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+
+ /* Check that parent data is allocated */
+ ut_assertok(device_find_child_by_seq(bus, 0, true, &dev));
+ ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
+ ut_assertok(device_get_child_by_seq(bus, 0, &dev));
+ parent_data = dev_get_parentdata(dev);
+ ut_assert(NULL != parent_data);
+
+ /* Check that it starts at 0 and goes away when device is removed */
+ parent_data->sum += 5;
+ ut_asserteq(5, parent_data->sum);
+ device_remove(dev);
+ ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
+
+ /* Check that we can do this twice */
+ ut_assertok(device_get_child_by_seq(bus, 0, &dev));
+ parent_data = dev_get_parentdata(dev);
+ ut_assert(NULL != parent_data);
+ parent_data->sum += 5;
+ ut_asserteq(5, parent_data->sum);
+
+ /* Add parent data to all children */
+ ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+ value = 5;
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus) {
+ ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
+ continue;
+ }
+ ut_assertok(device_probe(dev));
+ parent_data = dev_get_parentdata(dev);
+
+ parent_data->sum = value;
+ value += 5;
+ }
+
+ /* Check it is still there */
+ value = 5;
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus)
+ continue;
+ parent_data = dev_get_parentdata(dev);
+
+ ut_asserteq(value, parent_data->sum);
+ value += 5;
+ }
+
+ return 0;
+}
+
+DM_TEST(dm_test_bus_parent_data, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test that the bus ops are called when a child is probed/removed */
+static int dm_test_bus_parent_ops(struct dm_test_state *dms)
+{
+ struct dm_test_parent_data *parent_data;
+ struct udevice *bus, *dev;
+ struct uclass *uc;
+
+ test_state = dms;
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus)
+ continue;
+ ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
+
+ ut_assertok(device_probe(dev));
+ parent_data = dev_get_parentdata(dev);
+ ut_asserteq(FLAG_CHILD_PROBED, parent_data->flag);
+ }
+
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus)
+ continue;
+ parent_data = dev_get_parentdata(dev);
+ ut_asserteq(FLAG_CHILD_PROBED, parent_data->flag);
+ ut_assertok(device_remove(dev));
+ ut_asserteq_ptr(NULL, dev_get_parentdata(dev));
+ ut_asserteq_ptr(dms->removed, dev);
+ }
+ test_state = NULL;
+
+ return 0;
+}
+DM_TEST(dm_test_bus_parent_ops, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);