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:
		
					parent
					
						
							
								f594972083
							
						
					
				
			
			
				commit
				
					
						1cf3d8b3d2
					
				
			
		
					 12 changed files with 163 additions and 154 deletions
				
			
		|  | @ -2,43 +2,11 @@ | ||||||
| #define _PPC64_PSERIES_RECONFIG_H | #define _PPC64_PSERIES_RECONFIG_H | ||||||
| #ifdef __KERNEL__ | #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 | #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
 | /* Not the best place to put this, will be fixed when we move some
 | ||||||
|  * of the rtas suspend-me stuff to pseries */ |  * of the rtas suspend-me stuff to pseries */ | ||||||
| extern void pSeries_coalesce_init(void); | extern void pSeries_coalesce_init(void); | ||||||
| #else /* !CONFIG_PPC_PSERIES */ | #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) { } | static inline void pSeries_coalesce_init(void) { } | ||||||
| #endif /* CONFIG_PPC_PSERIES */ | #endif /* CONFIG_PPC_PSERIES */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ | ||||||
| #include <linux/debugfs.h> | #include <linux/debugfs.h> | ||||||
| #include <linux/irq.h> | #include <linux/irq.h> | ||||||
| #include <linux/memblock.h> | #include <linux/memblock.h> | ||||||
|  | #include <linux/of.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/prom.h> | #include <asm/prom.h> | ||||||
| #include <asm/rtas.h> | #include <asm/rtas.h> | ||||||
|  | @ -49,7 +50,6 @@ | ||||||
| #include <asm/btext.h> | #include <asm/btext.h> | ||||||
| #include <asm/sections.h> | #include <asm/sections.h> | ||||||
| #include <asm/machdep.h> | #include <asm/machdep.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| #include <asm/pci-bridge.h> | #include <asm/pci-bridge.h> | ||||||
| #include <asm/kexec.h> | #include <asm/kexec.h> | ||||||
| #include <asm/opal.h> | #include <asm/opal.h> | ||||||
|  | @ -802,7 +802,7 @@ static int prom_reconfig_notifier(struct notifier_block *nb, | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	switch (action) { | 	switch (action) { | ||||||
| 	case PSERIES_RECONFIG_ADD: | 	case OF_RECONFIG_ATTACH_NODE: | ||||||
| 		err = of_finish_dynamic_node(node); | 		err = of_finish_dynamic_node(node); | ||||||
| 		if (err < 0) | 		if (err < 0) | ||||||
| 			printk(KERN_ERR "finish_node returned %d\n", err); | 			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) | 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); | __initcall(prom_reconfig_setup); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -16,13 +16,13 @@ | ||||||
| #include <linux/spinlock.h> | #include <linux/spinlock.h> | ||||||
| #include <linux/cpu.h> | #include <linux/cpu.h> | ||||||
| #include <linux/slab.h> | #include <linux/slab.h> | ||||||
|  | #include <linux/of.h> | ||||||
| #include "offline_states.h" | #include "offline_states.h" | ||||||
| 
 | 
 | ||||||
| #include <asm/prom.h> | #include <asm/prom.h> | ||||||
| #include <asm/machdep.h> | #include <asm/machdep.h> | ||||||
| #include <asm/uaccess.h> | #include <asm/uaccess.h> | ||||||
| #include <asm/rtas.h> | #include <asm/rtas.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| 
 | 
 | ||||||
| struct cc_workarea { | struct cc_workarea { | ||||||
| 	u32	drc_index; | 	u32	drc_index; | ||||||
|  | @ -262,24 +262,26 @@ int dlpar_attach_node(struct device_node *dn) | ||||||
| 	if (!dn->parent) | 	if (!dn->parent) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	rc = pSeries_reconfig_notify(PSERIES_RECONFIG_ADD, dn); | 	rc = of_attach_node(dn); | ||||||
| 	if (rc) { | 	if (rc) { | ||||||
| 		printk(KERN_ERR "Failed to add device node %s\n", | 		printk(KERN_ERR "Failed to add device node %s\n", | ||||||
| 		       dn->full_name); | 		       dn->full_name); | ||||||
| 		return rc; | 		return rc; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	of_attach_node(dn); |  | ||||||
| 	of_node_put(dn->parent); | 	of_node_put(dn->parent); | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int dlpar_detach_node(struct device_node *dn) | int dlpar_detach_node(struct device_node *dn) | ||||||
| { | { | ||||||
| 	pSeries_reconfig_notify(PSERIES_RECONFIG_REMOVE, dn); | 	int rc; | ||||||
| 	of_detach_node(dn); |  | ||||||
| 	of_node_put(dn); /* Must decrement the refcount */ |  | ||||||
| 
 | 
 | ||||||
|  | 	rc = of_detach_node(dn); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
|  | 
 | ||||||
|  | 	of_node_put(dn); /* Must decrement the refcount */ | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,12 +23,12 @@ | ||||||
| #include <linux/delay.h> | #include <linux/delay.h> | ||||||
| #include <linux/sched.h>	/* for idle_task_exit */ | #include <linux/sched.h>	/* for idle_task_exit */ | ||||||
| #include <linux/cpu.h> | #include <linux/cpu.h> | ||||||
|  | #include <linux/of.h> | ||||||
| #include <asm/prom.h> | #include <asm/prom.h> | ||||||
| #include <asm/rtas.h> | #include <asm/rtas.h> | ||||||
| #include <asm/firmware.h> | #include <asm/firmware.h> | ||||||
| #include <asm/machdep.h> | #include <asm/machdep.h> | ||||||
| #include <asm/vdso_datapage.h> | #include <asm/vdso_datapage.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| #include <asm/xics.h> | #include <asm/xics.h> | ||||||
| #include "plpar_wrappers.h" | #include "plpar_wrappers.h" | ||||||
| #include "offline_states.h" | #include "offline_states.h" | ||||||
|  | @ -333,10 +333,10 @@ static int pseries_smp_notifier(struct notifier_block *nb, | ||||||
| 	int err = 0; | 	int err = 0; | ||||||
| 
 | 
 | ||||||
| 	switch (action) { | 	switch (action) { | ||||||
| 	case PSERIES_RECONFIG_ADD: | 	case OF_RECONFIG_ATTACH_NODE: | ||||||
| 		err = pseries_add_processor(node); | 		err = pseries_add_processor(node); | ||||||
| 		break; | 		break; | ||||||
| 	case PSERIES_RECONFIG_REMOVE: | 	case OF_RECONFIG_DETACH_NODE: | ||||||
| 		pseries_remove_processor(node); | 		pseries_remove_processor(node); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  | @ -399,7 +399,7 @@ static int __init pseries_cpu_hotplug_init(void) | ||||||
| 
 | 
 | ||||||
| 	/* Processors can be added/removed only on LPAR */ | 	/* Processors can be added/removed only on LPAR */ | ||||||
| 	if (firmware_has_feature(FW_FEATURE_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(); | 		cpu_maps_update_begin(); | ||||||
| 		if (cede_offline_enabled && parse_cede_parameters() == 0) { | 		if (cede_offline_enabled && parse_cede_parameters() == 0) { | ||||||
| 			default_offline_state = CPU_STATE_INACTIVE; | 			default_offline_state = CPU_STATE_INACTIVE; | ||||||
|  |  | ||||||
|  | @ -16,7 +16,6 @@ | ||||||
| 
 | 
 | ||||||
| #include <asm/firmware.h> | #include <asm/firmware.h> | ||||||
| #include <asm/machdep.h> | #include <asm/machdep.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| #include <asm/sparsemem.h> | #include <asm/sparsemem.h> | ||||||
| 
 | 
 | ||||||
| static unsigned long get_memblock_size(void) | static unsigned long get_memblock_size(void) | ||||||
|  | @ -187,22 +186,47 @@ static int pseries_add_memory(struct device_node *np) | ||||||
| 	return (ret < 0) ? -EINVAL : 0; | 	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; | 	unsigned long memblock_size; | ||||||
| 	int rc; | 	u32 entries; | ||||||
|  | 	u32 *p; | ||||||
|  | 	int i, rc = -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	memblock_size = get_memblock_size(); | 	memblock_size = get_memblock_size(); | ||||||
| 	if (!memblock_size) | 	if (!memblock_size) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	if (action == PSERIES_DRCONF_MEM_ADD) { | 	p = (u32 *)of_get_property(pr->dn, "ibm,dynamic-memory", NULL); | ||||||
| 		rc = memblock_add(*base, memblock_size); | 	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; | 			rc = (rc < 0) ? -EINVAL : 0; | ||||||
| 	} else if (action == PSERIES_DRCONF_MEM_REMOVE) { | 			break; | ||||||
| 		rc = pseries_remove_memblock(*base, memblock_size); | 		} | ||||||
| 	} else { |  | ||||||
| 		rc = -EINVAL; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return rc; | 	return rc; | ||||||
|  | @ -211,18 +235,20 @@ static int pseries_drconf_memory(unsigned long *base, unsigned int action) | ||||||
| static int pseries_memory_notifier(struct notifier_block *nb, | 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; | 	int err = 0; | ||||||
| 
 | 
 | ||||||
| 	switch (action) { | 	switch (action) { | ||||||
| 	case PSERIES_RECONFIG_ADD: | 	case OF_RECONFIG_ATTACH_NODE: | ||||||
| 		err = pseries_add_memory(node); | 		err = pseries_add_memory(node); | ||||||
| 		break; | 		break; | ||||||
| 	case PSERIES_RECONFIG_REMOVE: | 	case OF_RECONFIG_DETACH_NODE: | ||||||
| 		err = pseries_remove_memory(node); | 		err = pseries_remove_memory(node); | ||||||
| 		break; | 		break; | ||||||
| 	case PSERIES_DRCONF_MEM_ADD: | 	case OF_RECONFIG_UPDATE_PROPERTY: | ||||||
| 	case PSERIES_DRCONF_MEM_REMOVE: | 		pr = (struct of_prop_reconfig *)node; | ||||||
| 		err = pseries_drconf_memory(node, action); | 		if (!strcmp(pr->prop->name, "ibm,dynamic-memory")) | ||||||
|  | 			err = pseries_update_drconf_memory(pr); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	return notifier_from_errno(err); | 	return notifier_from_errno(err); | ||||||
|  | @ -235,7 +261,7 @@ static struct notifier_block pseries_mem_nb = { | ||||||
| static int __init pseries_memory_hotplug_init(void) | static int __init pseries_memory_hotplug_init(void) | ||||||
| { | { | ||||||
| 	if (firmware_has_feature(FW_FEATURE_LPAR)) | 	if (firmware_has_feature(FW_FEATURE_LPAR)) | ||||||
| 		pSeries_reconfig_notifier_register(&pseries_mem_nb); | 		of_reconfig_notifier_register(&pseries_mem_nb); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -36,13 +36,13 @@ | ||||||
| #include <linux/dma-mapping.h> | #include <linux/dma-mapping.h> | ||||||
| #include <linux/crash_dump.h> | #include <linux/crash_dump.h> | ||||||
| #include <linux/memory.h> | #include <linux/memory.h> | ||||||
|  | #include <linux/of.h> | ||||||
| #include <asm/io.h> | #include <asm/io.h> | ||||||
| #include <asm/prom.h> | #include <asm/prom.h> | ||||||
| #include <asm/rtas.h> | #include <asm/rtas.h> | ||||||
| #include <asm/iommu.h> | #include <asm/iommu.h> | ||||||
| #include <asm/pci-bridge.h> | #include <asm/pci-bridge.h> | ||||||
| #include <asm/machdep.h> | #include <asm/machdep.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| #include <asm/firmware.h> | #include <asm/firmware.h> | ||||||
| #include <asm/tce.h> | #include <asm/tce.h> | ||||||
| #include <asm/ppc-pci.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; | 	struct direct_window *window; | ||||||
| 
 | 
 | ||||||
| 	switch (action) { | 	switch (action) { | ||||||
| 	case PSERIES_RECONFIG_REMOVE: | 	case OF_RECONFIG_DETACH_NODE: | ||||||
| 		if (pci && pci->iommu_table) | 		if (pci && pci->iommu_table) | ||||||
| 			iommu_free_table(pci->iommu_table, np->full_name); | 			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); | 	register_memory_notifier(&iommu_mem_nb); | ||||||
| 
 | 
 | ||||||
| 	set_pci_dma_ops(&dma_iommu_ops); | 	set_pci_dma_ops(&dma_iommu_ops); | ||||||
|  |  | ||||||
|  | @ -16,11 +16,11 @@ | ||||||
| #include <linux/notifier.h> | #include <linux/notifier.h> | ||||||
| #include <linux/proc_fs.h> | #include <linux/proc_fs.h> | ||||||
| #include <linux/slab.h> | #include <linux/slab.h> | ||||||
|  | #include <linux/of.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/prom.h> | #include <asm/prom.h> | ||||||
| #include <asm/machdep.h> | #include <asm/machdep.h> | ||||||
| #include <asm/uaccess.h> | #include <asm/uaccess.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| #include <asm/mmu.h> | #include <asm/mmu.h> | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -55,28 +55,6 @@ static struct device_node *derive_parent(const char *path) | ||||||
| 	return parent; | 	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) | static int pSeries_reconfig_add_node(const char *path, struct property *proplist) | ||||||
| { | { | ||||||
| 	struct device_node *np; | 	struct device_node *np; | ||||||
|  | @ -100,13 +78,12 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist | ||||||
| 		goto out_err; | 		goto out_err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err = pSeries_reconfig_notify(PSERIES_RECONFIG_ADD, np); | 	err = of_attach_node(np); | ||||||
| 	if (err) { | 	if (err) { | ||||||
| 		printk(KERN_ERR "Failed to add device node %s\n", path); | 		printk(KERN_ERR "Failed to add device node %s\n", path); | ||||||
| 		goto out_err; | 		goto out_err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	of_attach_node(np); |  | ||||||
| 	of_node_put(np->parent); | 	of_node_put(np->parent); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -134,9 +111,7 @@ static int pSeries_reconfig_remove_node(struct device_node *np) | ||||||
| 		return -EBUSY; | 		return -EBUSY; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	pSeries_reconfig_notify(PSERIES_RECONFIG_REMOVE, np); |  | ||||||
| 	of_detach_node(np); | 	of_detach_node(np); | ||||||
| 
 |  | ||||||
| 	of_node_put(parent); | 	of_node_put(parent); | ||||||
| 	of_node_put(np); /* Must decrement the refcount */ | 	of_node_put(np); /* Must decrement the refcount */ | ||||||
| 	return 0; | 	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) | static int do_update_property(char *buf, size_t bufsize) | ||||||
| { | { | ||||||
| 	struct device_node *np; | 	struct device_node *np; | ||||||
| 	struct pSeries_reconfig_prop_update upd_value; |  | ||||||
| 	unsigned char *value; | 	unsigned char *value; | ||||||
| 	char *name, *end, *next_prop; | 	char *name, *end, *next_prop; | ||||||
| 	int rc, length; | 	int length; | ||||||
| 	struct property *newprop; | 	struct property *newprop; | ||||||
| 	buf = parse_node(buf, bufsize, &np); | 	buf = parse_node(buf, bufsize, &np); | ||||||
| 	end = buf + bufsize; | 	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")) | 	if (!strcmp(name, "slb-size") || !strcmp(name, "ibm,slb-size")) | ||||||
| 		slb_set_size(*(int *)value); | 		slb_set_size(*(int *)value); | ||||||
| 
 | 
 | ||||||
| 	upd_value.node = np; | 	return prom_update_property(np, newprop); | ||||||
| 	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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ | ||||||
| #include <linux/seq_file.h> | #include <linux/seq_file.h> | ||||||
| #include <linux/root_dev.h> | #include <linux/root_dev.h> | ||||||
| #include <linux/cpuidle.h> | #include <linux/cpuidle.h> | ||||||
|  | #include <linux/of.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/mmu.h> | #include <asm/mmu.h> | ||||||
| #include <asm/processor.h> | #include <asm/processor.h> | ||||||
|  | @ -63,7 +64,6 @@ | ||||||
| #include <asm/smp.h> | #include <asm/smp.h> | ||||||
| #include <asm/firmware.h> | #include <asm/firmware.h> | ||||||
| #include <asm/eeh.h> | #include <asm/eeh.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| 
 | 
 | ||||||
| #include "plpar_wrappers.h" | #include "plpar_wrappers.h" | ||||||
| #include "pseries.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; | 	int err = NOTIFY_OK; | ||||||
| 
 | 
 | ||||||
| 	switch (action) { | 	switch (action) { | ||||||
| 	case PSERIES_RECONFIG_ADD: | 	case OF_RECONFIG_ATTACH_NODE: | ||||||
| 		pci = np->parent->data; | 		pci = np->parent->data; | ||||||
| 		if (pci) { | 		if (pci) { | ||||||
| 			update_dn_pci_info(np, pci->phb); | 			update_dn_pci_info(np, pci->phb); | ||||||
|  | @ -389,7 +389,7 @@ static void __init pSeries_setup_arch(void) | ||||||
| 	/* Find and initialize PCI host bridges */ | 	/* Find and initialize PCI host bridges */ | ||||||
| 	init_pci_config_tokens(); | 	init_pci_config_tokens(); | ||||||
| 	find_and_init_phbs(); | 	find_and_init_phbs(); | ||||||
| 	pSeries_reconfig_notifier_register(&pci_dn_reconfig_nb); | 	of_reconfig_notifier_register(&pci_dn_reconfig_nb); | ||||||
| 
 | 
 | ||||||
| 	pSeries_nvram_init(); | 	pSeries_nvram_init(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -28,7 +28,6 @@ | ||||||
| #include <linux/slab.h> | #include <linux/slab.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/page.h> | #include <asm/page.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| #include <asm/vio.h> | #include <asm/vio.h> | ||||||
| 
 | 
 | ||||||
| #include "nx_csbcpb.h" /* struct nx_csbcpb */ | #include "nx_csbcpb.h" /* struct nx_csbcpb */ | ||||||
|  | @ -1014,26 +1013,23 @@ error_out: | ||||||
|  *	NOTIFY_BAD encoded with error number on failure, use |  *	NOTIFY_BAD encoded with error number on failure, use | ||||||
|  *		notifier_to_errno() to decode this value |  *		notifier_to_errno() to decode this value | ||||||
|  */ |  */ | ||||||
| static int nx842_OF_notifier(struct notifier_block *np, | static int nx842_OF_notifier(struct notifier_block *np, unsigned long action, | ||||||
| 					unsigned long action, |  | ||||||
| 			     void *update) | 			     void *update) | ||||||
| { | { | ||||||
| 	struct pSeries_reconfig_prop_update *upd; | 	struct of_prop_reconfig *upd = update; | ||||||
| 	struct nx842_devdata *local_devdata; | 	struct nx842_devdata *local_devdata; | ||||||
| 	struct device_node *node = NULL; | 	struct device_node *node = NULL; | ||||||
| 
 | 
 | ||||||
| 	upd = (struct pSeries_reconfig_prop_update *)update; |  | ||||||
| 
 |  | ||||||
| 	rcu_read_lock(); | 	rcu_read_lock(); | ||||||
| 	local_devdata = rcu_dereference(devdata); | 	local_devdata = rcu_dereference(devdata); | ||||||
| 	if (local_devdata) | 	if (local_devdata) | ||||||
| 		node = local_devdata->dev->of_node; | 		node = local_devdata->dev->of_node; | ||||||
| 
 | 
 | ||||||
| 	if (local_devdata && | 	if (local_devdata && | ||||||
| 			action == PSERIES_UPDATE_PROPERTY && | 			action == OF_RECONFIG_UPDATE_PROPERTY && | ||||||
| 			!strcmp(upd->node->name, node->name)) { | 			!strcmp(upd->dn->name, node->name)) { | ||||||
| 		rcu_read_unlock(); | 		rcu_read_unlock(); | ||||||
| 		nx842_OF_upd(upd->property); | 		nx842_OF_upd(upd->prop); | ||||||
| 	} else | 	} else | ||||||
| 		rcu_read_unlock(); | 		rcu_read_unlock(); | ||||||
| 
 | 
 | ||||||
|  | @ -1182,7 +1178,7 @@ static int __init nx842_probe(struct vio_dev *viodev, | ||||||
| 	synchronize_rcu(); | 	synchronize_rcu(); | ||||||
| 	kfree(old_devdata); | 	kfree(old_devdata); | ||||||
| 
 | 
 | ||||||
| 	pSeries_reconfig_notifier_register(&nx842_of_nb); | 	of_reconfig_notifier_register(&nx842_of_nb); | ||||||
| 
 | 
 | ||||||
| 	ret = nx842_OF_upd(NULL); | 	ret = nx842_OF_upd(NULL); | ||||||
| 	if (ret && ret != -ENODEV) { | 	if (ret && ret != -ENODEV) { | ||||||
|  | @ -1228,7 +1224,7 @@ static int __exit nx842_remove(struct vio_dev *viodev) | ||||||
| 	spin_lock_irqsave(&devdata_mutex, flags); | 	spin_lock_irqsave(&devdata_mutex, flags); | ||||||
| 	old_devdata = rcu_dereference_check(devdata, | 	old_devdata = rcu_dereference_check(devdata, | ||||||
| 			lockdep_is_held(&devdata_mutex)); | 			lockdep_is_held(&devdata_mutex)); | ||||||
| 	pSeries_reconfig_notifier_unregister(&nx842_of_nb); | 	of_reconfig_notifier_unregister(&nx842_of_nb); | ||||||
| 	rcu_assign_pointer(devdata, NULL); | 	rcu_assign_pointer(devdata, NULL); | ||||||
| 	spin_unlock_irqrestore(&devdata_mutex, flags); | 	spin_unlock_irqrestore(&devdata_mutex, flags); | ||||||
| 	synchronize_rcu(); | 	synchronize_rcu(); | ||||||
|  |  | ||||||
|  | @ -33,7 +33,6 @@ | ||||||
| #include <linux/scatterlist.h> | #include <linux/scatterlist.h> | ||||||
| #include <linux/device.h> | #include <linux/device.h> | ||||||
| #include <linux/of.h> | #include <linux/of.h> | ||||||
| #include <asm/pSeries_reconfig.h> |  | ||||||
| #include <asm/hvcall.h> | #include <asm/hvcall.h> | ||||||
| #include <asm/vio.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); | 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 |  * 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; | 	struct property **next; | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
| 
 | 
 | ||||||
| 	prop->next = NULL; | 	prop->next = NULL; | ||||||
| 	write_lock_irqsave(&devtree_lock, flags); | 	write_lock_irqsave(&devtree_lock, flags); | ||||||
|  | @ -1072,6 +1095,11 @@ int prom_remove_property(struct device_node *np, struct property *prop) | ||||||
| 	struct property **next; | 	struct property **next; | ||||||
| 	unsigned long flags; | 	unsigned long flags; | ||||||
| 	int found = 0; | 	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); | 	write_lock_irqsave(&devtree_lock, flags); | ||||||
| 	next = &np->properties; | 	next = &np->properties; | ||||||
|  | @ -1114,7 +1142,11 @@ int prom_update_property(struct device_node *np, | ||||||
| { | { | ||||||
| 	struct property **next, *oldprop; | 	struct property **next, *oldprop; | ||||||
| 	unsigned long flags; | 	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) | 	if (!newprop->name) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | @ -1160,6 +1192,26 @@ int prom_update_property(struct device_node *np, | ||||||
|  * device tree nodes. |  * 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 | #ifdef CONFIG_PROC_DEVICETREE | ||||||
| static void of_add_proc_dt_entry(struct device_node *dn) | 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. |  * 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; | 	unsigned long flags; | ||||||
|  | 	int rc; | ||||||
|  | 
 | ||||||
|  | 	rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); | ||||||
|  | 	if (rc) | ||||||
|  | 		return rc; | ||||||
| 
 | 
 | ||||||
| 	write_lock_irqsave(&devtree_lock, flags); | 	write_lock_irqsave(&devtree_lock, flags); | ||||||
| 	np->sibling = np->parent->child; | 	np->sibling = np->parent->child; | ||||||
|  | @ -1191,6 +1248,7 @@ void of_attach_node(struct device_node *np) | ||||||
| 	write_unlock_irqrestore(&devtree_lock, flags); | 	write_unlock_irqrestore(&devtree_lock, flags); | ||||||
| 
 | 
 | ||||||
| 	of_add_proc_dt_entry(np); | 	of_add_proc_dt_entry(np); | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_PROC_DEVICETREE | #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 caller must hold a reference to the node.  The memory associated with | ||||||
|  * the node is not freed until its refcount goes to zero. |  * 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; | 	struct device_node *parent; | ||||||
| 	unsigned long flags; | 	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); | 	write_lock_irqsave(&devtree_lock, flags); | ||||||
| 
 | 
 | ||||||
| 	if (of_node_check_flag(np, OF_DETACHED)) { | 	if (of_node_check_flag(np, OF_DETACHED)) { | ||||||
| 		/* someone already detached it */ | 		/* someone already detached it */ | ||||||
| 		write_unlock_irqrestore(&devtree_lock, flags); | 		write_unlock_irqrestore(&devtree_lock, flags); | ||||||
| 		return; | 		return rc; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	parent = np->parent; | 	parent = np->parent; | ||||||
| 	if (!parent) { | 	if (!parent) { | ||||||
| 		write_unlock_irqrestore(&devtree_lock, flags); | 		write_unlock_irqrestore(&devtree_lock, flags); | ||||||
| 		return; | 		return rc; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (allnodes == np) | 	if (allnodes == np) | ||||||
|  | @ -1265,6 +1328,7 @@ void of_detach_node(struct device_node *np) | ||||||
| 	write_unlock_irqrestore(&devtree_lock, flags); | 	write_unlock_irqrestore(&devtree_lock, flags); | ||||||
| 
 | 
 | ||||||
| 	of_remove_proc_dt_entry(np); | 	of_remove_proc_dt_entry(np); | ||||||
|  | 	return rc; | ||||||
| } | } | ||||||
| #endif /* defined(CONFIG_OF_DYNAMIC) */ | #endif /* defined(CONFIG_OF_DYNAMIC) */ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ | ||||||
| #include <linux/mod_devicetable.h> | #include <linux/mod_devicetable.h> | ||||||
| #include <linux/spinlock.h> | #include <linux/spinlock.h> | ||||||
| #include <linux/topology.h> | #include <linux/topology.h> | ||||||
|  | #include <linux/notifier.h> | ||||||
| 
 | 
 | ||||||
| #include <asm/byteorder.h> | #include <asm/byteorder.h> | ||||||
| #include <asm/errno.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, | extern int prom_update_property(struct device_node *np, | ||||||
| 				struct property *newprop); | 				struct property *newprop); | ||||||
| 
 | 
 | ||||||
| #if defined(CONFIG_OF_DYNAMIC) |  | ||||||
| /* For updating the device tree at runtime */ | /* For updating the device tree at runtime */ | ||||||
| extern void of_attach_node(struct device_node *); | #define OF_RECONFIG_ATTACH_NODE		0x0001 | ||||||
| extern void of_detach_node(struct device_node *); | #define OF_RECONFIG_DETACH_NODE		0x0002 | ||||||
| #endif | #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) | #define of_match_ptr(_ptr)	(_ptr) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nathan Fontenot
				Nathan Fontenot