dm: Add child_pre_probe() and child_post_remove() methods

Some devices (particularly bus devices) must track their children, knowing
when a new child is added so that it can be set up for communication on the
bus.

Add a child_pre_probe() method to provide this feature, and a corresponding
child_post_remove() method.

Signed-off-by: Simon Glass <sjg@chromium.org>
diff --git a/test/dm/bus.c b/test/dm/bus.c
index df8edcb..873d64e 100644
--- a/test/dm/bus.c
+++ b/test/dm/bus.c
@@ -14,11 +14,39 @@
 
 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",
@@ -34,6 +62,8 @@
 	.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) = {
@@ -172,3 +202,41 @@
 }
 
 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);