Skip to content

Commit

Permalink
of: dynamic: Add interfaces for creating device node dynamically
Browse files Browse the repository at this point in the history
of_changeset_create_node() creates device node dynamically and attaches
the newly created node to a changeset.

Expand of_changeset APIs to handle specific types of properties.
    of_changeset_add_prop_string()
    of_changeset_add_prop_string_array()
    of_changeset_add_prop_u32_array()

Signed-off-by: Clément Léger <clement.leger@bootlin.com>
Signed-off-by: Lizhi Hou <lizhi.hou@amd.com>
Link: https://lore.kernel.org/r/1692120000-46900-2-git-send-email-lizhi.hou@amd.com
Signed-off-by: Rob Herring <robh@kernel.org>
  • Loading branch information
Lizhi Hou authored and Rob Herring committed Aug 22, 2023
1 parent ef04d28 commit b544fc2
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 1 deletion.
164 changes: 164 additions & 0 deletions drivers/of/dynamic.c
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,38 @@ struct device_node *__of_node_dup(const struct device_node *np,
return NULL;
}

/**
* of_changeset_create_node - Dynamically create a device node and attach to
* a given changeset.
*
* @ocs: Pointer to changeset
* @parent: Pointer to parent device node
* @full_name: Node full name
*
* Return: Pointer to the created device node or NULL in case of an error.
*/
struct device_node *of_changeset_create_node(struct of_changeset *ocs,
struct device_node *parent,
const char *full_name)
{
struct device_node *np;
int ret;

np = __of_node_dup(NULL, full_name);
if (!np)
return NULL;
np->parent = parent;

ret = of_changeset_attach_node(ocs, np);
if (ret) {
of_node_put(np);
return NULL;
}

return np;
}
EXPORT_SYMBOL(of_changeset_create_node);

static void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
{
if (ce->action == OF_RECONFIG_ATTACH_NODE &&
Expand Down Expand Up @@ -875,3 +907,135 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action,
return 0;
}
EXPORT_SYMBOL_GPL(of_changeset_action);

static int of_changeset_add_prop_helper(struct of_changeset *ocs,
struct device_node *np,
const struct property *pp)
{
struct property *new_pp;
int ret;

new_pp = __of_prop_dup(pp, GFP_KERNEL);
if (!new_pp)
return -ENOMEM;

ret = of_changeset_add_property(ocs, np, new_pp);
if (ret) {
kfree(new_pp->name);
kfree(new_pp->value);
kfree(new_pp);
}

return ret;
}

/**
* of_changeset_add_prop_string - Add a string property to a changeset
*
* @ocs: changeset pointer
* @np: device node pointer
* @prop_name: name of the property to be added
* @str: pointer to null terminated string
*
* Create a string property and add it to a changeset.
*
* Return: 0 on success, a negative error value in case of an error.
*/
int of_changeset_add_prop_string(struct of_changeset *ocs,
struct device_node *np,
const char *prop_name, const char *str)
{
struct property prop;

prop.name = (char *)prop_name;
prop.length = strlen(str) + 1;
prop.value = (void *)str;

return of_changeset_add_prop_helper(ocs, np, &prop);
}
EXPORT_SYMBOL_GPL(of_changeset_add_prop_string);

/**
* of_changeset_add_prop_string_array - Add a string list property to
* a changeset
*
* @ocs: changeset pointer
* @np: device node pointer
* @prop_name: name of the property to be added
* @str_array: pointer to an array of null terminated strings
* @sz: number of string array elements
*
* Create a string list property and add it to a changeset.
*
* Return: 0 on success, a negative error value in case of an error.
*/
int of_changeset_add_prop_string_array(struct of_changeset *ocs,
struct device_node *np,
const char *prop_name,
const char **str_array, size_t sz)
{
struct property prop;
int i, ret;
char *vp;

prop.name = (char *)prop_name;

prop.length = 0;
for (i = 0; i < sz; i++)
prop.length += strlen(str_array[i]) + 1;

prop.value = kmalloc(prop.length, GFP_KERNEL);
if (!prop.value)
return -ENOMEM;

vp = prop.value;
for (i = 0; i < sz; i++) {
vp += snprintf(vp, (char *)prop.value + prop.length - vp, "%s",
str_array[i]) + 1;
}
ret = of_changeset_add_prop_helper(ocs, np, &prop);
kfree(prop.value);

return ret;
}
EXPORT_SYMBOL_GPL(of_changeset_add_prop_string_array);

/**
* of_changeset_add_prop_u32_array - Add a property of 32 bit integers
* property to a changeset
*
* @ocs: changeset pointer
* @np: device node pointer
* @prop_name: name of the property to be added
* @array: pointer to an array of 32 bit integers
* @sz: number of array elements
*
* Create a property of 32 bit integers and add it to a changeset.
*
* Return: 0 on success, a negative error value in case of an error.
*/
int of_changeset_add_prop_u32_array(struct of_changeset *ocs,
struct device_node *np,
const char *prop_name,
const u32 *array, size_t sz)
{
struct property prop;
__be32 *val;
int i, ret;

val = kcalloc(sz, sizeof(__be32), GFP_KERNEL);
if (!val)
return -ENOMEM;

for (i = 0; i < sz; i++)
val[i] = cpu_to_be32(array[i]);
prop.name = (char *)prop_name;
prop.length = sizeof(u32) * sz;
prop.value = (void *)val;

ret = of_changeset_add_prop_helper(ocs, np, &prop);
kfree(val);

return ret;
}
EXPORT_SYMBOL_GPL(of_changeset_add_prop_u32_array);
19 changes: 18 additions & 1 deletion drivers/of/unittest.c
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,9 @@ static void __init of_unittest_changeset(void)
struct property *ppname_n21, pname_n21 = { .name = "name", .length = 3, .value = "n21" };
struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
struct property *ppremove;
struct device_node *n1, *n2, *n21, *nchangeset, *nremove, *parent, *np;
struct device_node *n1, *n2, *n21, *n22, *nchangeset, *nremove, *parent, *np;
static const char * const str_array[] = { "str1", "str2", "str3" };
const u32 u32_array[] = { 1, 2, 3 };
struct of_changeset chgset;

n1 = __of_node_dup(NULL, "n1");
Expand Down Expand Up @@ -857,6 +859,17 @@ static void __init of_unittest_changeset(void)
unittest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop prop-add\n");
unittest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
unittest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
n22 = of_changeset_create_node(&chgset, n2, "n22");
unittest(n22, "fail create n22\n");
unittest(!of_changeset_add_prop_string(&chgset, n22, "prop-str", "abcd"),
"fail add prop prop-str");
unittest(!of_changeset_add_prop_string_array(&chgset, n22, "prop-str-array",
(const char **)str_array,
ARRAY_SIZE(str_array)),
"fail add prop prop-str-array");
unittest(!of_changeset_add_prop_u32_array(&chgset, n22, "prop-u32-array",
u32_array, ARRAY_SIZE(u32_array)),
"fail add prop prop-u32-array");

unittest(!of_changeset_apply(&chgset), "apply failed\n");

Expand All @@ -866,6 +879,9 @@ static void __init of_unittest_changeset(void)
unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n21")),
"'%pOF' not added\n", n21);
of_node_put(np);
unittest((np = of_find_node_by_path("/testcase-data/changeset/n2/n22")),
"'%pOF' not added\n", n22);
of_node_put(np);

unittest(!of_changeset_revert(&chgset), "revert failed\n");

Expand All @@ -874,6 +890,7 @@ static void __init of_unittest_changeset(void)
of_node_put(n1);
of_node_put(n2);
of_node_put(n21);
of_node_put(n22);
#endif
}

Expand Down
23 changes: 23 additions & 0 deletions include/linux/of.h
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,29 @@ static inline int of_changeset_update_property(struct of_changeset *ocs,
{
return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop);
}

struct device_node *of_changeset_create_node(struct of_changeset *ocs,
struct device_node *parent,
const char *full_name);
int of_changeset_add_prop_string(struct of_changeset *ocs,
struct device_node *np,
const char *prop_name, const char *str);
int of_changeset_add_prop_string_array(struct of_changeset *ocs,
struct device_node *np,
const char *prop_name,
const char **str_array, size_t sz);
int of_changeset_add_prop_u32_array(struct of_changeset *ocs,
struct device_node *np,
const char *prop_name,
const u32 *array, size_t sz);
static inline int of_changeset_add_prop_u32(struct of_changeset *ocs,
struct device_node *np,
const char *prop_name,
const u32 val)
{
return of_changeset_add_prop_u32_array(ocs, np, prop_name, &val, 1);
}

#else /* CONFIG_OF_DYNAMIC */
static inline int of_reconfig_notifier_register(struct notifier_block *nb)
{
Expand Down

0 comments on commit b544fc2

Please sign in to comment.