powerpc+of: Add of node/property notification chain for adds and removes
This patch moves the notification chain for updates to the device tree from the powerpc/pseries code to the base OF code. This makes this functionality available to all architectures. Additionally the notification chain is updated to allow notifications for property add/remove/update. To make this work a pointer to a new struct (of_prop_reconfig) is passed to the routines in the notification chain. The of_prop_reconfig property contains a pointer to the node containing the property and a pointer to the property itself. In the case of property updates, the property pointer refers to the new property. Signed-off-by: Nathan Fontenot <nfont@linux.vnet.ibm.com> Acked-by: Rob Herring <rob.herring@calxeda.com> Acked-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
Родитель
f594972083
Коммит
1cf3d8b3d2
|
@ -2,43 +2,11 @@
|
|||
#define _PPC64_PSERIES_RECONFIG_H
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
||||
/*
|
||||
* Use this API if your code needs to know about OF device nodes being
|
||||
* added or removed on pSeries systems.
|
||||
*/
|
||||
|
||||
#define PSERIES_RECONFIG_ADD 0x0001
|
||||
#define PSERIES_RECONFIG_REMOVE 0x0002
|
||||
#define PSERIES_DRCONF_MEM_ADD 0x0003
|
||||
#define PSERIES_DRCONF_MEM_REMOVE 0x0004
|
||||
#define PSERIES_UPDATE_PROPERTY 0x0005
|
||||
|
||||
/**
|
||||
* pSeries_reconfig_notify - Notifier value structure for OFDT property updates
|
||||
*
|
||||
* @node: Device tree node which owns the property being updated
|
||||
* @property: Updated property
|
||||
*/
|
||||
struct pSeries_reconfig_prop_update {
|
||||
struct device_node *node;
|
||||
struct property *property;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PPC_PSERIES
|
||||
extern int pSeries_reconfig_notifier_register(struct notifier_block *);
|
||||
extern void pSeries_reconfig_notifier_unregister(struct notifier_block *);
|
||||
extern int pSeries_reconfig_notify(unsigned long action, void *p);
|
||||
/* Not the best place to put this, will be fixed when we move some
|
||||
* of the rtas suspend-me stuff to pseries */
|
||||
extern void pSeries_coalesce_init(void);
|
||||
#else /* !CONFIG_PPC_PSERIES */
|
||||
static inline int pSeries_reconfig_notifier_register(struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void pSeries_reconfig_notifier_unregister(struct notifier_block *nb) { }
|
||||
static inline void pSeries_coalesce_init(void) { }
|
||||
#endif /* CONFIG_PPC_PSERIES */
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
|
@ -49,7 +50,6 @@
|
|||
#include <asm/btext.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/kexec.h>
|
||||
#include <asm/opal.h>
|
||||
|
@ -802,7 +802,7 @@ static int prom_reconfig_notifier(struct notifier_block *nb,
|
|||
int err;
|
||||
|
||||
switch (action) {
|
||||
case PSERIES_RECONFIG_ADD:
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
err = of_finish_dynamic_node(node);
|
||||
if (err < 0)
|
||||
printk(KERN_ERR "finish_node returned %d\n", err);
|
||||
|
@ -821,7 +821,7 @@ static struct notifier_block prom_reconfig_nb = {
|
|||
|
||||
static int __init prom_reconfig_setup(void)
|
||||
{
|
||||
return pSeries_reconfig_notifier_register(&prom_reconfig_nb);
|
||||
return of_reconfig_notifier_register(&prom_reconfig_nb);
|
||||
}
|
||||
__initcall(prom_reconfig_setup);
|
||||
#endif
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include "offline_states.h"
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
|
||||
struct cc_workarea {
|
||||
u32 drc_index;
|
||||
|
@ -262,24 +262,26 @@ int dlpar_attach_node(struct device_node *dn)
|
|||
if (!dn->parent)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = pSeries_reconfig_notify(PSERIES_RECONFIG_ADD, dn);
|
||||
rc = of_attach_node(dn);
|
||||
if (rc) {
|
||||
printk(KERN_ERR "Failed to add device node %s\n",
|
||||
dn->full_name);
|
||||
return rc;
|
||||
}
|
||||
|
||||
of_attach_node(dn);
|
||||
of_node_put(dn->parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dlpar_detach_node(struct device_node *dn)
|
||||
{
|
||||
pSeries_reconfig_notify(PSERIES_RECONFIG_REMOVE, dn);
|
||||
of_detach_node(dn);
|
||||
of_node_put(dn); /* Must decrement the refcount */
|
||||
int rc;
|
||||
|
||||
rc = of_detach_node(dn);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
of_node_put(dn); /* Must decrement the refcount */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,12 +23,12 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/sched.h> /* for idle_task_exit */
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/vdso_datapage.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/xics.h>
|
||||
#include "plpar_wrappers.h"
|
||||
#include "offline_states.h"
|
||||
|
@ -333,10 +333,10 @@ static int pseries_smp_notifier(struct notifier_block *nb,
|
|||
int err = 0;
|
||||
|
||||
switch (action) {
|
||||
case PSERIES_RECONFIG_ADD:
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
err = pseries_add_processor(node);
|
||||
break;
|
||||
case PSERIES_RECONFIG_REMOVE:
|
||||
case OF_RECONFIG_DETACH_NODE:
|
||||
pseries_remove_processor(node);
|
||||
break;
|
||||
}
|
||||
|
@ -399,7 +399,7 @@ static int __init pseries_cpu_hotplug_init(void)
|
|||
|
||||
/* Processors can be added/removed only on LPAR */
|
||||
if (firmware_has_feature(FW_FEATURE_LPAR)) {
|
||||
pSeries_reconfig_notifier_register(&pseries_smp_nb);
|
||||
of_reconfig_notifier_register(&pseries_smp_nb);
|
||||
cpu_maps_update_begin();
|
||||
if (cede_offline_enabled && parse_cede_parameters() == 0) {
|
||||
default_offline_state = CPU_STATE_INACTIVE;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/sparsemem.h>
|
||||
|
||||
static unsigned long get_memblock_size(void)
|
||||
|
@ -187,42 +186,69 @@ static int pseries_add_memory(struct device_node *np)
|
|||
return (ret < 0) ? -EINVAL : 0;
|
||||
}
|
||||
|
||||
static int pseries_drconf_memory(unsigned long *base, unsigned int action)
|
||||
static int pseries_update_drconf_memory(struct of_prop_reconfig *pr)
|
||||
{
|
||||
struct of_drconf_cell *new_drmem, *old_drmem;
|
||||
unsigned long memblock_size;
|
||||
int rc;
|
||||
u32 entries;
|
||||
u32 *p;
|
||||
int i, rc = -EINVAL;
|
||||
|
||||
memblock_size = get_memblock_size();
|
||||
if (!memblock_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (action == PSERIES_DRCONF_MEM_ADD) {
|
||||
rc = memblock_add(*base, memblock_size);
|
||||
rc = (rc < 0) ? -EINVAL : 0;
|
||||
} else if (action == PSERIES_DRCONF_MEM_REMOVE) {
|
||||
rc = pseries_remove_memblock(*base, memblock_size);
|
||||
} else {
|
||||
rc = -EINVAL;
|
||||
p = (u32 *)of_get_property(pr->dn, "ibm,dynamic-memory", NULL);
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
/* The first int of the property is the number of lmb's described
|
||||
* by the property. This is followed by an array of of_drconf_cell
|
||||
* entries. Get the niumber of entries and skip to the array of
|
||||
* of_drconf_cell's.
|
||||
*/
|
||||
entries = *p++;
|
||||
old_drmem = (struct of_drconf_cell *)p;
|
||||
|
||||
p = (u32 *)pr->prop->value;
|
||||
p++;
|
||||
new_drmem = (struct of_drconf_cell *)p;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
if ((old_drmem[i].flags & DRCONF_MEM_ASSIGNED) &&
|
||||
(!(new_drmem[i].flags & DRCONF_MEM_ASSIGNED))) {
|
||||
rc = pseries_remove_memblock(old_drmem[i].base_addr,
|
||||
memblock_size);
|
||||
break;
|
||||
} else if ((!(old_drmem[i].flags & DRCONF_MEM_ASSIGNED)) &&
|
||||
(new_drmem[i].flags & DRCONF_MEM_ASSIGNED)) {
|
||||
rc = memblock_add(old_drmem[i].base_addr,
|
||||
memblock_size);
|
||||
rc = (rc < 0) ? -EINVAL : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pseries_memory_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *node)
|
||||
unsigned long action, void *node)
|
||||
{
|
||||
struct of_prop_reconfig *pr;
|
||||
int err = 0;
|
||||
|
||||
switch (action) {
|
||||
case PSERIES_RECONFIG_ADD:
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
err = pseries_add_memory(node);
|
||||
break;
|
||||
case PSERIES_RECONFIG_REMOVE:
|
||||
case OF_RECONFIG_DETACH_NODE:
|
||||
err = pseries_remove_memory(node);
|
||||
break;
|
||||
case PSERIES_DRCONF_MEM_ADD:
|
||||
case PSERIES_DRCONF_MEM_REMOVE:
|
||||
err = pseries_drconf_memory(node, action);
|
||||
case OF_RECONFIG_UPDATE_PROPERTY:
|
||||
pr = (struct of_prop_reconfig *)node;
|
||||
if (!strcmp(pr->prop->name, "ibm,dynamic-memory"))
|
||||
err = pseries_update_drconf_memory(pr);
|
||||
break;
|
||||
}
|
||||
return notifier_from_errno(err);
|
||||
|
@ -235,7 +261,7 @@ static struct notifier_block pseries_mem_nb = {
|
|||
static int __init pseries_memory_hotplug_init(void)
|
||||
{
|
||||
if (firmware_has_feature(FW_FEATURE_LPAR))
|
||||
pSeries_reconfig_notifier_register(&pseries_mem_nb);
|
||||
of_reconfig_notifier_register(&pseries_mem_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -36,13 +36,13 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/prom.h>
|
||||
#include <asm/rtas.h>
|
||||
#include <asm/iommu.h>
|
||||
#include <asm/pci-bridge.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/tce.h>
|
||||
#include <asm/ppc-pci.h>
|
||||
|
@ -1294,7 +1294,7 @@ static int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long acti
|
|||
struct direct_window *window;
|
||||
|
||||
switch (action) {
|
||||
case PSERIES_RECONFIG_REMOVE:
|
||||
case OF_RECONFIG_DETACH_NODE:
|
||||
if (pci && pci->iommu_table)
|
||||
iommu_free_table(pci->iommu_table, np->full_name);
|
||||
|
||||
|
@ -1357,7 +1357,7 @@ void iommu_init_early_pSeries(void)
|
|||
}
|
||||
|
||||
|
||||
pSeries_reconfig_notifier_register(&iommu_reconfig_nb);
|
||||
of_reconfig_notifier_register(&iommu_reconfig_nb);
|
||||
register_memory_notifier(&iommu_mem_nb);
|
||||
|
||||
set_pci_dma_ops(&dma_iommu_ops);
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
#include <linux/notifier.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/prom.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/mmu.h>
|
||||
|
||||
/**
|
||||
|
@ -55,28 +55,6 @@ static struct device_node *derive_parent(const char *path)
|
|||
return parent;
|
||||
}
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
|
||||
|
||||
int pSeries_reconfig_notifier_register(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_register(&pSeries_reconfig_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_register);
|
||||
|
||||
void pSeries_reconfig_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
blocking_notifier_chain_unregister(&pSeries_reconfig_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pSeries_reconfig_notifier_unregister);
|
||||
|
||||
int pSeries_reconfig_notify(unsigned long action, void *p)
|
||||
{
|
||||
int err = blocking_notifier_call_chain(&pSeries_reconfig_chain,
|
||||
action, p);
|
||||
|
||||
return notifier_to_errno(err);
|
||||
}
|
||||
|
||||
static int pSeries_reconfig_add_node(const char *path, struct property *proplist)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -100,13 +78,12 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
err = pSeries_reconfig_notify(PSERIES_RECONFIG_ADD, np);
|
||||
err = of_attach_node(np);
|
||||
if (err) {
|
||||
printk(KERN_ERR "Failed to add device node %s\n", path);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
of_attach_node(np);
|
||||
of_node_put(np->parent);
|
||||
|
||||
return 0;
|
||||
|
@ -134,9 +111,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np)
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
pSeries_reconfig_notify(PSERIES_RECONFIG_REMOVE, np);
|
||||
of_detach_node(np);
|
||||
|
||||
of_node_put(parent);
|
||||
of_node_put(np); /* Must decrement the refcount */
|
||||
return 0;
|
||||
|
@ -381,10 +356,9 @@ static int do_remove_property(char *buf, size_t bufsize)
|
|||
static int do_update_property(char *buf, size_t bufsize)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct pSeries_reconfig_prop_update upd_value;
|
||||
unsigned char *value;
|
||||
char *name, *end, *next_prop;
|
||||
int rc, length;
|
||||
int length;
|
||||
struct property *newprop;
|
||||
buf = parse_node(buf, bufsize, &np);
|
||||
end = buf + bufsize;
|
||||
|
@ -406,41 +380,7 @@ static int do_update_property(char *buf, size_t bufsize)
|
|||
if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size"))
|
||||
slb_set_size(*(int *)value);
|
||||
|
||||
upd_value.node = np;
|
||||
upd_value.property = newprop;
|
||||
pSeries_reconfig_notify(PSERIES_UPDATE_PROPERTY, &upd_value);
|
||||
|
||||
rc = prom_update_property(np, newprop);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* For memory under the ibm,dynamic-reconfiguration-memory node
|
||||
* of the device tree, adding and removing memory is just an update
|
||||
* to the ibm,dynamic-memory property instead of adding/removing a
|
||||
* memory node in the device tree. For these cases we still need to
|
||||
* involve the notifier chain.
|
||||
*/
|
||||
if (!strcmp(name, "ibm,dynamic-memory")) {
|
||||
int action;
|
||||
|
||||
next_prop = parse_next_property(next_prop, end, &name,
|
||||
&length, &value);
|
||||
if (!next_prop)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(name, "add"))
|
||||
action = PSERIES_DRCONF_MEM_ADD;
|
||||
else
|
||||
action = PSERIES_DRCONF_MEM_REMOVE;
|
||||
|
||||
rc = pSeries_reconfig_notify(action, value);
|
||||
if (rc) {
|
||||
prom_update_property(np, newprop);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return prom_update_property(np, newprop);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <linux/seq_file.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/mmu.h>
|
||||
#include <asm/processor.h>
|
||||
|
@ -63,7 +64,6 @@
|
|||
#include <asm/smp.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/eeh.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
|
||||
#include "plpar_wrappers.h"
|
||||
#include "pseries.h"
|
||||
|
@ -258,7 +258,7 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act
|
|||
int err = NOTIFY_OK;
|
||||
|
||||
switch (action) {
|
||||
case PSERIES_RECONFIG_ADD:
|
||||
case OF_RECONFIG_ATTACH_NODE:
|
||||
pci = np->parent->data;
|
||||
if (pci) {
|
||||
update_dn_pci_info(np, pci->phb);
|
||||
|
@ -389,7 +389,7 @@ static void __init pSeries_setup_arch(void)
|
|||
/* Find and initialize PCI host bridges */
|
||||
init_pci_config_tokens();
|
||||
find_and_init_phbs();
|
||||
pSeries_reconfig_notifier_register(&pci_dn_reconfig_nb);
|
||||
of_reconfig_notifier_register(&pci_dn_reconfig_nb);
|
||||
|
||||
pSeries_nvram_init();
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/vio.h>
|
||||
|
||||
#include "nx_csbcpb.h" /* struct nx_csbcpb */
|
||||
|
@ -1014,26 +1013,23 @@ error_out:
|
|||
* NOTIFY_BAD encoded with error number on failure, use
|
||||
* notifier_to_errno() to decode this value
|
||||
*/
|
||||
static int nx842_OF_notifier(struct notifier_block *np,
|
||||
unsigned long action,
|
||||
void *update)
|
||||
static int nx842_OF_notifier(struct notifier_block *np, unsigned long action,
|
||||
void *update)
|
||||
{
|
||||
struct pSeries_reconfig_prop_update *upd;
|
||||
struct of_prop_reconfig *upd = update;
|
||||
struct nx842_devdata *local_devdata;
|
||||
struct device_node *node = NULL;
|
||||
|
||||
upd = (struct pSeries_reconfig_prop_update *)update;
|
||||
|
||||
rcu_read_lock();
|
||||
local_devdata = rcu_dereference(devdata);
|
||||
if (local_devdata)
|
||||
node = local_devdata->dev->of_node;
|
||||
|
||||
if (local_devdata &&
|
||||
action == PSERIES_UPDATE_PROPERTY &&
|
||||
!strcmp(upd->node->name, node->name)) {
|
||||
action == OF_RECONFIG_UPDATE_PROPERTY &&
|
||||
!strcmp(upd->dn->name, node->name)) {
|
||||
rcu_read_unlock();
|
||||
nx842_OF_upd(upd->property);
|
||||
nx842_OF_upd(upd->prop);
|
||||
} else
|
||||
rcu_read_unlock();
|
||||
|
||||
|
@ -1182,7 +1178,7 @@ static int __init nx842_probe(struct vio_dev *viodev,
|
|||
synchronize_rcu();
|
||||
kfree(old_devdata);
|
||||
|
||||
pSeries_reconfig_notifier_register(&nx842_of_nb);
|
||||
of_reconfig_notifier_register(&nx842_of_nb);
|
||||
|
||||
ret = nx842_OF_upd(NULL);
|
||||
if (ret && ret != -ENODEV) {
|
||||
|
@ -1228,7 +1224,7 @@ static int __exit nx842_remove(struct vio_dev *viodev)
|
|||
spin_lock_irqsave(&devdata_mutex, flags);
|
||||
old_devdata = rcu_dereference_check(devdata,
|
||||
lockdep_is_held(&devdata_mutex));
|
||||
pSeries_reconfig_notifier_unregister(&nx842_of_nb);
|
||||
of_reconfig_notifier_unregister(&nx842_of_nb);
|
||||
rcu_assign_pointer(devdata, NULL);
|
||||
spin_unlock_irqrestore(&devdata_mutex, flags);
|
||||
synchronize_rcu();
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include <linux/scatterlist.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/pSeries_reconfig.h>
|
||||
#include <asm/hvcall.h>
|
||||
#include <asm/vio.h>
|
||||
|
||||
|
|
|
@ -1028,6 +1028,24 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name,
|
|||
}
|
||||
EXPORT_SYMBOL(of_parse_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;
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
* prom_add_property - Add a property to a node
|
||||
*/
|
||||
|
@ -1035,6 +1053,11 @@ int prom_add_property(struct device_node *np, struct property *prop)
|
|||
{
|
||||
struct property **next;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
prop->next = NULL;
|
||||
write_lock_irqsave(&devtree_lock, flags);
|
||||
|
@ -1072,6 +1095,11 @@ int prom_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;
|
||||
|
||||
write_lock_irqsave(&devtree_lock, flags);
|
||||
next = &np->properties;
|
||||
|
@ -1114,7 +1142,11 @@ int prom_update_property(struct device_node *np,
|
|||
{
|
||||
struct property **next, *oldprop;
|
||||
unsigned long flags;
|
||||
int found = 0;
|
||||
int rc, found = 0;
|
||||
|
||||
rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!newprop->name)
|
||||
return -EINVAL;
|
||||
|
@ -1160,6 +1192,26 @@ int prom_update_property(struct device_node *np,
|
|||
* 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);
|
||||
}
|
||||
|
||||
int of_reconfig_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
return blocking_notifier_chain_unregister(&of_reconfig_chain, nb);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_DEVICETREE
|
||||
static void of_add_proc_dt_entry(struct device_node *dn)
|
||||
{
|
||||
|
@ -1179,9 +1231,14 @@ static void of_add_proc_dt_entry(struct device_node *dn)
|
|||
/**
|
||||
* of_attach_node - Plug a device node into the tree and global list.
|
||||
*/
|
||||
void of_attach_node(struct device_node *np)
|
||||
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;
|
||||
|
||||
write_lock_irqsave(&devtree_lock, flags);
|
||||
np->sibling = np->parent->child;
|
||||
|
@ -1191,6 +1248,7 @@ void of_attach_node(struct device_node *np)
|
|||
write_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
of_add_proc_dt_entry(np);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_DEVICETREE
|
||||
|
@ -1220,23 +1278,28 @@ static void of_remove_proc_dt_entry(struct device_node *dn)
|
|||
* The caller must hold a reference to the node. The memory associated with
|
||||
* the node is not freed until its refcount goes to zero.
|
||||
*/
|
||||
void of_detach_node(struct device_node *np)
|
||||
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);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
write_lock_irqsave(&devtree_lock, flags);
|
||||
|
||||
if (of_node_check_flag(np, OF_DETACHED)) {
|
||||
/* someone already detached it */
|
||||
write_unlock_irqrestore(&devtree_lock, flags);
|
||||
return;
|
||||
return rc;
|
||||
}
|
||||
|
||||
parent = np->parent;
|
||||
if (!parent) {
|
||||
write_unlock_irqrestore(&devtree_lock, flags);
|
||||
return;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (allnodes == np)
|
||||
|
@ -1265,6 +1328,7 @@ void of_detach_node(struct device_node *np)
|
|||
write_unlock_irqrestore(&devtree_lock, flags);
|
||||
|
||||
of_remove_proc_dt_entry(np);
|
||||
return rc;
|
||||
}
|
||||
#endif /* defined(CONFIG_OF_DYNAMIC) */
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/topology.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/errno.h>
|
||||
|
@ -272,11 +273,24 @@ extern int prom_remove_property(struct device_node *np, struct property *prop);
|
|||
extern int prom_update_property(struct device_node *np,
|
||||
struct property *newprop);
|
||||
|
||||
#if defined(CONFIG_OF_DYNAMIC)
|
||||
/* For updating the device tree at runtime */
|
||||
extern void of_attach_node(struct device_node *);
|
||||
extern void of_detach_node(struct device_node *);
|
||||
#endif
|
||||
#define OF_RECONFIG_ATTACH_NODE 0x0001
|
||||
#define OF_RECONFIG_DETACH_NODE 0x0002
|
||||
#define OF_RECONFIG_ADD_PROPERTY 0x0003
|
||||
#define OF_RECONFIG_REMOVE_PROPERTY 0x0004
|
||||
#define OF_RECONFIG_UPDATE_PROPERTY 0x0005
|
||||
|
||||
struct of_prop_reconfig {
|
||||
struct device_node *dn;
|
||||
struct property *prop;
|
||||
};
|
||||
|
||||
extern int of_reconfig_notifier_register(struct notifier_block *);
|
||||
extern int of_reconfig_notifier_unregister(struct notifier_block *);
|
||||
extern int of_reconfig_notify(unsigned long, void *);
|
||||
|
||||
extern int of_attach_node(struct device_node *);
|
||||
extern int of_detach_node(struct device_node *);
|
||||
|
||||
#define of_match_ptr(_ptr) (_ptr)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче