Commit 663d3f7c authored by Grant Likely's avatar Grant Likely

Merge branch 'devicetree/next-overlay' into devicetree/next

Conflicts:
	drivers/of/testcase-data/testcases.dts
parents b775e642 b6ae5dc5
A DT changeset is a method which allows one to apply changes
in the live tree in such a way that either the full set of changes
will be applied, or none of them will be. If an error occurs partway
through applying the changeset, then the tree will be rolled back to the
previous state. A changeset can also be removed after it has been
applied.
When a changeset is applied, all of the changes get applied to the tree
at once before emitting OF_RECONFIG notifiers. This is so that the
receiver sees a complete and consistent state of the tree when it
receives the notifier.
The sequence of a changeset is as follows.
1. of_changeset_init() - initializes a changeset
2. A number of DT tree change calls, of_changeset_attach_node(),
of_changeset_detach_node(), of_changeset_add_property(),
of_changeset_remove_property, of_changeset_update_property() to prepare
a set of changes. No changes to the active tree are made at this point.
All the change operations are recorded in the of_changeset 'entries'
list.
3. mutex_lock(of_mutex) - starts a changeset; The global of_mutex
ensures there can only be one editor at a time.
4. of_changeset_apply() - Apply the changes to the tree. Either the
entire changeset will get applied, or if there is an error the tree will
be restored to the previous state
5. mutex_unlock(of_mutex) - All operations complete, release the mutex
If a successfully applied changeset needs to be removed, it can be done
with the following sequence.
1. mutex_lock(of_mutex)
2. of_changeset_revert()
3. mutex_unlock(of_mutex)
...@@ -821,76 +821,6 @@ int cpu_to_chip_id(int cpu) ...@@ -821,76 +821,6 @@ int cpu_to_chip_id(int cpu)
} }
EXPORT_SYMBOL(cpu_to_chip_id); EXPORT_SYMBOL(cpu_to_chip_id);
#ifdef CONFIG_PPC_PSERIES
/*
* Fix up the uninitialized fields in a new device node:
* name, type and pci-specific fields
*/
static int of_finish_dynamic_node(struct device_node *node)
{
struct device_node *parent = of_get_parent(node);
int err = 0;
const phandle *ibm_phandle;
node->name = of_get_property(node, "name", NULL);
node->type = of_get_property(node, "device_type", NULL);
if (!node->name)
node->name = "<NULL>";
if (!node->type)
node->type = "<NULL>";
if (!parent) {
err = -ENODEV;
goto out;
}
/* We don't support that function on PowerMac, at least
* not yet
*/
if (machine_is(powermac))
return -ENODEV;
/* fix up new node's phandle field */
if ((ibm_phandle = of_get_property(node, "ibm,phandle", NULL)))
node->phandle = *ibm_phandle;
out:
of_node_put(parent);
return err;
}
static int prom_reconfig_notifier(struct notifier_block *nb,
unsigned long action, void *node)
{
int err;
switch (action) {
case OF_RECONFIG_ATTACH_NODE:
err = of_finish_dynamic_node(node);
if (err < 0)
printk(KERN_ERR "finish_node returned %d\n", err);
break;
default:
err = 0;
break;
}
return notifier_from_errno(err);
}
static struct notifier_block prom_reconfig_nb = {
.notifier_call = prom_reconfig_notifier,
.priority = 10, /* This one needs to run first */
};
static int __init prom_reconfig_setup(void)
{
return of_reconfig_notifier_register(&prom_reconfig_nb);
}
__initcall(prom_reconfig_setup);
#endif
bool arch_match_cpu_phys_id(int cpu, u64 phys_id) bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
{ {
return (int)phys_id == get_hard_smp_processor_id(cpu); return (int)phys_id == get_hard_smp_processor_id(cpu);
......
...@@ -194,7 +194,7 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr) ...@@ -194,7 +194,7 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr)
if (!memblock_size) if (!memblock_size)
return -EINVAL; return -EINVAL;
p = (u32 *)of_get_property(pr->dn, "ibm,dynamic-memory", NULL); p = (u32 *) pr->old_prop->value;
if (!p) if (!p)
return -EINVAL; return -EINVAL;
......
...@@ -936,28 +936,14 @@ static int nx842_OF_upd(struct property *new_prop) ...@@ -936,28 +936,14 @@ static int nx842_OF_upd(struct property *new_prop)
goto error_out; goto error_out;
} }
/* Set ptr to new property if provided */
if (new_prop) {
/* Single property */
if (!strncmp(new_prop->name, "status", new_prop->length)) {
status = new_prop;
} else if (!strncmp(new_prop->name, "ibm,max-sg-len",
new_prop->length)) {
maxsglen = new_prop;
} else if (!strncmp(new_prop->name, "ibm,max-sync-cop",
new_prop->length)) {
maxsyncop = new_prop;
} else {
/* /*
* Skip the update, the property being updated * If this is a property update, there are only certain properties that
* has no impact. * we care about. Bail if it isn't in the below list
*/ */
if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) ||
strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) ||
strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length)))
goto out; goto out;
}
}
/* Perform property updates */ /* Perform property updates */
ret = nx842_OF_upd_status(new_devdata, status); ret = nx842_OF_upd_status(new_devdata, status);
......
obj-y = base.o device.o platform.o obj-y = base.o device.o platform.o
obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_FLATTREE) += fdt.o
obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_PROMTREE) += pdt.o
......
...@@ -38,13 +38,15 @@ struct device_node *of_chosen; ...@@ -38,13 +38,15 @@ struct device_node *of_chosen;
struct device_node *of_aliases; struct device_node *of_aliases;
struct device_node *of_stdout; 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 * Used to protect the of_aliases, to hold off addition of nodes to sysfs.
* 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, /* use when traversing tree through the allnext, child, sibling,
* or parent members of struct device_node. * or parent members of struct device_node.
...@@ -90,79 +92,7 @@ int __weak of_node_to_nid(struct device_node *np) ...@@ -90,79 +92,7 @@ int __weak of_node_to_nid(struct device_node *np)
} }
#endif #endif
#if defined(CONFIG_OF_DYNAMIC) #ifndef 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
static void of_node_release(struct kobject *kobj) static void of_node_release(struct kobject *kobj)
{ {
/* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */
...@@ -201,13 +131,16 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name) ...@@ -201,13 +131,16 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name)
return 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; int rc;
/* Important: Don't leak passwords */ /* Important: Don't leak passwords */
bool secure = strncmp(pp->name, "security-", 9) == 0; bool secure = strncmp(pp->name, "security-", 9) == 0;
if (!of_kset || !of_node_is_attached(np))
return 0;
sysfs_bin_attr_init(&pp->attr); sysfs_bin_attr_init(&pp->attr);
pp->attr.attr.name = safe_name(&np->kobj, pp->name); pp->attr.attr.name = safe_name(&np->kobj, pp->name);
pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO; 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) ...@@ -219,12 +152,15 @@ static int __of_add_property_sysfs(struct device_node *np, struct property *pp)
return rc; return rc;
} }
static int __of_node_add(struct device_node *np) int __of_attach_node_sysfs(struct device_node *np)
{ {
const char *name; const char *name;
struct property *pp; struct property *pp;
int rc; int rc;
if (!of_kset)
return 0;
np->kobj.kset = of_kset; np->kobj.kset = of_kset;
if (!np->parent) { if (!np->parent) {
/* Nodes without parents are new top level trees */ /* Nodes without parents are new top level trees */
...@@ -246,59 +182,20 @@ static int __of_node_add(struct device_node *np) ...@@ -246,59 +182,20 @@ static int __of_node_add(struct device_node *np)
return 0; 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) static int __init of_init(void)
{ {
struct device_node *np; struct device_node *np;
/* Create the kset, and register existing nodes */ /* 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); of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
if (!of_kset) { if (!of_kset) {
mutex_unlock(&of_aliases_mutex); mutex_unlock(&of_mutex);
return -ENOMEM; return -ENOMEM;
} }
for_each_of_allnodes(np) for_each_of_allnodes(np)
__of_node_add(np); __of_attach_node_sysfs(np);
mutex_unlock(&of_aliases_mutex); mutex_unlock(&of_mutex);
/* Symlink in /proc as required by userspace ABI */ /* Symlink in /proc as required by userspace ABI */
if (of_allnodes) if (of_allnodes)
...@@ -370,7 +267,7 @@ EXPORT_SYMBOL(of_find_all_nodes); ...@@ -370,7 +267,7 @@ EXPORT_SYMBOL(of_find_all_nodes);
* Find a property with a given name for a given node * Find a property with a given name for a given node
* and return the value. * and return the value.
*/ */
static const void *__of_get_property(const struct device_node *np, const void *__of_get_property(const struct device_node *np,
const char *name, int *lenp) const char *name, int *lenp)
{ {
struct property *pp = __of_find_property(np, name, 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 ...@@ -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); 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 * __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; struct property **next;
...@@ -1800,22 +1675,49 @@ int of_add_property(struct device_node *np, struct property *prop) ...@@ -1800,22 +1675,49 @@ int of_add_property(struct device_node *np, struct property *prop)
unsigned long flags; unsigned long flags;
int rc; int rc;
rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop); mutex_lock(&of_mutex);
if (rc)
return rc;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
rc = __of_add_property(np, prop); rc = __of_add_property(np, prop);
raw_spin_unlock_irqrestore(&devtree_lock, flags); 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); __of_add_property_sysfs(np, prop);
mutex_unlock(&of_mutex);
if (!rc)
of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop, NULL);
return rc; 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. * of_remove_property - Remove a property from a node.
* *
...@@ -1826,211 +1728,98 @@ int of_add_property(struct device_node *np, struct property *prop) ...@@ -1826,211 +1728,98 @@ int of_add_property(struct device_node *np, struct property *prop)
*/ */
int of_remove_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; unsigned long flags;
int found = 0;
int rc; int rc;
rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop); mutex_lock(&of_mutex);
if (rc)
return rc;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
next = &np->properties; rc = __of_remove_property(np, prop);
while (*next) {
if (*next == prop) {
/* found the node */
*next = prop->next;
prop->next = np->deadprops;
np->deadprops = prop;
found = 1;
break;
}
next = &(*next)->next;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags); raw_spin_unlock_irqrestore(&devtree_lock, flags);
if (!found) if (!rc)
return -ENODEV; __of_remove_property_sysfs(np, prop);
/* at early boot, bail hear and defer setup to of_init() */ mutex_unlock(&of_mutex);
if (!of_kset)
return 0;
sysfs_remove_bin_file(&np->kobj, &prop->attr); if (!rc)
of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop, NULL);
return 0; return rc;
} }
/* int __of_update_property(struct device_node *np, struct property *newprop,
* of_update_property - Update a property in a node, if the property does struct property **oldpropp)
* not exist, add it.
*
* Note that we don't actually remove it, since we have given out
* who-knows-how-many pointers to the data using get-property.
* Instead we just move the property to the "dead properties" list,
* and add the new property to the property list
*/
int of_update_property(struct device_node *np, struct property *newprop)
{ {
struct property **next, *oldprop; struct property **next, *oldprop;
unsigned long flags;
int rc;
rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
if (rc)
return rc;
if (!newprop->name) for (next = &np->properties; *next; next = &(*next)->next) {
return -EINVAL; if (of_prop_cmp((*next)->name, newprop->name) == 0)
break;
}
*oldpropp = oldprop = *next;
raw_spin_lock_irqsave(&devtree_lock, flags); if (oldprop) {
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 */ /* replace the node */
if (*next == oldprop) {
newprop->next = oldprop->next; newprop->next = oldprop->next;
*next = newprop; *next = newprop;
oldprop->next = np->deadprops; oldprop->next = np->deadprops;
np->deadprops = oldprop; np->deadprops = oldprop;
break; } else {
} /* new node */
next = &(*next)->next; newprop->next = NULL;
*next = newprop;
} }
raw_spin_unlock_irqrestore(&devtree_lock, flags);
if (rc)
return rc;
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() */ /* At early boot, bail out and defer setup to of_init() */
if (!of_kset) if (!of_kset)
return 0; return;
/* Update the sysfs attribute */
if (oldprop) if (oldprop)
sysfs_remove_bin_file(&np->kobj, &oldprop->attr); sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
__of_add_property_sysfs(np, newprop); __of_add_property_sysfs(np, newprop);
return 0;
} }
#if defined(CONFIG_OF_DYNAMIC)
/* /*
* Support for dynamic device trees. * of_update_property - Update a property in a node, if the property does
* not exist, add it.
* *
* On some platforms, the device tree can be manipulated at runtime. * Note that we don't actually remove it, since we have given out
* The routines in this section support adding, removing and changing * who-knows-how-many pointers to the data using get-property.
* device tree nodes. * Instead we just move the property to the "dead properties" list,
*/ * and add the new property to the property list
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) int of_update_property(struct device_node *np, struct property *newprop)
{ {
struct property *oldprop;
unsigned long flags; unsigned long flags;
int rc; int rc;
rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); if (!newprop->name)
if (rc) return -EINVAL;
return rc;
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);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
of_node_add(np);
return 0;
}
/**
* 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;
rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); mutex_lock(&of_mutex);
if (rc)
return rc;
raw_spin_lock_irqsave(&devtree_lock, flags); raw_spin_lock_irqsave(&devtree_lock, flags);
rc = __of_update_property(np, newprop, &oldprop);
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); raw_spin_unlock_irqrestore(&devtree_lock, flags);
return rc;
}
if (of_allnodes == np) if (!rc)
of_allnodes = np->allnext; __of_update_property_sysfs(np, newprop, oldprop);
else {
struct device_node *prev;
for (prev = of_allnodes;
prev->allnext != np;
prev = prev->allnext)
;
prev->allnext = np->allnext;
}
if (parent->child == np) mutex_unlock(&of_mutex);
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); if (!rc)
raw_spin_unlock_irqrestore(&devtree_lock, flags); of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop, oldprop);
of_node_remove(np);
return rc; return rc;
} }
#endif /* defined(CONFIG_OF_DYNAMIC) */
static void of_alias_add(struct alias_prop *ap, struct device_node *np, static void of_alias_add(struct alias_prop *ap, struct device_node *np,
int id, const char *stem, int stem_len) int id, const char *stem, int stem_len)
...@@ -2126,7 +1915,7 @@ int of_alias_get_id(struct device_node *np, const char *stem) ...@@ -2126,7 +1915,7 @@ int of_alias_get_id(struct device_node *np, const char *stem)
struct alias_prop *app; struct alias_prop *app;
int id = -ENODEV; int id = -ENODEV;
mutex_lock(&of_aliases_mutex); mutex_lock(&of_mutex);
list_for_each_entry(app, &aliases_lookup, link) { list_for_each_entry(app, &aliases_lookup, link) {
if (strcmp(app->stem, stem) != 0) if (strcmp(app->stem, stem) != 0)
continue; continue;
...@@ -2136,7 +1925,7 @@ int of_alias_get_id(struct device_node *np, const char *stem) ...@@ -2136,7 +1925,7 @@ int of_alias_get_id(struct device_node *np, const char *stem)
break; break;
} }
} }
mutex_unlock(&of_aliases_mutex); mutex_unlock(&of_mutex);
return id; return id;
} }
......
...@@ -160,7 +160,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -160,7 +160,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen);
seen = 0; seen = 0;
mutex_lock(&of_aliases_mutex); mutex_lock(&of_mutex);
list_for_each_entry(app, &aliases_lookup, link) { list_for_each_entry(app, &aliases_lookup, link) {
if (dev->of_node == app->np) { if (dev->of_node == app->np) {
add_uevent_var(env, "OF_ALIAS_%d=%s", seen, add_uevent_var(env, "OF_ALIAS_%d=%s", seen,
...@@ -168,7 +168,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -168,7 +168,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
seen++; seen++;
} }
} }
mutex_unlock(&of_aliases_mutex); mutex_unlock(&of_mutex);
} }
int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env)
......
/*
* 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 property *oldprop)
{
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;
pr.old_prop = oldprop;
return of_reconfig_notify(action, &pr);
}
void __of_attach_node(struct device_node *np)
{
const __be32 *phandle;
int sz;
np->name = __of_get_property(np, "name", NULL) ? : "<NULL>";
np->type = __of_get_property(np, "device_type", NULL) ? : "<NULL>";
phandle = __of_get_property(np, "phandle", &sz);
if (!phandle)
phandle = __of_get_property(np, "linux,phandle", &sz);
if (IS_ENABLED(PPC_PSERIES) && !phandle)
phandle = __of_get_property(np, "ibm,phandle", &sz);
np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0;
np->child = NULL;
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;
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);
of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np);
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;
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);
of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np);
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;
}
static void __of_changeset_entry_destroy(struct of_changeset_entry *ce)
{
of_node_put(ce->np);
list_del(&ce->node);
kfree(ce);
}
#ifdef DEBUG
static void __of_changeset_entry_dump(struct of_changeset_entry *ce)
{
switch (ce->action) {
case OF_RECONFIG_ADD_PROPERTY:
pr_debug("%p: %s %s/%s\n",
ce, "ADD_PROPERTY ", ce->np->full_name,
ce->prop->name);
break;
case OF_RECONFIG_REMOVE_PROPERTY:
pr_debug("%p: %s %s/%s\n",
ce, "REMOVE_PROPERTY", ce->np->full_name,
ce->prop->name);
break;
case OF_RECONFIG_UPDATE_PROPERTY:
pr_debug("%p: %s %s/%s\n",
ce, "UPDATE_PROPERTY", ce->np->full_name,
ce->prop->name);
break;
case OF_RECONFIG_ATTACH_NODE:
pr_debug("%p: %s %s\n",
ce, "ATTACH_NODE ", ce->np->full_name);
break;
case OF_RECONFIG_DETACH_NODE:
pr_debug("%p: %s %s\n",
ce, "DETACH_NODE ", ce->np->full_name);
break;
}
}
#else
static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce)
{
/* empty */
}
#endif
static void __of_changeset_entry_invert(struct of_changeset_entry *ce,
struct of_changeset_entry *rce)
{
memcpy(rce, ce, sizeof(*rce));
switch (ce->action) {
case OF_RECONFIG_ATTACH_NODE:
rce->action = OF_RECONFIG_DETACH_NODE;
break;
case OF_RECONFIG_DETACH_NODE:
rce->action = OF_RECONFIG_ATTACH_NODE;
break;
case OF_RECONFIG_ADD_PROPERTY:
rce->action = OF_RECONFIG_REMOVE_PROPERTY;
break;
case OF_RECONFIG_REMOVE_PROPERTY:
rce->action = OF_RECONFIG_ADD_PROPERTY;
break;
case OF_RECONFIG_UPDATE_PROPERTY:
rce->old_prop = ce->prop;
rce->prop = ce->old_prop;
break;
}
}
static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert)
{
struct of_changeset_entry ce_inverted;
int ret;
if (revert) {
__of_changeset_entry_invert(ce, &ce_inverted);
ce = &ce_inverted;
}
switch (ce->action) {
case OF_RECONFIG_ATTACH_NODE:
case OF_RECONFIG_DETACH_NODE:
ret = of_reconfig_notify(ce->action, ce->np);
break;
case OF_RECONFIG_ADD_PROPERTY:
case OF_RECONFIG_REMOVE_PROPERTY:
case OF_RECONFIG_UPDATE_PROPERTY:
ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop);
break;
default:
pr_err("%s: invalid devicetree changeset action: %i\n", __func__,
(int)ce->action);
return;
}
if (ret)
pr_err("%s: notifier error @%s\n", __func__, ce->np->full_name);
}
static int __of_changeset_entry_apply(struct of_changeset_entry *ce)
{
struct property *old_prop, **propp;
unsigned long flags;
int ret = 0;
__of_changeset_entry_dump(ce);
raw_spin_lock_irqsave(&devtree_lock, flags);
switch (ce->action) {
case OF_RECONFIG_ATTACH_NODE:
__of_attach_node(ce->np);
break;
case OF_RECONFIG_DETACH_NODE:
__of_detach_node(ce->np);
break;
case OF_RECONFIG_ADD_PROPERTY:
/* If the property is in deadprops then it must be removed */
for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
if (*propp == ce->prop) {
*propp = ce->prop->next;
ce->prop->next = NULL;
break;
}
}
ret = __of_add_property(ce->np, ce->prop);
if (ret) {
pr_err("%s: add_property failed @%s/%s\n",
__func__, ce->np->full_name,
ce->prop->name);
break;
}
break;
case OF_RECONFIG_REMOVE_PROPERTY:
ret = __of_remove_property(ce->np, ce->prop);
if (ret) {
pr_err("%s: remove_property failed @%s/%s\n",
__func__, ce->np->full_name,
ce->prop->name);
break;
}
break;
case OF_RECONFIG_UPDATE_PROPERTY:
/* If the property is in deadprops then it must be removed */
for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) {
if (*propp == ce->prop) {
*propp = ce->prop->next;
ce->prop->next = NULL;
break;
}
}
ret = __of_update_property(ce->np, ce->prop, &old_prop);
if (ret) {
pr_err("%s: update_property failed @%s/%s\n",
__func__, ce->np->full_name,
ce->prop->name);
break;
}
break;
default:
ret = -EINVAL;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
if (ret)
return ret;
switch (ce->action) {
case OF_RECONFIG_ATTACH_NODE:
__of_attach_node_sysfs(ce->np);
break;
case OF_RECONFIG_DETACH_NODE:
__of_detach_node_sysfs(ce->np);
break;
case OF_RECONFIG_ADD_PROPERTY:
/* ignore duplicate names */
__of_add_property_sysfs(ce->np, ce->prop);
break;
case OF_RECONFIG_REMOVE_PROPERTY:
__of_remove_property_sysfs(ce->np, ce->prop);
break;
case OF_RECONFIG_UPDATE_PROPERTY:
__of_update_property_sysfs(ce->np, ce->prop, ce->old_prop);
break;
}
return 0;
}
static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce)
{
struct of_changeset_entry ce_inverted;
__of_changeset_entry_invert(ce, &ce_inverted);
return __of_changeset_entry_apply(&ce_inverted);
}
/**
* of_changeset_init - Initialize a changeset for use
*
* @ocs: changeset pointer
*
* Initialize a changeset structure
*/
void of_changeset_init(struct of_changeset *ocs)
{
memset(ocs, 0, sizeof(*ocs));
INIT_LIST_HEAD(&ocs->entries);
}
/**
* 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)
{
struct of_changeset_entry *ce, *cen;
list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node)
__of_changeset_entry_destroy(ce);
}
/**
* 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
* sucess, like creation/destruction of devices and side-effects
* like creation of sysfs properties and directories.
* Returns 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)
{
struct of_changeset_entry *ce;
int ret;
/* perform the rest of the work */
pr_debug("of_changeset: applying...\n");
list_for_each_entry(ce, &ocs->entries, node) {
ret = __of_changeset_entry_apply(ce);
if (ret) {
pr_err("%s: Error applying changeset (%d)\n", __func__, ret);
list_for_each_entry_continue_reverse(ce, &ocs->entries, node)
__of_changeset_entry_revert(ce);
return ret;
}
}
pr_debug("of_changeset: applied, emitting notifiers.\n");
/* drop the global lock while emitting notifiers */
mutex_unlock(&of_mutex);
list_for_each_entry(ce, &ocs->entries, node)
__of_changeset_entry_notify(ce, 0);
mutex_lock(&of_mutex);
pr_debug("of_changeset: notifiers sent.\n");
return 0;
}
/**
* 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.
* Returns 0 on success, a negative error value in case of an error.
*/
int of_changeset_revert(struct of_changeset *ocs)
{
struct of_changeset_entry *ce;
int ret;
pr_debug("of_changeset: reverting...\n");
list_for_each_entry_reverse(ce, &ocs->entries, node) {
ret = __of_changeset_entry_revert(ce);
if (ret) {
pr_err("%s: Error reverting changeset (%d)\n", __func__, ret);
list_for_each_entry_continue(ce, &ocs->entries, node)
__of_changeset_entry_apply(ce);
return ret;
}
}
pr_debug("of_changeset: reverted, emitting notifiers.\n");
/* drop the global lock while emitting notifiers */
mutex_unlock(&of_mutex);
list_for_each_entry_reverse(ce, &ocs->entries, node)
__of_changeset_entry_notify(ce, 1);
mutex_lock(&of_mutex);
pr_debug("of_changeset: notifiers sent.\n");
return 0;
}
/**
* of_changeset_action - Perform a changeset action
*
* @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
* Returns 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)
{
struct of_changeset_entry *ce;
ce = kzalloc(sizeof(*ce), GFP_KERNEL);
if (!ce) {
pr_err("%s: Failed to allocate\n", __func__);
return -ENOMEM;
}
/* get a reference to the node */
ce->action = action;
ce->np = of_node_get(np);
ce->prop = prop;
if (action == OF_RECONFIG_UPDATE_PROPERTY && prop)
ce->old_prop = of_find_property(np, prop->name, NULL);
/* add it to the list */
list_add_tail(&ce->node, &ocs->entries);
return 0;
}
...@@ -31,6 +31,63 @@ struct alias_prop { ...@@ -31,6 +31,63 @@ struct alias_prop {
char stem[0]; char stem[0];
}; };
extern struct mutex of_aliases_mutex; extern struct mutex of_mutex;
extern struct list_head aliases_lookup; extern struct list_head aliases_lookup;
extern struct kset *of_kset;
static inline struct device_node *kobj_to_device_node(struct kobject *kobj)
{
return container_of(kobj, struct device_node, kobj);
}
#if defined(CONFIG_OF_DYNAMIC)
extern int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *old_prop);
extern void of_node_release(struct kobject *kobj);
#else /* CONFIG_OF_DYNAMIC */
static inline int of_property_notify(int action, struct device_node *np,
struct property *prop, struct property *old_prop)
{
return 0;
}
#endif /* CONFIG_OF_DYNAMIC */
/**
* General utilities for working with live trees.
*
* All functions with two leading underscores operate
* without taking node references, so you either have to
* own the devtree lock or work on detached trees only.
*/
struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags);
struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags);
extern const void *__of_get_property(const struct device_node *np,
const char *name, int *lenp);
extern int __of_add_property(struct device_node *np, struct property *prop);
extern int __of_add_property_sysfs(struct device_node *np,
struct property *prop);
extern int __of_remove_property(struct device_node *np, struct property *prop);
extern void __of_remove_property_sysfs(struct device_node *np,
struct property *prop);
extern int __of_update_property(struct device_node *np,
struct property *newprop, struct property **oldprop);
extern void __of_update_property_sysfs(struct device_node *np,
struct property *newprop, struct property *oldprop);
extern void __of_attach_node(struct device_node *np);
extern int __of_attach_node_sysfs(struct device_node *np);
extern void __of_detach_node(struct device_node *np);
extern void __of_detach_node_sysfs(struct device_node *np);
/* iterators for transactions, used for overlays */
/* forward iterator */
#define for_each_transaction_entry(_oft, _te) \
list_for_each_entry(_te, &(_oft)->te_list, node)
/* reverse iterator */
#define for_each_transaction_entry_reverse(_oft, _te) \
list_for_each_entry_reverse(_te, &(_oft)->te_list, node)
#endif /* _LINUX_OF_PRIVATE_H */ #endif /* _LINUX_OF_PRIVATE_H */
...@@ -422,6 +422,7 @@ static int of_platform_bus_create(struct device_node *bus, ...@@ -422,6 +422,7 @@ static int of_platform_bus_create(struct device_node *bus,
break; break;
} }
} }
of_node_set_flag(bus, OF_POPULATED_BUS);
return rc; return rc;
} }
...@@ -508,19 +509,13 @@ EXPORT_SYMBOL_GPL(of_platform_populate); ...@@ -508,19 +509,13 @@ EXPORT_SYMBOL_GPL(of_platform_populate);
static int of_platform_device_destroy(struct device *dev, void *data) static int of_platform_device_destroy(struct device *dev, void *data)
{ {
bool *children_left = data;
/* Do not touch devices not populated from the device tree */ /* Do not touch devices not populated from the device tree */
if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED))
*children_left = true;
return 0; return 0;
}
/* Recurse, but don't touch this device if it has any children left */ /* Recurse for any nodes that were treated as busses */
if (of_platform_depopulate(dev) != 0) { if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS))
*children_left = true; device_for_each_child(dev, NULL, of_platform_device_destroy);
return 0;
}
if (dev->bus == &platform_bus_type) if (dev->bus == &platform_bus_type)
platform_device_unregister(to_platform_device(dev)); platform_device_unregister(to_platform_device(dev));
...@@ -528,19 +523,15 @@ static int of_platform_device_destroy(struct device *dev, void *data) ...@@ -528,19 +523,15 @@ static int of_platform_device_destroy(struct device *dev, void *data)
else if (dev->bus == &amba_bustype) else if (dev->bus == &amba_bustype)
amba_device_unregister(to_amba_device(dev)); amba_device_unregister(to_amba_device(dev));
#endif #endif
else {
*children_left = true;
return 0;
}
of_node_clear_flag(dev->of_node, OF_POPULATED); of_node_clear_flag(dev->of_node, OF_POPULATED);
of_node_clear_flag(dev->of_node, OF_POPULATED_BUS);
return 0; return 0;
} }
/** /**
* of_platform_depopulate() - Remove devices populated from device tree * of_platform_depopulate() - Remove devices populated from device tree
* @parent: device which childred will be removed * @parent: device which children will be removed
* *
* Complementary to of_platform_populate(), this function removes children * Complementary to of_platform_populate(), this function removes children
* of the given device (and, recurrently, their children) that have been * of the given device (and, recurrently, their children) that have been
...@@ -550,14 +541,9 @@ static int of_platform_device_destroy(struct device *dev, void *data) ...@@ -550,14 +541,9 @@ static int of_platform_device_destroy(struct device *dev, void *data)
* Returns 0 when all children devices have been removed or * Returns 0 when all children devices have been removed or
* -EBUSY when some children remained. * -EBUSY when some children remained.
*/ */
int of_platform_depopulate(struct device *parent) void of_platform_depopulate(struct device *parent)
{ {
bool children_left = false; device_for_each_child(parent, NULL, of_platform_device_destroy);
device_for_each_child(parent, &children_left,
of_platform_device_destroy);
return children_left ? -EBUSY : 0;
} }
EXPORT_SYMBOL_GPL(of_platform_depopulate); EXPORT_SYMBOL_GPL(of_platform_depopulate);
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/device.h> #include <linux/device.h>
#include "of_private.h"
static struct selftest_results { static struct selftest_results {
int passed; int passed;
int failed; int failed;
...@@ -271,6 +273,81 @@ static void __init of_selftest_property_match_string(void) ...@@ -271,6 +273,81 @@ static void __init of_selftest_property_match_string(void)
selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc); selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
} }
#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
(p1)->value && (p2)->value && \
!memcmp((p1)->value, (p2)->value, (p1)->length) && \
!strcmp((p1)->name, (p2)->name))
static void __init of_selftest_property_copy(void)
{
#ifdef CONFIG_OF_DYNAMIC
struct property p1 = { .name = "p1", .length = 0, .value = "" };
struct property p2 = { .name = "p2", .length = 5, .value = "abcd" };
struct property *new;
new = __of_prop_dup(&p1, GFP_KERNEL);
selftest(new && propcmp(&p1, new), "empty property didn't copy correctly\n");
kfree(new->value);
kfree(new->name);
kfree(new);
new = __of_prop_dup(&p2, GFP_KERNEL);
selftest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n");
kfree(new->value);
kfree(new->name);
kfree(new);
#endif
}
static void __init of_selftest_changeset(void)
{
#ifdef CONFIG_OF_DYNAMIC
struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" };
struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" };
struct property *ppremove;
struct device_node *n1, *n2, *n21, *nremove, *parent;
struct of_changeset chgset;
of_changeset_init(&chgset);
n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL);
selftest(n1, "testcase setup failure\n");
n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL);
selftest(n2, "testcase setup failure\n");
n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL);
selftest(n21, "testcase setup failure %p\n", n21);
nremove = of_find_node_by_path("/testcase-data/changeset/node-remove");
selftest(nremove, "testcase setup failure\n");
ppadd = __of_prop_dup(&padd, GFP_KERNEL);
selftest(ppadd, "testcase setup failure\n");
ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL);
selftest(ppupdate, "testcase setup failure\n");
parent = nremove->parent;
n1->parent = parent;
n2->parent = parent;
n21->parent = n2;
n2->child = n21;
ppremove = of_find_property(parent, "prop-remove", NULL);
selftest(ppremove, "failed to find removal prop");
of_changeset_init(&chgset);
selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n");
selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n");
selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n");
selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n");
selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n");
selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n");
selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n");
mutex_lock(&of_mutex);
selftest(!of_changeset_apply(&chgset), "apply failed\n");
mutex_unlock(&of_mutex);
mutex_lock(&of_mutex);
selftest(!of_changeset_revert(&chgset), "revert failed\n");
mutex_unlock(&of_mutex);
of_changeset_destroy(&chgset);
#endif
}
static void __init of_selftest_parse_interrupts(void) static void __init of_selftest_parse_interrupts(void)
{ {
struct device_node *np; struct device_node *np;
...@@ -685,6 +762,8 @@ static int __init of_selftest(void) ...@@ -685,6 +762,8 @@ static int __init of_selftest(void)
of_selftest_dynamic(); of_selftest_dynamic();
of_selftest_parse_phandle_with_args(); of_selftest_parse_phandle_with_args();
of_selftest_property_match_string(); of_selftest_property_match_string();
of_selftest_property_copy();
of_selftest_changeset();
of_selftest_parse_interrupts(); of_selftest_parse_interrupts();
of_selftest_parse_interrupts_extended(); of_selftest_parse_interrupts_extended();
of_selftest_match_node(); of_selftest_match_node();
......
/dts-v1/; /dts-v1/;
/ {
testcase-data {
changeset {
prop-update = "hello";
prop-remove = "world";
node-remove {
};
};
};
};
#include "tests-phandle.dtsi" #include "tests-phandle.dtsi"
#include "tests-interrupts.dtsi" #include "tests-interrupts.dtsi"
#include "tests-match.dtsi" #include "tests-match.dtsi"
......
...@@ -74,8 +74,6 @@ struct of_phandle_args { ...@@ -74,8 +74,6 @@ struct of_phandle_args {
uint32_t args[MAX_PHANDLE_ARGS]; uint32_t args[MAX_PHANDLE_ARGS];
}; };
extern int of_node_add(struct device_node *node);
/* initialize a node */ /* initialize a node */
extern struct kobj_type of_node_ktype; extern struct kobj_type of_node_ktype;
static inline void of_node_init(struct device_node *node) static inline void of_node_init(struct device_node *node)
...@@ -205,6 +203,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) ...@@ -205,6 +203,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size)
#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ #define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */
#define OF_DETACHED 2 /* node has been detached from the device tree */ #define OF_DETACHED 2 /* node has been detached from the device tree */
#define OF_POPULATED 3 /* device already created for the node */ #define OF_POPULATED 3 /* device already created for the node */
#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */
#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags)
#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags)
...@@ -323,6 +322,7 @@ extern int of_update_property(struct device_node *np, struct property *newprop); ...@@ -323,6 +322,7 @@ extern int of_update_property(struct device_node *np, struct property *newprop);
struct of_prop_reconfig { struct of_prop_reconfig {
struct device_node *dn; struct device_node *dn;
struct property *prop; struct property *prop;
struct property *old_prop;
}; };
extern int of_reconfig_notifier_register(struct notifier_block *); extern int of_reconfig_notifier_register(struct notifier_block *);
...@@ -787,4 +787,80 @@ typedef void (*of_init_fn_1)(struct device_node *); ...@@ -787,4 +787,80 @@ typedef void (*of_init_fn_1)(struct device_node *);
#define OF_DECLARE_2(table, name, compat, fn) \ #define OF_DECLARE_2(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_2) _OF_DECLARE(table, name, compat, fn, of_init_fn_2)
/**
* struct of_changeset_entry - Holds a changeset entry
*
* @node: list_head for the log list
* @action: notifier action
* @np: pointer to the device node affected
* @prop: pointer to the property affected
* @old_prop: hold a pointer to the original property
*
* Every modification of the device tree during a changeset
* is held in a list of of_changeset_entry structures.
* That way we can recover from a partial application, or we can
* revert the changeset
*/
struct of_changeset_entry {
struct list_head node;
unsigned long action;
struct device_node *np;
struct property *prop;
struct property *old_prop;
};
/**
* struct of_changeset - changeset tracker structure
*
* @entries: list_head for the changeset entries
*
* changesets are a convenient way to apply bulk changes to the
* live tree. In case of an error, changes are rolled-back.
* changesets live on after initial application, and if not
* destroyed after use, they can be reverted in one single call.
*/
struct of_changeset {
struct list_head entries;
};
#ifdef CONFIG_OF_DYNAMIC
extern void of_changeset_init(struct of_changeset *ocs);
extern void of_changeset_destroy(struct of_changeset *ocs);
extern int of_changeset_apply(struct of_changeset *ocs);
extern int of_changeset_revert(struct of_changeset *ocs);
extern int of_changeset_action(struct of_changeset *ocs,
unsigned long action, struct device_node *np,
struct property *prop);
static inline int of_changeset_attach_node(struct of_changeset *ocs,
struct device_node *np)
{
return of_changeset_action(ocs, OF_RECONFIG_ATTACH_NODE, np, NULL);
}
static inline int of_changeset_detach_node(struct of_changeset *ocs,
struct device_node *np)
{
return of_changeset_action(ocs, OF_RECONFIG_DETACH_NODE, np, NULL);
}
static inline int of_changeset_add_property(struct of_changeset *ocs,
struct device_node *np, struct property *prop)
{
return of_changeset_action(ocs, OF_RECONFIG_ADD_PROPERTY, np, prop);
}
static inline int of_changeset_remove_property(struct of_changeset *ocs,
struct device_node *np, struct property *prop)
{
return of_changeset_action(ocs, OF_RECONFIG_REMOVE_PROPERTY, np, prop);
}
static inline int of_changeset_update_property(struct of_changeset *ocs,
struct device_node *np, struct property *prop)
{
return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop);
}
#endif
#endif /* _LINUX_OF_H */ #endif /* _LINUX_OF_H */
...@@ -72,7 +72,7 @@ extern int of_platform_populate(struct device_node *root, ...@@ -72,7 +72,7 @@ extern int of_platform_populate(struct device_node *root,
const struct of_device_id *matches, const struct of_device_id *matches,
const struct of_dev_auxdata *lookup, const struct of_dev_auxdata *lookup,
struct device *parent); struct device *parent);
extern int of_platform_depopulate(struct device *parent); extern void of_platform_depopulate(struct device *parent);
#else #else
static inline int of_platform_populate(struct device_node *root, static inline int of_platform_populate(struct device_node *root,
const struct of_device_id *matches, const struct of_device_id *matches,
...@@ -81,10 +81,7 @@ static inline int of_platform_populate(struct device_node *root, ...@@ -81,10 +81,7 @@ static inline int of_platform_populate(struct device_node *root,
{ {
return -ENODEV; return -ENODEV;
} }
static inline int of_platform_depopulate(struct device *parent) static inline void of_platform_depopulate(struct device *parent) { }
{
return -ENODEV;
}
#endif #endif
#endif /* _LINUX_OF_PLATFORM_H */ #endif /* _LINUX_OF_PLATFORM_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment