diff --git a/drivers/core/of_access.c b/drivers/core/of_access.c index 1bb4d8eab7..c8db743f52 100644 --- a/drivers/core/of_access.c +++ b/drivers/core/of_access.c @@ -1040,3 +1040,68 @@ int of_add_subnode(struct device_node *parent, const char *name, int len, return 0; } + +int __of_remove_property(struct device_node *np, struct property *prop) +{ + struct property **next; + + for (next = &np->properties; *next; next = &(*next)->next) { + if (*next == prop) + break; + } + if (!*next) + return -ENODEV; + + /* found the node */ + *next = prop->next; + + return 0; +} + +int of_remove_property(struct device_node *np, struct property *prop) +{ + int rc; + + mutex_lock(&of_mutex); + + rc = __of_remove_property(np, prop); + + mutex_unlock(&of_mutex); + + return rc; +} + +int of_remove_node(struct device_node *to_remove) +{ + struct device_node *parent = to_remove->parent; + struct device_node *np, *prev; + + if (!parent) + return -EPERM; + prev = NULL; + __for_each_child_of_node(parent, np) { + if (np == to_remove) + break; + prev = np; + } + if (!np) + return -EFAULT; + + /* if there is a previous node, link it to this one's sibling */ + if (prev) + prev->sibling = np->sibling; + else + parent->child = np->sibling; + + /* + * don't free it, since if this is an unflattened tree, all the memory + * was alloced in one block; this pointer will be somewhere in the + * middle of that + * + * TODO(sjg@chromium.org): Consider marking nodes as 'allocated'? + * + * free(np); + */ + + return 0; +} diff --git a/drivers/core/ofnode.c b/drivers/core/ofnode.c index 403ee06ad9..a5efedf6af 100644 --- a/drivers/core/ofnode.c +++ b/drivers/core/ofnode.c @@ -1779,6 +1779,29 @@ int ofnode_add_subnode(ofnode node, const char *name, ofnode *subnodep) return ret; /* 0 or -EEXIST */ } +int ofnode_delete(ofnode *nodep) +{ + ofnode node = *nodep; + int ret; + + assert(ofnode_valid(node)); + if (ofnode_is_np(node)) { + ret = of_remove_node(ofnode_to_np(node)); + } else { + void *fdt = ofnode_to_fdt(node); + int offset = ofnode_to_offset(node); + + ret = fdt_del_node(fdt, offset); + if (ret) + ret = -EFAULT; + } + if (ret) + return ret; + *nodep = ofnode_null(); + + return 0; +} + int ofnode_copy_props(ofnode dst, ofnode src) { struct ofprop prop; diff --git a/include/dm/of_access.h b/include/dm/of_access.h index 9361d0a87b..de740d4467 100644 --- a/include/dm/of_access.h +++ b/include/dm/of_access.h @@ -597,4 +597,22 @@ int of_write_prop(struct device_node *np, const char *propname, int len, int of_add_subnode(struct device_node *node, const char *name, int len, struct device_node **subnodep); +/** + * of_remove_property() - Remove a property from a node + * + * @np: Node to remove from + * @prop: Pointer to property to remove + * Return 0 if OK, -ENODEV if the property could not be found in the node + */ +int of_remove_property(struct device_node *np, struct property *prop); + +/** + * of_remove_node() - Remove a node from the tree + * + * @to_remove: Node to remove + * Return: 0 if OK, -EPERM if it is the root node (wWhich cannot be removed), + * -ENOENT if the tree is broken (to_remove is not a child of its parent) + */ +int of_remove_node(struct device_node *to_remove); + #endif diff --git a/include/dm/ofnode.h b/include/dm/ofnode.h index 7eb04accd6..f1ee02cd83 100644 --- a/include/dm/ofnode.h +++ b/include/dm/ofnode.h @@ -1635,4 +1635,17 @@ int ofnode_copy_props(ofnode dst, ofnode src); int ofnode_copy_node(ofnode dst_parent, const char *name, ofnode src, ofnode *nodep); +/** + * ofnode_delete() - Delete a node + * + * Delete a node from the tree + * + * @nodep: Pointer to node to delete (set to ofnode_null() on success) + * Return: 0 if OK, -ENOENT if the node does not exist, -EPERM if it is the root + * node (wWhich cannot be removed), -EFAULT if the tree is broken (to_remove is + * not a child of its parent), + * + */ +int ofnode_delete(ofnode *nodep); + #endif diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c index 5459a9afbb..845cded449 100644 --- a/test/dm/ofnode.c +++ b/test/dm/ofnode.c @@ -1425,3 +1425,34 @@ static int dm_test_ofnode_copy_node_ot(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_ofnode_copy_node_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT); + +static int dm_test_ofnode_delete(struct unit_test_state *uts) +{ + ofnode node; + + /* + * At present the livetree is not restored after changes made in tests. + * See test_pre_run() for how this is done with the other FDT and + * dm_test_pre_run() where it sets up the root-tree pointer. So use + * nodes which don't matter to other tests. + * + * We could fix this by detecting livetree changes and regenerating it + * before the next test if needed. + */ + node = ofnode_path("/leds/iracibble"); + ut_assert(ofnode_valid(node)); + ut_assertok(ofnode_delete(&node)); + ut_assert(!ofnode_valid(node)); + ut_assert(!ofnode_valid(ofnode_path("/leds/iracibble"))); + + node = ofnode_path("/leds/default_on"); + ut_assert(ofnode_valid(node)); + ut_assertok(ofnode_delete(&node)); + ut_assert(!ofnode_valid(node)); + ut_assert(!ofnode_valid(ofnode_path("/leds/default_on"))); + + ut_asserteq(2, ofnode_get_child_count(ofnode_path("/leds"))); + + return 0; +} +DM_TEST(dm_test_ofnode_delete, UT_TESTF_SCAN_FDT);