of: dynamic: Add interfaces for creating device node dynamically

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>
This commit is contained in:
Lizhi Hou 2023-08-15 10:19:56 -07:00 committed by Rob Herring
parent ef04d2801c
commit b544fc2b86
3 changed files with 205 additions and 1 deletions

View File

@ -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 &&
@ -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);

View File

@ -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");
@ -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");
@ -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");
@ -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
}

View File

@ -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)
{