From cfecbaf4e768991056a88d3a7a3daf4b4baf8038 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 7 Sep 2020 17:46:33 +0300 Subject: [PATCH] dm: core: add support for device re-parenting 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 Reviewed-by: Simon Glass --- drivers/core/device.c | 22 +++++ include/dm/device-internal.h | 9 ++ test/dm/core.c | 160 +++++++++++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) 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 @@ -83,6 +83,15 @@ int device_bind_with_driver_data(struct udevice *parent, 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 * 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) { -- 2.39.5