18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Support for dynamic device trees.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * On some platforms, the device tree can be manipulated at runtime.
68c2ecf20Sopenharmony_ci * The routines in this section support adding, removing and changing
78c2ecf20Sopenharmony_ci * device tree nodes.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	"OF: " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/string.h>
168c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "of_private.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct device_node *kobj_to_device_node(struct kobject *kobj)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	return container_of(kobj, struct device_node, kobj);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/**
268c2ecf20Sopenharmony_ci * of_node_get() - Increment refcount of a node
278c2ecf20Sopenharmony_ci * @node:	Node to inc refcount, NULL is supported to simplify writing of
288c2ecf20Sopenharmony_ci *		callers
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Return: The node with refcount incremented.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cistruct device_node *of_node_get(struct device_node *node)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	if (node)
358c2ecf20Sopenharmony_ci		kobject_get(&node->kobj);
368c2ecf20Sopenharmony_ci	return node;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_node_get);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/**
418c2ecf20Sopenharmony_ci * of_node_put() - Decrement refcount of a node
428c2ecf20Sopenharmony_ci * @node:	Node to dec refcount, NULL is supported to simplify writing of
438c2ecf20Sopenharmony_ci *		callers
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_civoid of_node_put(struct device_node *node)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	if (node)
488c2ecf20Sopenharmony_ci		kobject_put(&node->kobj);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_node_put);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciint of_reconfig_notifier_register(struct notifier_block *nb)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	return blocking_notifier_chain_register(&of_reconfig_chain, nb);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_reconfig_notifier_register);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ciint of_reconfig_notifier_unregister(struct notifier_block *nb)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic const char *action_names[] = {
678c2ecf20Sopenharmony_ci	[0] = "INVALID",
688c2ecf20Sopenharmony_ci	[OF_RECONFIG_ATTACH_NODE] = "ATTACH_NODE",
698c2ecf20Sopenharmony_ci	[OF_RECONFIG_DETACH_NODE] = "DETACH_NODE",
708c2ecf20Sopenharmony_ci	[OF_RECONFIG_ADD_PROPERTY] = "ADD_PROPERTY",
718c2ecf20Sopenharmony_ci	[OF_RECONFIG_REMOVE_PROPERTY] = "REMOVE_PROPERTY",
728c2ecf20Sopenharmony_ci	[OF_RECONFIG_UPDATE_PROPERTY] = "UPDATE_PROPERTY",
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ciint of_reconfig_notify(unsigned long action, struct of_reconfig_data *p)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	int rc;
788c2ecf20Sopenharmony_ci#ifdef DEBUG
798c2ecf20Sopenharmony_ci	struct of_reconfig_data *pr = p;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	switch (action) {
828c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
838c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
848c2ecf20Sopenharmony_ci		pr_debug("notify %-15s %pOF\n", action_names[action],
858c2ecf20Sopenharmony_ci			pr->dn);
868c2ecf20Sopenharmony_ci		break;
878c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
888c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
898c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
908c2ecf20Sopenharmony_ci		pr_debug("notify %-15s %pOF:%s\n", action_names[action],
918c2ecf20Sopenharmony_ci			pr->dn, pr->prop->name);
928c2ecf20Sopenharmony_ci		break;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci#endif
968c2ecf20Sopenharmony_ci	rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
978c2ecf20Sopenharmony_ci	return notifier_to_errno(rc);
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/*
1018c2ecf20Sopenharmony_ci * of_reconfig_get_state_change()	- Returns new state of device
1028c2ecf20Sopenharmony_ci * @action	- action of the of notifier
1038c2ecf20Sopenharmony_ci * @arg		- argument of the of notifier
1048c2ecf20Sopenharmony_ci *
1058c2ecf20Sopenharmony_ci * Returns the new state of a device based on the notifier used.
1068c2ecf20Sopenharmony_ci *
1078c2ecf20Sopenharmony_ci * Return: OF_RECONFIG_CHANGE_REMOVE on device going from enabled to
1088c2ecf20Sopenharmony_ci * disabled, OF_RECONFIG_CHANGE_ADD on device going from disabled to
1098c2ecf20Sopenharmony_ci * enabled and OF_RECONFIG_NO_CHANGE on no change.
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_ciint of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data *pr)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct property *prop, *old_prop = NULL;
1148c2ecf20Sopenharmony_ci	int is_status, status_state, old_status_state, prev_state, new_state;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* figure out if a device should be created or destroyed */
1178c2ecf20Sopenharmony_ci	switch (action) {
1188c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
1198c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
1208c2ecf20Sopenharmony_ci		prop = of_find_property(pr->dn, "status", NULL);
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
1238c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
1248c2ecf20Sopenharmony_ci		prop = pr->prop;
1258c2ecf20Sopenharmony_ci		break;
1268c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
1278c2ecf20Sopenharmony_ci		prop = pr->prop;
1288c2ecf20Sopenharmony_ci		old_prop = pr->old_prop;
1298c2ecf20Sopenharmony_ci		break;
1308c2ecf20Sopenharmony_ci	default:
1318c2ecf20Sopenharmony_ci		return OF_RECONFIG_NO_CHANGE;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	is_status = 0;
1358c2ecf20Sopenharmony_ci	status_state = -1;
1368c2ecf20Sopenharmony_ci	old_status_state = -1;
1378c2ecf20Sopenharmony_ci	prev_state = -1;
1388c2ecf20Sopenharmony_ci	new_state = -1;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	if (prop && !strcmp(prop->name, "status")) {
1418c2ecf20Sopenharmony_ci		is_status = 1;
1428c2ecf20Sopenharmony_ci		status_state = !strcmp(prop->value, "okay") ||
1438c2ecf20Sopenharmony_ci			       !strcmp(prop->value, "ok");
1448c2ecf20Sopenharmony_ci		if (old_prop)
1458c2ecf20Sopenharmony_ci			old_status_state = !strcmp(old_prop->value, "okay") ||
1468c2ecf20Sopenharmony_ci					   !strcmp(old_prop->value, "ok");
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	switch (action) {
1508c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
1518c2ecf20Sopenharmony_ci		prev_state = 0;
1528c2ecf20Sopenharmony_ci		/* -1 & 0 status either missing or okay */
1538c2ecf20Sopenharmony_ci		new_state = status_state != 0;
1548c2ecf20Sopenharmony_ci		break;
1558c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
1568c2ecf20Sopenharmony_ci		/* -1 & 0 status either missing or okay */
1578c2ecf20Sopenharmony_ci		prev_state = status_state != 0;
1588c2ecf20Sopenharmony_ci		new_state = 0;
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
1618c2ecf20Sopenharmony_ci		if (is_status) {
1628c2ecf20Sopenharmony_ci			/* no status property -> enabled (legacy) */
1638c2ecf20Sopenharmony_ci			prev_state = 1;
1648c2ecf20Sopenharmony_ci			new_state = status_state;
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
1688c2ecf20Sopenharmony_ci		if (is_status) {
1698c2ecf20Sopenharmony_ci			prev_state = status_state;
1708c2ecf20Sopenharmony_ci			/* no status property -> enabled (legacy) */
1718c2ecf20Sopenharmony_ci			new_state = 1;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci		break;
1748c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
1758c2ecf20Sopenharmony_ci		if (is_status) {
1768c2ecf20Sopenharmony_ci			prev_state = old_status_state != 0;
1778c2ecf20Sopenharmony_ci			new_state = status_state != 0;
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci		break;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (prev_state == new_state)
1838c2ecf20Sopenharmony_ci		return OF_RECONFIG_NO_CHANGE;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return new_state ? OF_RECONFIG_CHANGE_ADD : OF_RECONFIG_CHANGE_REMOVE;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_reconfig_get_state_change);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciint of_property_notify(int action, struct device_node *np,
1908c2ecf20Sopenharmony_ci		       struct property *prop, struct property *oldprop)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct of_reconfig_data pr;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* only call notifiers if the node is attached */
1958c2ecf20Sopenharmony_ci	if (!of_node_is_attached(np))
1968c2ecf20Sopenharmony_ci		return 0;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	pr.dn = np;
1998c2ecf20Sopenharmony_ci	pr.prop = prop;
2008c2ecf20Sopenharmony_ci	pr.old_prop = oldprop;
2018c2ecf20Sopenharmony_ci	return of_reconfig_notify(action, &pr);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic void __of_attach_node(struct device_node *np)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	const __be32 *phandle;
2078c2ecf20Sopenharmony_ci	int sz;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (!of_node_check_flag(np, OF_OVERLAY)) {
2108c2ecf20Sopenharmony_ci		np->name = __of_get_property(np, "name", NULL);
2118c2ecf20Sopenharmony_ci		if (!np->name)
2128c2ecf20Sopenharmony_ci			np->name = "<NULL>";
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		phandle = __of_get_property(np, "phandle", &sz);
2158c2ecf20Sopenharmony_ci		if (!phandle)
2168c2ecf20Sopenharmony_ci			phandle = __of_get_property(np, "linux,phandle", &sz);
2178c2ecf20Sopenharmony_ci		if (IS_ENABLED(CONFIG_PPC_PSERIES) && !phandle)
2188c2ecf20Sopenharmony_ci			phandle = __of_get_property(np, "ibm,phandle", &sz);
2198c2ecf20Sopenharmony_ci		if (phandle && (sz >= 4))
2208c2ecf20Sopenharmony_ci			np->phandle = be32_to_cpup(phandle);
2218c2ecf20Sopenharmony_ci		else
2228c2ecf20Sopenharmony_ci			np->phandle = 0;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	np->child = NULL;
2268c2ecf20Sopenharmony_ci	np->sibling = np->parent->child;
2278c2ecf20Sopenharmony_ci	np->parent->child = np;
2288c2ecf20Sopenharmony_ci	of_node_clear_flag(np, OF_DETACHED);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/**
2328c2ecf20Sopenharmony_ci * of_attach_node() - Plug a device node into the tree and global list.
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_ciint of_attach_node(struct device_node *np)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct of_reconfig_data rd;
2378c2ecf20Sopenharmony_ci	unsigned long flags;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	memset(&rd, 0, sizeof(rd));
2408c2ecf20Sopenharmony_ci	rd.dn = np;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	mutex_lock(&of_mutex);
2438c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&devtree_lock, flags);
2448c2ecf20Sopenharmony_ci	__of_attach_node(np);
2458c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&devtree_lock, flags);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	__of_attach_node_sysfs(np);
2488c2ecf20Sopenharmony_ci	mutex_unlock(&of_mutex);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, &rd);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return 0;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_civoid __of_detach_node(struct device_node *np)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct device_node *parent;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (WARN_ON(of_node_check_flag(np, OF_DETACHED)))
2608c2ecf20Sopenharmony_ci		return;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	parent = np->parent;
2638c2ecf20Sopenharmony_ci	if (WARN_ON(!parent))
2648c2ecf20Sopenharmony_ci		return;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	if (parent->child == np)
2678c2ecf20Sopenharmony_ci		parent->child = np->sibling;
2688c2ecf20Sopenharmony_ci	else {
2698c2ecf20Sopenharmony_ci		struct device_node *prevsib;
2708c2ecf20Sopenharmony_ci		for (prevsib = np->parent->child;
2718c2ecf20Sopenharmony_ci		     prevsib->sibling != np;
2728c2ecf20Sopenharmony_ci		     prevsib = prevsib->sibling)
2738c2ecf20Sopenharmony_ci			;
2748c2ecf20Sopenharmony_ci		prevsib->sibling = np->sibling;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	of_node_set_flag(np, OF_DETACHED);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* race with of_find_node_by_phandle() prevented by devtree_lock */
2808c2ecf20Sopenharmony_ci	__of_phandle_cache_inv_entry(np->phandle);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/**
2848c2ecf20Sopenharmony_ci * of_detach_node() - "Unplug" a node from the device tree.
2858c2ecf20Sopenharmony_ci */
2868c2ecf20Sopenharmony_ciint of_detach_node(struct device_node *np)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct of_reconfig_data rd;
2898c2ecf20Sopenharmony_ci	unsigned long flags;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	memset(&rd, 0, sizeof(rd));
2928c2ecf20Sopenharmony_ci	rd.dn = np;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	mutex_lock(&of_mutex);
2958c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&devtree_lock, flags);
2968c2ecf20Sopenharmony_ci	__of_detach_node(np);
2978c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&devtree_lock, flags);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	__of_detach_node_sysfs(np);
3008c2ecf20Sopenharmony_ci	mutex_unlock(&of_mutex);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	of_reconfig_notify(OF_RECONFIG_DETACH_NODE, &rd);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_detach_node);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic void property_list_free(struct property *prop_list)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct property *prop, *next;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	for (prop = prop_list; prop != NULL; prop = next) {
3138c2ecf20Sopenharmony_ci		next = prop->next;
3148c2ecf20Sopenharmony_ci		kfree(prop->name);
3158c2ecf20Sopenharmony_ci		kfree(prop->value);
3168c2ecf20Sopenharmony_ci		kfree(prop);
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/**
3218c2ecf20Sopenharmony_ci * of_node_release() - release a dynamically allocated node
3228c2ecf20Sopenharmony_ci * @kref: kref element of the node to be released
3238c2ecf20Sopenharmony_ci *
3248c2ecf20Sopenharmony_ci * In of_node_put() this function is passed to kref_put() as the destructor.
3258c2ecf20Sopenharmony_ci */
3268c2ecf20Sopenharmony_civoid of_node_release(struct kobject *kobj)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct device_node *node = kobj_to_device_node(kobj);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* We should never be releasing nodes that haven't been detached. */
3318c2ecf20Sopenharmony_ci	if (!of_node_check_flag(node, OF_DETACHED)) {
3328c2ecf20Sopenharmony_ci		pr_err("ERROR: Bad of_node_put() on %pOF\n", node);
3338c2ecf20Sopenharmony_ci		dump_stack();
3348c2ecf20Sopenharmony_ci		return;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci	if (!of_node_check_flag(node, OF_DYNAMIC))
3378c2ecf20Sopenharmony_ci		return;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (of_node_check_flag(node, OF_OVERLAY)) {
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		if (!of_node_check_flag(node, OF_OVERLAY_FREE_CSET)) {
3428c2ecf20Sopenharmony_ci			/* premature refcount of zero, do not free memory */
3438c2ecf20Sopenharmony_ci			pr_err("ERROR: memory leak before free overlay changeset,  %pOF\n",
3448c2ecf20Sopenharmony_ci			       node);
3458c2ecf20Sopenharmony_ci			return;
3468c2ecf20Sopenharmony_ci		}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		/*
3498c2ecf20Sopenharmony_ci		 * If node->properties non-empty then properties were added
3508c2ecf20Sopenharmony_ci		 * to this node either by different overlay that has not
3518c2ecf20Sopenharmony_ci		 * yet been removed, or by a non-overlay mechanism.
3528c2ecf20Sopenharmony_ci		 */
3538c2ecf20Sopenharmony_ci		if (node->properties)
3548c2ecf20Sopenharmony_ci			pr_err("ERROR: %s(), unexpected properties in %pOF\n",
3558c2ecf20Sopenharmony_ci			       __func__, node);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	property_list_free(node->properties);
3598c2ecf20Sopenharmony_ci	property_list_free(node->deadprops);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	kfree(node->full_name);
3628c2ecf20Sopenharmony_ci	kfree(node->data);
3638c2ecf20Sopenharmony_ci	kfree(node);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/**
3678c2ecf20Sopenharmony_ci * __of_prop_dup - Copy a property dynamically.
3688c2ecf20Sopenharmony_ci * @prop:	Property to copy
3698c2ecf20Sopenharmony_ci * @allocflags:	Allocation flags (typically pass GFP_KERNEL)
3708c2ecf20Sopenharmony_ci *
3718c2ecf20Sopenharmony_ci * Copy a property by dynamically allocating the memory of both the
3728c2ecf20Sopenharmony_ci * property structure and the property name & contents. The property's
3738c2ecf20Sopenharmony_ci * flags have the OF_DYNAMIC bit set so that we can differentiate between
3748c2ecf20Sopenharmony_ci * dynamically allocated properties and not.
3758c2ecf20Sopenharmony_ci *
3768c2ecf20Sopenharmony_ci * Return: The newly allocated property or NULL on out of memory error.
3778c2ecf20Sopenharmony_ci */
3788c2ecf20Sopenharmony_cistruct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct property *new;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	new = kzalloc(sizeof(*new), allocflags);
3838c2ecf20Sopenharmony_ci	if (!new)
3848c2ecf20Sopenharmony_ci		return NULL;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/*
3878c2ecf20Sopenharmony_ci	 * NOTE: There is no check for zero length value.
3888c2ecf20Sopenharmony_ci	 * In case of a boolean property, this will allocate a value
3898c2ecf20Sopenharmony_ci	 * of zero bytes. We do this to work around the use
3908c2ecf20Sopenharmony_ci	 * of of_get_property() calls on boolean values.
3918c2ecf20Sopenharmony_ci	 */
3928c2ecf20Sopenharmony_ci	new->name = kstrdup(prop->name, allocflags);
3938c2ecf20Sopenharmony_ci	new->value = kmemdup(prop->value, prop->length, allocflags);
3948c2ecf20Sopenharmony_ci	new->length = prop->length;
3958c2ecf20Sopenharmony_ci	if (!new->name || !new->value)
3968c2ecf20Sopenharmony_ci		goto err_free;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	/* mark the property as dynamic */
3998c2ecf20Sopenharmony_ci	of_property_set_flag(new, OF_DYNAMIC);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return new;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci err_free:
4048c2ecf20Sopenharmony_ci	kfree(new->name);
4058c2ecf20Sopenharmony_ci	kfree(new->value);
4068c2ecf20Sopenharmony_ci	kfree(new);
4078c2ecf20Sopenharmony_ci	return NULL;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci/**
4118c2ecf20Sopenharmony_ci * __of_node_dup() - Duplicate or create an empty device node dynamically.
4128c2ecf20Sopenharmony_ci * @np:		if not NULL, contains properties to be duplicated in new node
4138c2ecf20Sopenharmony_ci * @full_name:	string value to be duplicated into new node's full_name field
4148c2ecf20Sopenharmony_ci *
4158c2ecf20Sopenharmony_ci * Create a device tree node, optionally duplicating the properties of
4168c2ecf20Sopenharmony_ci * another node.  The node data are dynamically allocated and all the node
4178c2ecf20Sopenharmony_ci * flags have the OF_DYNAMIC & OF_DETACHED bits set.
4188c2ecf20Sopenharmony_ci *
4198c2ecf20Sopenharmony_ci * Return: The newly allocated node or NULL on out of memory error.
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_cistruct device_node *__of_node_dup(const struct device_node *np,
4228c2ecf20Sopenharmony_ci				  const char *full_name)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct device_node *node;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	node = kzalloc(sizeof(*node), GFP_KERNEL);
4278c2ecf20Sopenharmony_ci	if (!node)
4288c2ecf20Sopenharmony_ci		return NULL;
4298c2ecf20Sopenharmony_ci	node->full_name = kstrdup(full_name, GFP_KERNEL);
4308c2ecf20Sopenharmony_ci	if (!node->full_name) {
4318c2ecf20Sopenharmony_ci		kfree(node);
4328c2ecf20Sopenharmony_ci		return NULL;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	of_node_set_flag(node, OF_DYNAMIC);
4368c2ecf20Sopenharmony_ci	of_node_set_flag(node, OF_DETACHED);
4378c2ecf20Sopenharmony_ci	of_node_init(node);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	/* Iterate over and duplicate all properties */
4408c2ecf20Sopenharmony_ci	if (np) {
4418c2ecf20Sopenharmony_ci		struct property *pp, *new_pp;
4428c2ecf20Sopenharmony_ci		for_each_property_of_node(np, pp) {
4438c2ecf20Sopenharmony_ci			new_pp = __of_prop_dup(pp, GFP_KERNEL);
4448c2ecf20Sopenharmony_ci			if (!new_pp)
4458c2ecf20Sopenharmony_ci				goto err_prop;
4468c2ecf20Sopenharmony_ci			if (__of_add_property(node, new_pp)) {
4478c2ecf20Sopenharmony_ci				kfree(new_pp->name);
4488c2ecf20Sopenharmony_ci				kfree(new_pp->value);
4498c2ecf20Sopenharmony_ci				kfree(new_pp);
4508c2ecf20Sopenharmony_ci				goto err_prop;
4518c2ecf20Sopenharmony_ci			}
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci	return node;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci err_prop:
4578c2ecf20Sopenharmony_ci	of_node_put(node); /* Frees the node and properties */
4588c2ecf20Sopenharmony_ci	return NULL;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	if (ce->action == OF_RECONFIG_ATTACH_NODE &&
4648c2ecf20Sopenharmony_ci	    of_node_check_flag(ce->np, OF_OVERLAY)) {
4658c2ecf20Sopenharmony_ci		if (kref_read(&ce->np->kobj.kref) > 1) {
4668c2ecf20Sopenharmony_ci			pr_err("ERROR: memory leak, expected refcount 1 instead of %d, of_node_get()/of_node_put() unbalanced - destroy cset entry: attach overlay node %pOF\n",
4678c2ecf20Sopenharmony_ci			       kref_read(&ce->np->kobj.kref), ce->np);
4688c2ecf20Sopenharmony_ci		} else {
4698c2ecf20Sopenharmony_ci			of_node_set_flag(ce->np, OF_OVERLAY_FREE_CSET);
4708c2ecf20Sopenharmony_ci		}
4718c2ecf20Sopenharmony_ci	}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	of_node_put(ce->np);
4748c2ecf20Sopenharmony_ci	list_del(&ce->node);
4758c2ecf20Sopenharmony_ci	kfree(ce);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci#ifdef DEBUG
4798c2ecf20Sopenharmony_cistatic void __of_changeset_entry_dump(struct of_changeset_entry *ce)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	switch (ce->action) {
4828c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
4838c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
4848c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
4858c2ecf20Sopenharmony_ci		pr_debug("cset<%p> %-15s %pOF/%s\n", ce, action_names[ce->action],
4868c2ecf20Sopenharmony_ci			ce->np, ce->prop->name);
4878c2ecf20Sopenharmony_ci		break;
4888c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
4898c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
4908c2ecf20Sopenharmony_ci		pr_debug("cset<%p> %-15s %pOF\n", ce, action_names[ce->action],
4918c2ecf20Sopenharmony_ci			ce->np);
4928c2ecf20Sopenharmony_ci		break;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci#else
4968c2ecf20Sopenharmony_cistatic inline void __of_changeset_entry_dump(struct of_changeset_entry *ce)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	/* empty */
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci#endif
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void __of_changeset_entry_invert(struct of_changeset_entry *ce,
5038c2ecf20Sopenharmony_ci					  struct of_changeset_entry *rce)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	memcpy(rce, ce, sizeof(*rce));
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	switch (ce->action) {
5088c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
5098c2ecf20Sopenharmony_ci		rce->action = OF_RECONFIG_DETACH_NODE;
5108c2ecf20Sopenharmony_ci		break;
5118c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
5128c2ecf20Sopenharmony_ci		rce->action = OF_RECONFIG_ATTACH_NODE;
5138c2ecf20Sopenharmony_ci		break;
5148c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
5158c2ecf20Sopenharmony_ci		rce->action = OF_RECONFIG_REMOVE_PROPERTY;
5168c2ecf20Sopenharmony_ci		break;
5178c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
5188c2ecf20Sopenharmony_ci		rce->action = OF_RECONFIG_ADD_PROPERTY;
5198c2ecf20Sopenharmony_ci		break;
5208c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
5218c2ecf20Sopenharmony_ci		rce->old_prop = ce->prop;
5228c2ecf20Sopenharmony_ci		rce->prop = ce->old_prop;
5238c2ecf20Sopenharmony_ci		/* update was used but original property did not exist */
5248c2ecf20Sopenharmony_ci		if (!rce->prop) {
5258c2ecf20Sopenharmony_ci			rce->action = OF_RECONFIG_REMOVE_PROPERTY;
5268c2ecf20Sopenharmony_ci			rce->prop = ce->prop;
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci		break;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic int __of_changeset_entry_notify(struct of_changeset_entry *ce,
5338c2ecf20Sopenharmony_ci		bool revert)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	struct of_reconfig_data rd;
5368c2ecf20Sopenharmony_ci	struct of_changeset_entry ce_inverted;
5378c2ecf20Sopenharmony_ci	int ret = 0;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (revert) {
5408c2ecf20Sopenharmony_ci		__of_changeset_entry_invert(ce, &ce_inverted);
5418c2ecf20Sopenharmony_ci		ce = &ce_inverted;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	switch (ce->action) {
5458c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
5468c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
5478c2ecf20Sopenharmony_ci		memset(&rd, 0, sizeof(rd));
5488c2ecf20Sopenharmony_ci		rd.dn = ce->np;
5498c2ecf20Sopenharmony_ci		ret = of_reconfig_notify(ce->action, &rd);
5508c2ecf20Sopenharmony_ci		break;
5518c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
5528c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
5538c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
5548c2ecf20Sopenharmony_ci		ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop);
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci	default:
5578c2ecf20Sopenharmony_ci		pr_err("invalid devicetree changeset action: %i\n",
5588c2ecf20Sopenharmony_ci			(int)ce->action);
5598c2ecf20Sopenharmony_ci		ret = -EINVAL;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (ret)
5638c2ecf20Sopenharmony_ci		pr_err("changeset notifier error @%pOF\n", ce->np);
5648c2ecf20Sopenharmony_ci	return ret;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic int __of_changeset_entry_apply(struct of_changeset_entry *ce)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct property *old_prop, **propp;
5708c2ecf20Sopenharmony_ci	unsigned long flags;
5718c2ecf20Sopenharmony_ci	int ret = 0;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	__of_changeset_entry_dump(ce);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&devtree_lock, flags);
5768c2ecf20Sopenharmony_ci	switch (ce->action) {
5778c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
5788c2ecf20Sopenharmony_ci		__of_attach_node(ce->np);
5798c2ecf20Sopenharmony_ci		break;
5808c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
5818c2ecf20Sopenharmony_ci		__of_detach_node(ce->np);
5828c2ecf20Sopenharmony_ci		break;
5838c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
5848c2ecf20Sopenharmony_ci		/* If the property is in deadprops then it must be removed */
5858c2ecf20Sopenharmony_ci		for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
5868c2ecf20Sopenharmony_ci			if (*propp == ce->prop) {
5878c2ecf20Sopenharmony_ci				*propp = ce->prop->next;
5888c2ecf20Sopenharmony_ci				ce->prop->next = NULL;
5898c2ecf20Sopenharmony_ci				break;
5908c2ecf20Sopenharmony_ci			}
5918c2ecf20Sopenharmony_ci		}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		ret = __of_add_property(ce->np, ce->prop);
5948c2ecf20Sopenharmony_ci		break;
5958c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
5968c2ecf20Sopenharmony_ci		ret = __of_remove_property(ce->np, ce->prop);
5978c2ecf20Sopenharmony_ci		break;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
6008c2ecf20Sopenharmony_ci		/* If the property is in deadprops then it must be removed */
6018c2ecf20Sopenharmony_ci		for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
6028c2ecf20Sopenharmony_ci			if (*propp == ce->prop) {
6038c2ecf20Sopenharmony_ci				*propp = ce->prop->next;
6048c2ecf20Sopenharmony_ci				ce->prop->next = NULL;
6058c2ecf20Sopenharmony_ci				break;
6068c2ecf20Sopenharmony_ci			}
6078c2ecf20Sopenharmony_ci		}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		ret = __of_update_property(ce->np, ce->prop, &old_prop);
6108c2ecf20Sopenharmony_ci		break;
6118c2ecf20Sopenharmony_ci	default:
6128c2ecf20Sopenharmony_ci		ret = -EINVAL;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&devtree_lock, flags);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	if (ret) {
6178c2ecf20Sopenharmony_ci		pr_err("changeset: apply failed: %-15s %pOF:%s\n",
6188c2ecf20Sopenharmony_ci		       action_names[ce->action], ce->np, ce->prop->name);
6198c2ecf20Sopenharmony_ci		return ret;
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	switch (ce->action) {
6238c2ecf20Sopenharmony_ci	case OF_RECONFIG_ATTACH_NODE:
6248c2ecf20Sopenharmony_ci		__of_attach_node_sysfs(ce->np);
6258c2ecf20Sopenharmony_ci		break;
6268c2ecf20Sopenharmony_ci	case OF_RECONFIG_DETACH_NODE:
6278c2ecf20Sopenharmony_ci		__of_detach_node_sysfs(ce->np);
6288c2ecf20Sopenharmony_ci		break;
6298c2ecf20Sopenharmony_ci	case OF_RECONFIG_ADD_PROPERTY:
6308c2ecf20Sopenharmony_ci		/* ignore duplicate names */
6318c2ecf20Sopenharmony_ci		__of_add_property_sysfs(ce->np, ce->prop);
6328c2ecf20Sopenharmony_ci		break;
6338c2ecf20Sopenharmony_ci	case OF_RECONFIG_REMOVE_PROPERTY:
6348c2ecf20Sopenharmony_ci		__of_remove_property_sysfs(ce->np, ce->prop);
6358c2ecf20Sopenharmony_ci		break;
6368c2ecf20Sopenharmony_ci	case OF_RECONFIG_UPDATE_PROPERTY:
6378c2ecf20Sopenharmony_ci		__of_update_property_sysfs(ce->np, ce->prop, ce->old_prop);
6388c2ecf20Sopenharmony_ci		break;
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	return 0;
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic inline int __of_changeset_entry_revert(struct of_changeset_entry *ce)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct of_changeset_entry ce_inverted;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	__of_changeset_entry_invert(ce, &ce_inverted);
6498c2ecf20Sopenharmony_ci	return __of_changeset_entry_apply(&ce_inverted);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci/**
6538c2ecf20Sopenharmony_ci * of_changeset_init - Initialize a changeset for use
6548c2ecf20Sopenharmony_ci *
6558c2ecf20Sopenharmony_ci * @ocs:	changeset pointer
6568c2ecf20Sopenharmony_ci *
6578c2ecf20Sopenharmony_ci * Initialize a changeset structure
6588c2ecf20Sopenharmony_ci */
6598c2ecf20Sopenharmony_civoid of_changeset_init(struct of_changeset *ocs)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	memset(ocs, 0, sizeof(*ocs));
6628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&ocs->entries);
6638c2ecf20Sopenharmony_ci}
6648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_changeset_init);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci/**
6678c2ecf20Sopenharmony_ci * of_changeset_destroy - Destroy a changeset
6688c2ecf20Sopenharmony_ci *
6698c2ecf20Sopenharmony_ci * @ocs:	changeset pointer
6708c2ecf20Sopenharmony_ci *
6718c2ecf20Sopenharmony_ci * Destroys a changeset. Note that if a changeset is applied,
6728c2ecf20Sopenharmony_ci * its changes to the tree cannot be reverted.
6738c2ecf20Sopenharmony_ci */
6748c2ecf20Sopenharmony_civoid of_changeset_destroy(struct of_changeset *ocs)
6758c2ecf20Sopenharmony_ci{
6768c2ecf20Sopenharmony_ci	struct of_changeset_entry *ce, *cen;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node)
6798c2ecf20Sopenharmony_ci		__of_changeset_entry_destroy(ce);
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_changeset_destroy);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/*
6848c2ecf20Sopenharmony_ci * Apply the changeset entries in @ocs.
6858c2ecf20Sopenharmony_ci * If apply fails, an attempt is made to revert the entries that were
6868c2ecf20Sopenharmony_ci * successfully applied.
6878c2ecf20Sopenharmony_ci *
6888c2ecf20Sopenharmony_ci * If multiple revert errors occur then only the final revert error is reported.
6898c2ecf20Sopenharmony_ci *
6908c2ecf20Sopenharmony_ci * Returns 0 on success, a negative error value in case of an error.
6918c2ecf20Sopenharmony_ci * If a revert error occurs, it is returned in *ret_revert.
6928c2ecf20Sopenharmony_ci */
6938c2ecf20Sopenharmony_ciint __of_changeset_apply_entries(struct of_changeset *ocs, int *ret_revert)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	struct of_changeset_entry *ce;
6968c2ecf20Sopenharmony_ci	int ret, ret_tmp;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	pr_debug("changeset: applying...\n");
6998c2ecf20Sopenharmony_ci	list_for_each_entry(ce, &ocs->entries, node) {
7008c2ecf20Sopenharmony_ci		ret = __of_changeset_entry_apply(ce);
7018c2ecf20Sopenharmony_ci		if (ret) {
7028c2ecf20Sopenharmony_ci			pr_err("Error applying changeset (%d)\n", ret);
7038c2ecf20Sopenharmony_ci			list_for_each_entry_continue_reverse(ce, &ocs->entries,
7048c2ecf20Sopenharmony_ci							     node) {
7058c2ecf20Sopenharmony_ci				ret_tmp = __of_changeset_entry_revert(ce);
7068c2ecf20Sopenharmony_ci				if (ret_tmp)
7078c2ecf20Sopenharmony_ci					*ret_revert = ret_tmp;
7088c2ecf20Sopenharmony_ci			}
7098c2ecf20Sopenharmony_ci			return ret;
7108c2ecf20Sopenharmony_ci		}
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	return 0;
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci/*
7178c2ecf20Sopenharmony_ci * Returns 0 on success, a negative error value in case of an error.
7188c2ecf20Sopenharmony_ci *
7198c2ecf20Sopenharmony_ci * If multiple changeset entry notification errors occur then only the
7208c2ecf20Sopenharmony_ci * final notification error is reported.
7218c2ecf20Sopenharmony_ci */
7228c2ecf20Sopenharmony_ciint __of_changeset_apply_notify(struct of_changeset *ocs)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct of_changeset_entry *ce;
7258c2ecf20Sopenharmony_ci	int ret = 0, ret_tmp;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	pr_debug("changeset: emitting notifiers.\n");
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* drop the global lock while emitting notifiers */
7308c2ecf20Sopenharmony_ci	mutex_unlock(&of_mutex);
7318c2ecf20Sopenharmony_ci	list_for_each_entry(ce, &ocs->entries, node) {
7328c2ecf20Sopenharmony_ci		ret_tmp = __of_changeset_entry_notify(ce, 0);
7338c2ecf20Sopenharmony_ci		if (ret_tmp)
7348c2ecf20Sopenharmony_ci			ret = ret_tmp;
7358c2ecf20Sopenharmony_ci	}
7368c2ecf20Sopenharmony_ci	mutex_lock(&of_mutex);
7378c2ecf20Sopenharmony_ci	pr_debug("changeset: notifiers sent.\n");
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	return ret;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci/*
7438c2ecf20Sopenharmony_ci * Returns 0 on success, a negative error value in case of an error.
7448c2ecf20Sopenharmony_ci *
7458c2ecf20Sopenharmony_ci * If a changeset entry apply fails, an attempt is made to revert any
7468c2ecf20Sopenharmony_ci * previous entries in the changeset.  If any of the reverts fails,
7478c2ecf20Sopenharmony_ci * that failure is not reported.  Thus the state of the device tree
7488c2ecf20Sopenharmony_ci * is unknown if an apply error occurs.
7498c2ecf20Sopenharmony_ci */
7508c2ecf20Sopenharmony_cistatic int __of_changeset_apply(struct of_changeset *ocs)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	int ret, ret_revert = 0;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	ret = __of_changeset_apply_entries(ocs, &ret_revert);
7558c2ecf20Sopenharmony_ci	if (!ret)
7568c2ecf20Sopenharmony_ci		ret = __of_changeset_apply_notify(ocs);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	return ret;
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci/**
7628c2ecf20Sopenharmony_ci * of_changeset_apply - Applies a changeset
7638c2ecf20Sopenharmony_ci *
7648c2ecf20Sopenharmony_ci * @ocs:	changeset pointer
7658c2ecf20Sopenharmony_ci *
7668c2ecf20Sopenharmony_ci * Applies a changeset to the live tree.
7678c2ecf20Sopenharmony_ci * Any side-effects of live tree state changes are applied here on
7688c2ecf20Sopenharmony_ci * success, like creation/destruction of devices and side-effects
7698c2ecf20Sopenharmony_ci * like creation of sysfs properties and directories.
7708c2ecf20Sopenharmony_ci *
7718c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error value in case of an error.
7728c2ecf20Sopenharmony_ci * On error the partially applied effects are reverted.
7738c2ecf20Sopenharmony_ci */
7748c2ecf20Sopenharmony_ciint of_changeset_apply(struct of_changeset *ocs)
7758c2ecf20Sopenharmony_ci{
7768c2ecf20Sopenharmony_ci	int ret;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	mutex_lock(&of_mutex);
7798c2ecf20Sopenharmony_ci	ret = __of_changeset_apply(ocs);
7808c2ecf20Sopenharmony_ci	mutex_unlock(&of_mutex);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	return ret;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_changeset_apply);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci/*
7878c2ecf20Sopenharmony_ci * Revert the changeset entries in @ocs.
7888c2ecf20Sopenharmony_ci * If revert fails, an attempt is made to re-apply the entries that were
7898c2ecf20Sopenharmony_ci * successfully removed.
7908c2ecf20Sopenharmony_ci *
7918c2ecf20Sopenharmony_ci * If multiple re-apply errors occur then only the final apply error is
7928c2ecf20Sopenharmony_ci * reported.
7938c2ecf20Sopenharmony_ci *
7948c2ecf20Sopenharmony_ci * Returns 0 on success, a negative error value in case of an error.
7958c2ecf20Sopenharmony_ci * If an apply error occurs, it is returned in *ret_apply.
7968c2ecf20Sopenharmony_ci */
7978c2ecf20Sopenharmony_ciint __of_changeset_revert_entries(struct of_changeset *ocs, int *ret_apply)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct of_changeset_entry *ce;
8008c2ecf20Sopenharmony_ci	int ret, ret_tmp;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	pr_debug("changeset: reverting...\n");
8038c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(ce, &ocs->entries, node) {
8048c2ecf20Sopenharmony_ci		ret = __of_changeset_entry_revert(ce);
8058c2ecf20Sopenharmony_ci		if (ret) {
8068c2ecf20Sopenharmony_ci			pr_err("Error reverting changeset (%d)\n", ret);
8078c2ecf20Sopenharmony_ci			list_for_each_entry_continue(ce, &ocs->entries, node) {
8088c2ecf20Sopenharmony_ci				ret_tmp = __of_changeset_entry_apply(ce);
8098c2ecf20Sopenharmony_ci				if (ret_tmp)
8108c2ecf20Sopenharmony_ci					*ret_apply = ret_tmp;
8118c2ecf20Sopenharmony_ci			}
8128c2ecf20Sopenharmony_ci			return ret;
8138c2ecf20Sopenharmony_ci		}
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	return 0;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci/*
8208c2ecf20Sopenharmony_ci * If multiple changeset entry notification errors occur then only the
8218c2ecf20Sopenharmony_ci * final notification error is reported.
8228c2ecf20Sopenharmony_ci */
8238c2ecf20Sopenharmony_ciint __of_changeset_revert_notify(struct of_changeset *ocs)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct of_changeset_entry *ce;
8268c2ecf20Sopenharmony_ci	int ret = 0, ret_tmp;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	pr_debug("changeset: emitting notifiers.\n");
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* drop the global lock while emitting notifiers */
8318c2ecf20Sopenharmony_ci	mutex_unlock(&of_mutex);
8328c2ecf20Sopenharmony_ci	list_for_each_entry_reverse(ce, &ocs->entries, node) {
8338c2ecf20Sopenharmony_ci		ret_tmp = __of_changeset_entry_notify(ce, 1);
8348c2ecf20Sopenharmony_ci		if (ret_tmp)
8358c2ecf20Sopenharmony_ci			ret = ret_tmp;
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci	mutex_lock(&of_mutex);
8388c2ecf20Sopenharmony_ci	pr_debug("changeset: notifiers sent.\n");
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	return ret;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cistatic int __of_changeset_revert(struct of_changeset *ocs)
8448c2ecf20Sopenharmony_ci{
8458c2ecf20Sopenharmony_ci	int ret, ret_reply;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	ret_reply = 0;
8488c2ecf20Sopenharmony_ci	ret = __of_changeset_revert_entries(ocs, &ret_reply);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	if (!ret)
8518c2ecf20Sopenharmony_ci		ret = __of_changeset_revert_notify(ocs);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	return ret;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci/**
8578c2ecf20Sopenharmony_ci * of_changeset_revert - Reverts an applied changeset
8588c2ecf20Sopenharmony_ci *
8598c2ecf20Sopenharmony_ci * @ocs:	changeset pointer
8608c2ecf20Sopenharmony_ci *
8618c2ecf20Sopenharmony_ci * Reverts a changeset returning the state of the tree to what it
8628c2ecf20Sopenharmony_ci * was before the application.
8638c2ecf20Sopenharmony_ci * Any side-effects like creation/destruction of devices and
8648c2ecf20Sopenharmony_ci * removal of sysfs properties and directories are applied.
8658c2ecf20Sopenharmony_ci *
8668c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error value in case of an error.
8678c2ecf20Sopenharmony_ci */
8688c2ecf20Sopenharmony_ciint of_changeset_revert(struct of_changeset *ocs)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	int ret;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	mutex_lock(&of_mutex);
8738c2ecf20Sopenharmony_ci	ret = __of_changeset_revert(ocs);
8748c2ecf20Sopenharmony_ci	mutex_unlock(&of_mutex);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	return ret;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_changeset_revert);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci/**
8818c2ecf20Sopenharmony_ci * of_changeset_action - Add an action to the tail of the changeset list
8828c2ecf20Sopenharmony_ci *
8838c2ecf20Sopenharmony_ci * @ocs:	changeset pointer
8848c2ecf20Sopenharmony_ci * @action:	action to perform
8858c2ecf20Sopenharmony_ci * @np:		Pointer to device node
8868c2ecf20Sopenharmony_ci * @prop:	Pointer to property
8878c2ecf20Sopenharmony_ci *
8888c2ecf20Sopenharmony_ci * On action being one of:
8898c2ecf20Sopenharmony_ci * + OF_RECONFIG_ATTACH_NODE
8908c2ecf20Sopenharmony_ci * + OF_RECONFIG_DETACH_NODE,
8918c2ecf20Sopenharmony_ci * + OF_RECONFIG_ADD_PROPERTY
8928c2ecf20Sopenharmony_ci * + OF_RECONFIG_REMOVE_PROPERTY,
8938c2ecf20Sopenharmony_ci * + OF_RECONFIG_UPDATE_PROPERTY
8948c2ecf20Sopenharmony_ci *
8958c2ecf20Sopenharmony_ci * Return: 0 on success, a negative error value in case of an error.
8968c2ecf20Sopenharmony_ci */
8978c2ecf20Sopenharmony_ciint of_changeset_action(struct of_changeset *ocs, unsigned long action,
8988c2ecf20Sopenharmony_ci		struct device_node *np, struct property *prop)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct of_changeset_entry *ce;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if (WARN_ON(action >= ARRAY_SIZE(action_names)))
9038c2ecf20Sopenharmony_ci		return -EINVAL;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
9068c2ecf20Sopenharmony_ci	if (!ce)
9078c2ecf20Sopenharmony_ci		return -ENOMEM;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	/* get a reference to the node */
9108c2ecf20Sopenharmony_ci	ce->action = action;
9118c2ecf20Sopenharmony_ci	ce->np = of_node_get(np);
9128c2ecf20Sopenharmony_ci	ce->prop = prop;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (action == OF_RECONFIG_UPDATE_PROPERTY && prop)
9158c2ecf20Sopenharmony_ci		ce->old_prop = of_find_property(np, prop->name, NULL);
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/* add it to the list */
9188c2ecf20Sopenharmony_ci	list_add_tail(&ce->node, &ocs->entries);
9198c2ecf20Sopenharmony_ci	return 0;
9208c2ecf20Sopenharmony_ci}
9218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_changeset_action);
922