summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/core/device.c22
-rw-r--r--include/dm/device-internal.h9
-rw-r--r--test/dm/core.c160
3 files changed, 191 insertions, 0 deletions
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 355dbd147a..e90d70101c 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 5145fb4e14..1dcc22f689 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 8ed5bf7370..6f380a574c 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)
{