![Grant Likely](/assets/img/avatar_default.png)
All of the DT modification functions are split into two parts, the first part manipulates the DT data structure, and the second part updates sysfs, but the code isn't very consistent about how the second half is called. They don't all enforce the same rules about when it is valid to update sysfs, and there isn't any clarity on locking. The transactional DT modification feature that is coming also needs access to these functions so that it can perform all the structure changes together, and then all the sysfs updates as a second stage instead of doing each one at a time. Fix up the second have by creating a separate __of_*_sysfs() function for each of the helpers. The new functions have consistent naming (ie. of_node_add() becomes __of_attach_node_sysfs()) and all of them now defer if of_init hasn't been called yet. Callers of the new functions must hold the of_mutex to ensure there are no race conditions with of_init(). The mutex ensures that there will only ever be one writer to the tree at any given time. There can still be any number of readers and the raw_spin_lock is still used to make sure access to the data structure is still consistent. Finally, put the function prototypes into of_private.h so they are accessible to the transaction code. Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> [grant.likely: Changed suffix from _post to _sysfs to match existing code] [grant.likely: Reorganized to eliminate trivial wrappers] Signed-off-by: Grant Likely <grant.likely@linaro.org>
305 lines
6.9 KiB
C
305 lines
6.9 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
#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"
|
|
|
|
/**
|
|
* of_node_get() - Increment refcount of a node
|
|
* @node: Node to inc refcount, NULL is supported to simplify writing of
|
|
* callers
|
|
*
|
|
* Returns node.
|
|
*/
|
|
struct device_node *of_node_get(struct device_node *node)
|
|
{
|
|
if (node)
|
|
kobject_get(&node->kobj);
|
|
return node;
|
|
}
|
|
EXPORT_SYMBOL(of_node_get);
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
if (node)
|
|
kobject_put(&node->kobj);
|
|
}
|
|
EXPORT_SYMBOL(of_node_put);
|
|
|
|
void __of_detach_node_sysfs(struct device_node *np)
|
|
{
|
|
struct property *pp;
|
|
|
|
BUG_ON(!of_node_is_initialized(np));
|
|
if (!of_kset)
|
|
return;
|
|
|
|
/* only remove properties if on sysfs */
|
|
if (of_node_is_attached(np)) {
|
|
for_each_property_of_node(np, pp)
|
|
sysfs_remove_bin_file(&np->kobj, &pp->attr);
|
|
kobject_del(&np->kobj);
|
|
}
|
|
|
|
/* finally remove the kobj_init ref */
|
|
of_node_put(np);
|
|
}
|
|
|
|
static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain);
|
|
|
|
int of_reconfig_notifier_register(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_register(&of_reconfig_chain, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(of_reconfig_notifier_register);
|
|
|
|
int of_reconfig_notifier_unregister(struct notifier_block *nb)
|
|
{
|
|
return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
|
|
}
|
|
EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister);
|
|
|
|
int of_reconfig_notify(unsigned long action, void *p)
|
|
{
|
|
int rc;
|
|
|
|
rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p);
|
|
return notifier_to_errno(rc);
|
|
}
|
|
|
|
int of_property_notify(int action, struct device_node *np,
|
|
struct property *prop)
|
|
{
|
|
struct of_prop_reconfig pr;
|
|
|
|
/* only call notifiers if the node is attached */
|
|
if (!of_node_is_attached(np))
|
|
return 0;
|
|
|
|
pr.dn = np;
|
|
pr.prop = prop;
|
|
return of_reconfig_notify(action, &pr);
|
|
}
|
|
|
|
void __of_attach_node(struct device_node *np)
|
|
{
|
|
np->sibling = np->parent->child;
|
|
np->allnext = np->parent->allnext;
|
|
np->parent->allnext = np;
|
|
np->parent->child = np;
|
|
of_node_clear_flag(np, OF_DETACHED);
|
|
}
|
|
|
|
/**
|
|
* of_attach_node() - Plug a device node into the tree and global list.
|
|
*/
|
|
int of_attach_node(struct device_node *np)
|
|
{
|
|
unsigned long flags;
|
|
int rc;
|
|
|
|
rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
|
|
if (rc)
|
|
return rc;
|
|
|
|
mutex_lock(&of_mutex);
|
|
raw_spin_lock_irqsave(&devtree_lock, flags);
|
|
__of_attach_node(np);
|
|
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
|
|
|
__of_attach_node_sysfs(np);
|
|
mutex_unlock(&of_mutex);
|
|
return 0;
|
|
}
|
|
|
|
void __of_detach_node(struct device_node *np)
|
|
{
|
|
struct device_node *parent;
|
|
|
|
if (WARN_ON(of_node_check_flag(np, OF_DETACHED)))
|
|
return;
|
|
|
|
parent = np->parent;
|
|
if (WARN_ON(!parent))
|
|
return;
|
|
|
|
if (of_allnodes == np)
|
|
of_allnodes = np->allnext;
|
|
else {
|
|
struct device_node *prev;
|
|
for (prev = of_allnodes;
|
|
prev->allnext != np;
|
|
prev = prev->allnext)
|
|
;
|
|
prev->allnext = np->allnext;
|
|
}
|
|
|
|
if (parent->child == np)
|
|
parent->child = np->sibling;
|
|
else {
|
|
struct device_node *prevsib;
|
|
for (prevsib = np->parent->child;
|
|
prevsib->sibling != np;
|
|
prevsib = prevsib->sibling)
|
|
;
|
|
prevsib->sibling = np->sibling;
|
|
}
|
|
|
|
of_node_set_flag(np, OF_DETACHED);
|
|
}
|
|
|
|
/**
|
|
* of_detach_node() - "Unplug" a node from the device tree.
|
|
*
|
|
* The caller must hold a reference to the node. The memory associated with
|
|
* the node is not freed until its refcount goes to zero.
|
|
*/
|
|
int of_detach_node(struct device_node *np)
|
|
{
|
|
unsigned long flags;
|
|
int rc = 0;
|
|
|
|
rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
|
|
if (rc)
|
|
return rc;
|
|
|
|
mutex_lock(&of_mutex);
|
|
raw_spin_lock_irqsave(&devtree_lock, flags);
|
|
__of_detach_node(np);
|
|
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
|
|
|
__of_detach_node_sysfs(np);
|
|
mutex_unlock(&of_mutex);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* of_node_release() - release a dynamically allocated node
|
|
* @kref: kref element 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)
|
|
{
|
|
struct device_node *node = kobj_to_device_node(kobj);
|
|
struct property *prop = node->properties;
|
|
|
|
/* We should never be releasing nodes that haven't been detached. */
|
|
if (!of_node_check_flag(node, OF_DETACHED)) {
|
|
pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name);
|
|
dump_stack();
|
|
return;
|
|
}
|
|
|
|
if (!of_node_check_flag(node, OF_DYNAMIC))
|
|
return;
|
|
|
|
while (prop) {
|
|
struct property *next = prop->next;
|
|
kfree(prop->name);
|
|
kfree(prop->value);
|
|
kfree(prop);
|
|
prop = next;
|
|
|
|
if (!prop) {
|
|
prop = node->deadprops;
|
|
node->deadprops = NULL;
|
|
}
|
|
}
|
|
kfree(node->full_name);
|
|
kfree(node->data);
|
|
kfree(node);
|
|
}
|
|
|
|
/**
|
|
* __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 stucture 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.
|
|
* Returns the newly allocated property or NULL on out of memory error.
|
|
*/
|
|
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags)
|
|
{
|
|
struct property *new;
|
|
|
|
new = kzalloc(sizeof(*new), allocflags);
|
|
if (!new)
|
|
return NULL;
|
|
|
|
/*
|
|
* NOTE: There is no check for zero length value.
|
|
* In case of a boolean property This will allocate a value
|
|
* of zero bytes. We do this to work around the use
|
|
* of of_get_property() calls on boolean values.
|
|
*/
|
|
new->name = kstrdup(prop->name, allocflags);
|
|
new->value = kmemdup(prop->value, prop->length, allocflags);
|
|
new->length = prop->length;
|
|
if (!new->name || !new->value)
|
|
goto err_free;
|
|
|
|
/* mark the property as dynamic */
|
|
of_property_set_flag(new, OF_DYNAMIC);
|
|
|
|
return new;
|
|
|
|
err_free:
|
|
kfree(new->name);
|
|
kfree(new->value);
|
|
kfree(new);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* __of_node_alloc() - Create an empty device node dynamically.
|
|
* @full_name: Full name of the new device node
|
|
* @allocflags: Allocation flags (typically pass GFP_KERNEL)
|
|
*
|
|
* Create an empty device tree node, suitable for further modification.
|
|
* The node data are dynamically allocated and all the node flags
|
|
* have the OF_DYNAMIC & OF_DETACHED bits set.
|
|
* Returns the newly allocated node or NULL on out of memory error.
|
|
*/
|
|
struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags)
|
|
{
|
|
struct device_node *node;
|
|
|
|
node = kzalloc(sizeof(*node), allocflags);
|
|
if (!node)
|
|
return NULL;
|
|
|
|
node->full_name = kstrdup(full_name, allocflags);
|
|
of_node_set_flag(node, OF_DYNAMIC);
|
|
of_node_set_flag(node, OF_DETACHED);
|
|
if (!node->full_name)
|
|
goto err_free;
|
|
|
|
of_node_init(node);
|
|
|
|
return node;
|
|
|
|
err_free:
|
|
kfree(node->full_name);
|
|
kfree(node);
|
|
return NULL;
|
|
}
|