diff options
author | Lizhi Hou <lizhi.hou@amd.com> | 2023-08-15 20:19:56 +0300 |
---|---|---|
committer | Rob Herring <robh@kernel.org> | 2023-08-22 22:56:09 +0300 |
commit | b544fc2b8606d718d0cc788ff2ea2492871df488 (patch) | |
tree | 1445d3452f3152d6763edce639e81268d466baef /drivers/of/dynamic.c | |
parent | ef04d2801c5d239b83932f8ce97af1d4a1ec1cf7 (diff) | |
download | linux-b544fc2b8606d718d0cc788ff2ea2492871df488.tar.xz |
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>
Diffstat (limited to 'drivers/of/dynamic.c')
-rw-r--r-- | drivers/of/dynamic.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 3a9e38dcf9c2..0a3483e247a8 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -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); |