Merge branch 'devicetree/next-overlay' into devicetree/next
Conflicts: drivers/of/testcase-data/testcases.dts
This commit is contained in:
@@ -38,13 +38,15 @@ struct device_node *of_chosen;
|
||||
struct device_node *of_aliases;
|
||||
struct device_node *of_stdout;
|
||||
|
||||
static struct kset *of_kset;
|
||||
struct kset *of_kset;
|
||||
|
||||
/*
|
||||
* Used to protect the of_aliases; but also overloaded to hold off addition of
|
||||
* nodes to sysfs
|
||||
* Used to protect the of_aliases, to hold off addition of nodes to sysfs.
|
||||
* This mutex must be held whenever modifications are being made to the
|
||||
* device tree. The of_{attach,detach}_node() and
|
||||
* of_{add,remove,update}_property() helpers make sure this happens.
|
||||
*/
|
||||
DEFINE_MUTEX(of_aliases_mutex);
|
||||
DEFINE_MUTEX(of_mutex);
|
||||
|
||||
/* use when traversing tree through the allnext, child, sibling,
|
||||
* or parent members of struct device_node.
|
||||
@@ -90,79 +92,7 @@ int __weak of_node_to_nid(struct device_node *np)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
/**
|
||||
* 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);
|
||||
|
||||
static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct device_node, kobj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static 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_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);
|
||||
#else
|
||||
#ifndef CONFIG_OF_DYNAMIC
|
||||
static void of_node_release(struct kobject *kobj)
|
||||
{
|
||||
/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
|
||||
@@ -201,13 +131,16 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name)
|
||||
return name;
|
||||
}
|
||||
|
||||
static int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Important: Don't leak passwords */
|
||||
bool secure = strncmp(pp->name, "security-", 9) == 0;
|
||||
|
||||
if (!of_kset || !of_node_is_attached(np))
|
||||
return 0;
|
||||
|
||||
sysfs_bin_attr_init(&pp->attr);
|
||||
pp->attr.attr.name = safe_name(&np->kobj, pp->name);
|
||||
pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO;
|
||||
@@ -219,12 +152,15 @@ static int __of_add_property_sysfs(struct device_node *np, struct property *pp)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __of_node_add(struct device_node *np)
|
||||
int __of_attach_node_sysfs(struct device_node *np)
|
||||
{
|
||||
const char *name;
|
||||
struct property *pp;
|
||||
int rc;
|
||||
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
|
||||
np->kobj.kset = of_kset;
|
||||
if (!np->parent) {
|
||||
/* Nodes without parents are new top level trees */
|
||||
@@ -246,59 +182,20 @@ static int __of_node_add(struct device_node *np)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int of_node_add(struct device_node *np)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
BUG_ON(!of_node_is_initialized(np));
|
||||
|
||||
/*
|
||||
* Grab the mutex here so that in a race condition between of_init() and
|
||||
* of_node_add(), node addition will still be consistent.
|
||||
*/
|
||||
mutex_lock(&of_aliases_mutex);
|
||||
if (of_kset)
|
||||
rc = __of_node_add(np);
|
||||
else
|
||||
/* This scenario may be perfectly valid, but report it anyway */
|
||||
pr_info("of_node_add(%s) before of_init()\n", np->full_name);
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
static void of_node_remove(struct device_node *np)
|
||||
{
|
||||
struct property *pp;
|
||||
|
||||
BUG_ON(!of_node_is_initialized(np));
|
||||
|
||||
/* 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);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init of_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
/* Create the kset, and register existing nodes */
|
||||
mutex_lock(&of_aliases_mutex);
|
||||
mutex_lock(&of_mutex);
|
||||
of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
|
||||
if (!of_kset) {
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
mutex_unlock(&of_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
for_each_of_allnodes(np)
|
||||
__of_node_add(np);
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
__of_attach_node_sysfs(np);
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
/* Symlink in /proc as required by userspace ABI */
|
||||
if (of_allnodes)
|
||||
@@ -370,8 +267,8 @@ EXPORT_SYMBOL(of_find_all_nodes);
|
||||
* Find a property with a given name for a given node
|
||||
* and return the value.
|
||||
*/
|
||||
static const void *__of_get_property(const struct device_node *np,
|
||||
const char *name, int *lenp)
|
||||
const void *__of_get_property(const struct device_node *np,
|
||||
const char *name, int *lenp)
|
||||
{
|
||||
struct property *pp = __of_find_property(np, name, lenp);
|
||||
|
||||
@@ -1749,32 +1646,10 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na
|
||||
}
|
||||
EXPORT_SYMBOL(of_count_phandle_with_args);
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
static 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);
|
||||
}
|
||||
#else
|
||||
static int of_property_notify(int action, struct device_node *np,
|
||||
struct property *prop)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* __of_add_property - Add a property to a node without lock operations
|
||||
*/
|
||||
static int __of_add_property(struct device_node *np, struct property *prop)
|
||||
int __of_add_property(struct device_node *np, struct property *prop)
|
||||
{
|
||||
struct property **next;
|
||||
|
||||
@@ -1800,22 +1675,49 @@ int of_add_property(struct device_node *np, struct property *prop)
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop);
|
||||
if (rc)
|
||||
return rc;
|
||||
mutex_lock(&of_mutex);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
rc = __of_add_property(np, prop);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (of_node_is_attached(np))
|
||||
if (!rc)
|
||||
__of_add_property_sysfs(np, prop);
|
||||
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
if (!rc)
|
||||
of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __of_remove_property(struct device_node *np, struct property *prop)
|
||||
{
|
||||
struct property **next;
|
||||
|
||||
for (next = &np->properties; *next; next = &(*next)->next) {
|
||||
if (*next == prop)
|
||||
break;
|
||||
}
|
||||
if (*next == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
/* found the node */
|
||||
*next = prop->next;
|
||||
prop->next = np->deadprops;
|
||||
np->deadprops = prop;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
|
||||
{
|
||||
/* at early boot, bail here and defer setup to of_init() */
|
||||
if (of_kset && of_node_is_attached(np))
|
||||
sysfs_remove_bin_file(&np->kobj, &prop->attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_remove_property - Remove a property from a node.
|
||||
*
|
||||
@@ -1826,42 +1728,64 @@ int of_add_property(struct device_node *np, struct property *prop)
|
||||
*/
|
||||
int of_remove_property(struct device_node *np, struct property *prop)
|
||||
{
|
||||
struct property **next;
|
||||
unsigned long flags;
|
||||
int found = 0;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop);
|
||||
if (rc)
|
||||
return rc;
|
||||
mutex_lock(&of_mutex);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
next = &np->properties;
|
||||
while (*next) {
|
||||
if (*next == prop) {
|
||||
/* found the node */
|
||||
*next = prop->next;
|
||||
prop->next = np->deadprops;
|
||||
np->deadprops = prop;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
rc = __of_remove_property(np, prop);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
if (!found)
|
||||
return -ENODEV;
|
||||
if (!rc)
|
||||
__of_remove_property_sysfs(np, prop);
|
||||
|
||||
/* at early boot, bail hear and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
sysfs_remove_bin_file(&np->kobj, &prop->attr);
|
||||
if (!rc)
|
||||
of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int __of_update_property(struct device_node *np, struct property *newprop,
|
||||
struct property **oldpropp)
|
||||
{
|
||||
struct property **next, *oldprop;
|
||||
|
||||
for (next = &np->properties; *next; next = &(*next)->next) {
|
||||
if (of_prop_cmp((*next)->name, newprop->name) == 0)
|
||||
break;
|
||||
}
|
||||
*oldpropp = oldprop = *next;
|
||||
|
||||
if (oldprop) {
|
||||
/* replace the node */
|
||||
newprop->next = oldprop->next;
|
||||
*next = newprop;
|
||||
oldprop->next = np->deadprops;
|
||||
np->deadprops = oldprop;
|
||||
} else {
|
||||
/* new node */
|
||||
newprop->next = NULL;
|
||||
*next = newprop;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __of_update_property_sysfs(struct device_node *np, struct property *newprop,
|
||||
struct property *oldprop)
|
||||
{
|
||||
/* At early boot, bail out and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return;
|
||||
|
||||
if (oldprop)
|
||||
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
|
||||
__of_add_property_sysfs(np, newprop);
|
||||
}
|
||||
|
||||
/*
|
||||
* of_update_property - Update a property in a node, if the property does
|
||||
* not exist, add it.
|
||||
@@ -1873,164 +1797,29 @@ int of_remove_property(struct device_node *np, struct property *prop)
|
||||
*/
|
||||
int of_update_property(struct device_node *np, struct property *newprop)
|
||||
{
|
||||
struct property **next, *oldprop;
|
||||
struct property *oldprop;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!newprop->name)
|
||||
return -EINVAL;
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
next = &np->properties;
|
||||
oldprop = __of_find_property(np, newprop->name, NULL);
|
||||
if (!oldprop) {
|
||||
/* add the new node */
|
||||
rc = __of_add_property(np, newprop);
|
||||
} else while (*next) {
|
||||
/* replace the node */
|
||||
if (*next == oldprop) {
|
||||
newprop->next = oldprop->next;
|
||||
*next = newprop;
|
||||
oldprop->next = np->deadprops;
|
||||
np->deadprops = oldprop;
|
||||
break;
|
||||
}
|
||||
next = &(*next)->next;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* At early boot, bail out and defer setup to of_init() */
|
||||
if (!of_kset)
|
||||
return 0;
|
||||
|
||||
/* Update the sysfs attribute */
|
||||
if (oldprop)
|
||||
sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
|
||||
__of_add_property_sysfs(np, newprop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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);
|
||||
rc = __of_update_property(np, newprop, &oldprop);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
of_node_add(np);
|
||||
return 0;
|
||||
}
|
||||
if (!rc)
|
||||
__of_update_property_sysfs(np, newprop, oldprop);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct device_node *parent;
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!rc)
|
||||
of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop, oldprop);
|
||||
|
||||
raw_spin_lock_irqsave(&devtree_lock, flags);
|
||||
|
||||
if (of_node_check_flag(np, OF_DETACHED)) {
|
||||
/* someone already detached it */
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
parent = np->parent;
|
||||
if (!parent) {
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
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);
|
||||
raw_spin_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
of_node_remove(np);
|
||||
return rc;
|
||||
}
|
||||
#endif /* defined(CONFIG_OF_DYNAMIC) */
|
||||
|
||||
static void of_alias_add(struct alias_prop *ap, struct device_node *np,
|
||||
int id, const char *stem, int stem_len)
|
||||
@@ -2126,7 +1915,7 @@ int of_alias_get_id(struct device_node *np, const char *stem)
|
||||
struct alias_prop *app;
|
||||
int id = -ENODEV;
|
||||
|
||||
mutex_lock(&of_aliases_mutex);
|
||||
mutex_lock(&of_mutex);
|
||||
list_for_each_entry(app, &aliases_lookup, link) {
|
||||
if (strcmp(app->stem, stem) != 0)
|
||||
continue;
|
||||
@@ -2136,7 +1925,7 @@ int of_alias_get_id(struct device_node *np, const char *stem)
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&of_aliases_mutex);
|
||||
mutex_unlock(&of_mutex);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
Reference in New Issue
Block a user