diff mbox series

[v2,02/21] dm: core: add support for device re-parenting

Message ID 1596640325-20014-3-git-send-email-claudiu.beznea@microchip.com
State Superseded
Delegated to: Eugen Hristev
Headers show
Series clk: at91: add sama7g5 support | expand

Commit Message

Claudiu Beznea Aug. 5, 2020, 3:11 p.m. UTC
In common clock framework the relation b/w parent and child clocks is
determined based on the udevice parent/child information. A clock
parent could be changed based on devices needs. In case this is happen
the functionalities for clock who's parent is changed are broken. Add
a function that reparent a device. This will be used in clk-uclass.c
to reparent a clock device.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/core/device.c        |  22 ++++++
 include/dm/device-internal.h |   9 +++
 test/dm/core.c               | 160 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 191 insertions(+)

Comments

Simon Glass Aug. 16, 2020, 3:39 a.m. UTC | #1
On Wed, 5 Aug 2020 at 09:12, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
>
> In common clock framework the relation b/w parent and child clocks is
> determined based on the udevice parent/child information. A clock
> parent could be changed based on devices needs. In case this is happen
> the functionalities for clock who's parent is changed are broken. Add
> a function that reparent a device. This will be used in clk-uclass.c
> to reparent a clock device.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  drivers/core/device.c        |  22 ++++++
>  include/dm/device-internal.h |   9 +++
>  test/dm/core.c               | 160 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 191 insertions(+)

Reviewed-by: Simon Glass <sjg@chromium.org>
diff mbox series

Patch

diff --git a/drivers/core/device.c b/drivers/core/device.c
index 355dbd147a9e..e90d70101c20 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -276,6 +276,28 @@  int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 	return ret;
 }
 
+int device_reparent(struct udevice *dev, struct udevice *new_parent)
+{
+	struct udevice *pos, *n;
+
+	assert(dev);
+	assert(new_parent);
+
+	list_for_each_entry_safe(pos, n, &dev->parent->child_head,
+				 sibling_node) {
+		if (pos->driver != dev->driver)
+			continue;
+
+		list_del(&dev->sibling_node);
+		list_add_tail(&dev->sibling_node, &new_parent->child_head);
+		dev->parent = new_parent;
+
+		break;
+	}
+
+	return 0;
+}
+
 static void *alloc_priv(int size, uint flags)
 {
 	void *priv;
diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
index 5145fb4e1459..1dcc22f68915 100644
--- a/include/dm/device-internal.h
+++ b/include/dm/device-internal.h
@@ -84,6 +84,15 @@  int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 			struct driver_info *info, struct udevice **devp);
 
 /**
+ * device_reparent: reparent the device to a new parent
+ *
+ * @dev: pointer to device to be reparented
+ * @new_parent: pointer to new parent device
+ * @return 0 if OK, -ve on error
+ */
+int device_reparent(struct udevice *dev, struct udevice *new_parent);
+
+/**
  * device_ofdata_to_platdata() - Read platform data for a device
  *
  * Read platform data for a device (typically from the device tree) so that
diff --git a/test/dm/core.c b/test/dm/core.c
index 9b73ec3aa644..c9fa2d56d4e0 100644
--- a/test/dm/core.c
+++ b/test/dm/core.c
@@ -643,6 +643,166 @@  static int dm_test_children(struct unit_test_state *uts)
 }
 DM_TEST(dm_test_children, 0);
 
+static int dm_test_device_reparent(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];
+	struct udevice *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(uts, dms->root, NODE_COUNT, 0, top));
+
+	/* Now a few have their own children */
+	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(uts, 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]);
+
+	/* Probe everything */
+	for (i = 0; i < total; i++)
+		ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev));
+
+	/* Re-parent top-level children with no grandchildren. */
+	ut_assertok(device_reparent(top[3], top[0]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_reparent(top[4], top[0]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Re-parent top-level children with grandchildren. */
+	ut_assertok(device_reparent(top[2], top[0]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_reparent(top[5], top[2]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Re-parent grandchildren. */
+	ut_assertok(device_reparent(grandchild[0], top[1]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_reparent(grandchild[1], top[1]));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Remove re-pareneted devices. */
+	ut_assertok(device_remove(top[3], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(top[4], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(top[5], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(top[2], DM_REMOVE_NORMAL));
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(grandchild[0], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	ut_assertok(device_remove(grandchild[1], DM_REMOVE_NORMAL));
+	/* try to get devices */
+	for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+	     dev;
+	     ret = uclass_find_next_device(&dev)) {
+		ut_assert(!ret);
+		ut_assertnonnull(dev);
+	}
+
+	/* Try the same with unbind */
+	ut_assertok(device_unbind(top[3]));
+	ut_assertok(device_unbind(top[4]));
+	ut_assertok(device_unbind(top[5]));
+	ut_assertok(device_unbind(top[2]));
+
+	ut_assertok(device_unbind(grandchild[0]));
+	ut_assertok(device_unbind(grandchild[1]));
+
+	return 0;
+}
+DM_TEST(dm_test_device_reparent, 0);
+
 /* Test that pre-relocation devices work as expected */
 static int dm_test_pre_reloc(struct unit_test_state *uts)
 {