// SPDX-License-Identifier: GPL-2.0 /* * Support for dynamic device trees. * * On some platforms, the device tree can be manipulated at runtime. * The routines in this section support adding, removing and changing * device tree nodes. */ #define pr_fmt(fmt) … #include <linux/cleanup.h> #include <linux/device.h> #include <linux/of.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/proc_fs.h> #include "of_private.h" static struct device_node *kobj_to_device_node(struct kobject *kobj) { … } /** * of_node_get() - Increment refcount of a node * @node: Node to inc refcount, NULL is supported to simplify writing of * callers * * Return: The node with refcount incremented. */ struct device_node *of_node_get(struct device_node *node) { … } EXPORT_SYMBOL(…); /** * of_node_put() - Decrement refcount of a node * @node: Node to dec refcount, NULL is supported to simplify writing of * callers */ void of_node_put(struct device_node *node) { … } EXPORT_SYMBOL(…); static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain); int of_reconfig_notifier_register(struct notifier_block *nb) { … } EXPORT_SYMBOL_GPL(…); int of_reconfig_notifier_unregister(struct notifier_block *nb) { … } EXPORT_SYMBOL_GPL(…); static const char *action_names[] = …; #define _do_print(func, prefix, action, node, prop, ...) … #define of_changeset_action_err(...) … #define of_changeset_action_debug(...) … int of_reconfig_notify(unsigned long action, struct of_reconfig_data *p) { … } /* * of_reconfig_get_state_change() - Returns new state of device * @action - action of the of notifier * @arg - argument of the of notifier * * Returns the new state of a device based on the notifier used. * * Return: OF_RECONFIG_CHANGE_REMOVE on device going from enabled to * disabled, OF_RECONFIG_CHANGE_ADD on device going from disabled to * enabled and OF_RECONFIG_NO_CHANGE on no change. */ int of_reconfig_get_state_change(unsigned long action, struct of_reconfig_data *pr) { … } EXPORT_SYMBOL_GPL(…); int of_property_notify(int action, struct device_node *np, struct property *prop, struct property *oldprop) { … } static void __of_attach_node(struct device_node *np) { … } /** * of_attach_node() - Plug a device node into the tree and global list. * @np: Pointer to the caller's Device Node */ int of_attach_node(struct device_node *np) { … } void __of_detach_node(struct device_node *np) { … } /** * of_detach_node() - "Unplug" a node from the device tree. * @np: Pointer to the caller's Device Node */ int of_detach_node(struct device_node *np) { … } EXPORT_SYMBOL_GPL(…); void __of_prop_free(struct property *prop) { … } static void property_list_free(struct property *prop_list) { … } /** * of_node_release() - release a dynamically allocated node * @kobj: kernel object of the node to be released * * In of_node_put() this function is passed to kref_put() as the destructor. */ void of_node_release(struct kobject *kobj) { … } /** * __of_prop_dup - Copy a property dynamically. * @prop: Property to copy * @allocflags: Allocation flags (typically pass GFP_KERNEL) * * Copy a property by dynamically allocating the memory of both the * property structure and the property name & contents. The property's * flags have the OF_DYNAMIC bit set so that we can differentiate between * dynamically allocated properties and not. * * Return: The newly allocated property or NULL on out of memory error. */ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) { … } /** * __of_node_dup() - Duplicate or create an empty device node dynamically. * @np: if not NULL, contains properties to be duplicated in new node * @full_name: string value to be duplicated into new node's full_name field * * Create a device tree node, optionally duplicating the properties of * another node. The node data are dynamically allocated and all the node * flags have the OF_DYNAMIC & OF_DETACHED bits set. * * Return: The newly allocated node or NULL on out of memory error. Use * of_node_put() on it when done to free the memory allocated for it. */ struct device_node *__of_node_dup(const struct device_node *np, const char *full_name) { … } /** * 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) { … } EXPORT_SYMBOL(…); static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) { … } static void __of_changeset_entry_invert(struct of_changeset_entry *ce, struct of_changeset_entry *rce) { … } static int __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) { … } static int __of_changeset_entry_apply(struct of_changeset_entry *ce) { … } static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce) { … } /** * of_changeset_init - Initialize a changeset for use * * @ocs: changeset pointer * * Initialize a changeset structure */ void of_changeset_init(struct of_changeset *ocs) { … } EXPORT_SYMBOL_GPL(…); /** * of_changeset_destroy - Destroy a changeset * * @ocs: changeset pointer * * Destroys a changeset. Note that if a changeset is applied, * its changes to the tree cannot be reverted. */ void of_changeset_destroy(struct of_changeset *ocs) { … } EXPORT_SYMBOL_GPL(…); /* * Apply the changeset entries in @ocs. * If apply fails, an attempt is made to revert the entries that were * successfully applied. * * If multiple revert errors occur then only the final revert error is reported. * * Returns 0 on success, a negative error value in case of an error. * If a revert error occurs, it is returned in *ret_revert. */ int __of_changeset_apply_entries(struct of_changeset *ocs, int *ret_revert) { … } /* * Returns 0 on success, a negative error value in case of an error. * * If multiple changeset entry notification errors occur then only the * final notification error is reported. */ int __of_changeset_apply_notify(struct of_changeset *ocs) { … } /* * Returns 0 on success, a negative error value in case of an error. * * If a changeset entry apply fails, an attempt is made to revert any * previous entries in the changeset. If any of the reverts fails, * that failure is not reported. Thus the state of the device tree * is unknown if an apply error occurs. */ static int __of_changeset_apply(struct of_changeset *ocs) { … } /** * of_changeset_apply - Applies a changeset * * @ocs: changeset pointer * * Applies a changeset to the live tree. * Any side-effects of live tree state changes are applied here on * success, like creation/destruction of devices and side-effects * like creation of sysfs properties and directories. * * Return: 0 on success, a negative error value in case of an error. * On error the partially applied effects are reverted. */ int of_changeset_apply(struct of_changeset *ocs) { … } EXPORT_SYMBOL_GPL(…); /* * Revert the changeset entries in @ocs. * If revert fails, an attempt is made to re-apply the entries that were * successfully removed. * * If multiple re-apply errors occur then only the final apply error is * reported. * * Returns 0 on success, a negative error value in case of an error. * If an apply error occurs, it is returned in *ret_apply. */ int __of_changeset_revert_entries(struct of_changeset *ocs, int *ret_apply) { … } /* * If multiple changeset entry notification errors occur then only the * final notification error is reported. */ int __of_changeset_revert_notify(struct of_changeset *ocs) { … } static int __of_changeset_revert(struct of_changeset *ocs) { … } /** * of_changeset_revert - Reverts an applied changeset * * @ocs: changeset pointer * * Reverts a changeset returning the state of the tree to what it * was before the application. * Any side-effects like creation/destruction of devices and * removal of sysfs properties and directories are applied. * * Return: 0 on success, a negative error value in case of an error. */ int of_changeset_revert(struct of_changeset *ocs) { … } EXPORT_SYMBOL_GPL(…); /** * of_changeset_action - Add an action to the tail of the changeset list * * @ocs: changeset pointer * @action: action to perform * @np: Pointer to device node * @prop: Pointer to property * * On action being one of: * + OF_RECONFIG_ATTACH_NODE * + OF_RECONFIG_DETACH_NODE, * + OF_RECONFIG_ADD_PROPERTY * + OF_RECONFIG_REMOVE_PROPERTY, * + OF_RECONFIG_UPDATE_PROPERTY * * Return: 0 on success, a negative error value in case of an error. */ int of_changeset_action(struct of_changeset *ocs, unsigned long action, struct device_node *np, struct property *prop) { … } EXPORT_SYMBOL_GPL(…); static int of_changeset_add_prop_helper(struct of_changeset *ocs, struct device_node *np, const struct property *pp) { … } /** * 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) { … } EXPORT_SYMBOL_GPL(…); /** * 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 * const *str_array, size_t sz) { … } EXPORT_SYMBOL_GPL(…); /** * 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) { … } EXPORT_SYMBOL_GPL(…); /** * of_changeset_add_prop_bool - Add a boolean property (i.e. a property without * any values) to a changeset. * * @ocs: changeset pointer * @np: device node pointer * @prop_name: name of the property to be added * * Create a boolean 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_bool(struct of_changeset *ocs, struct device_node *np, const char *prop_name) { … } EXPORT_SYMBOL_GPL(…);