Commit b544fc2b authored by Lizhi Hou's avatar Lizhi Hou Committed by Rob Herring
Browse files

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: default avatarClément Léger <clement.leger@bootlin.com>
Signed-off-by: default avatarLizhi Hou <lizhi.hou@amd.com>
Link: https://lore.kernel.org/r/1692120000-46900-2-git-send-email-lizhi.hou@amd.com


Signed-off-by: default avatarRob Herring <robh@kernel.org>
parent ef04d280
Loading
Loading
Loading
Loading
+164 −0
Original line number Diff line number Diff line
@@ -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);
+18 −1
Original line number Diff line number Diff line
@@ -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
}

+23 −0
Original line number Diff line number Diff line
@@ -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)
{