of: dynamic: Add interfaces for creating device node dynamically
authorLizhi Hou <lizhi.hou@amd.com>
Tue, 15 Aug 2023 17:19:56 +0000 (10:19 -0700)
committerRob Herring <robh@kernel.org>
Tue, 22 Aug 2023 19:56:09 +0000 (14:56 -0500)
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>
drivers/of/dynamic.c
drivers/of/unittest.c
include/linux/of.h

index 3a9e38dcf9c240ee1e445a1d230414d3e946b1e8..0a3483e247a8932ca8ffe6ecbf0047f0c5472960 100644 (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);
index ac2d2ba2491967184ecccdae890c21050a162884..319d2254df9be5e008249e55574ea7dd78174dce 100644 (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
 }
 
index 7656cc63774620ff84925345124b260db878309e..bc2e37f016ec601a5a4e13788c9a9348a69837af 100644 (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)
 {