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:
parent
ef04d2801c
commit
b544fc2b86
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user