forked from mirrors/linux
		
	of/reconfig: Add OF_DYNAMIC notifier for platform_bus_type
Add OF notifier handler needed for creating/destroying platform devices according to dynamic runtime changes in the DT live tree. Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com> Signed-off-by: Grant Likely <grant.likely@linaro.org>
This commit is contained in:
		
							parent
							
								
									f5242e5a88
								
							
						
					
					
						commit
						801d728c10
					
				
					 3 changed files with 62 additions and 0 deletions
				
			
		| 
						 | 
					@ -1006,6 +1006,7 @@ int __init platform_bus_init(void)
 | 
				
			||||||
	error =  bus_register(&platform_bus_type);
 | 
						error =  bus_register(&platform_bus_type);
 | 
				
			||||||
	if (error)
 | 
						if (error)
 | 
				
			||||||
		device_unregister(&platform_bus);
 | 
							device_unregister(&platform_bus);
 | 
				
			||||||
 | 
						of_platform_register_reconfig_notifier();
 | 
				
			||||||
	return error;
 | 
						return error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -550,4 +550,59 @@ void of_platform_depopulate(struct device *parent)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(of_platform_depopulate);
 | 
					EXPORT_SYMBOL_GPL(of_platform_depopulate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_OF_DYNAMIC
 | 
				
			||||||
 | 
					static int of_platform_notify(struct notifier_block *nb,
 | 
				
			||||||
 | 
									unsigned long action, void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct of_reconfig_data *rd = arg;
 | 
				
			||||||
 | 
						struct platform_device *pdev_parent, *pdev;
 | 
				
			||||||
 | 
						bool children_left;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (of_reconfig_get_state_change(action, rd)) {
 | 
				
			||||||
 | 
						case OF_RECONFIG_CHANGE_ADD:
 | 
				
			||||||
 | 
							/* verify that the parent is a bus */
 | 
				
			||||||
 | 
							if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS))
 | 
				
			||||||
 | 
								return NOTIFY_OK;	/* not for us */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* pdev_parent may be NULL when no bus platform device */
 | 
				
			||||||
 | 
							pdev_parent = of_find_device_by_node(rd->dn->parent);
 | 
				
			||||||
 | 
							pdev = of_platform_device_create(rd->dn, NULL,
 | 
				
			||||||
 | 
									pdev_parent ? &pdev_parent->dev : NULL);
 | 
				
			||||||
 | 
							of_dev_put(pdev_parent);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (pdev == NULL) {
 | 
				
			||||||
 | 
								pr_err("%s: failed to create for '%s'\n",
 | 
				
			||||||
 | 
										__func__, rd->dn->full_name);
 | 
				
			||||||
 | 
								/* of_platform_device_create tosses the error code */
 | 
				
			||||||
 | 
								return notifier_from_errno(-EINVAL);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case OF_RECONFIG_CHANGE_REMOVE:
 | 
				
			||||||
 | 
							/* find our device by node */
 | 
				
			||||||
 | 
							pdev = of_find_device_by_node(rd->dn);
 | 
				
			||||||
 | 
							if (pdev == NULL)
 | 
				
			||||||
 | 
								return NOTIFY_OK;	/* no? not meant for us */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* unregister takes one ref away */
 | 
				
			||||||
 | 
							of_platform_device_destroy(&pdev->dev, &children_left);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* and put the reference of the find */
 | 
				
			||||||
 | 
							of_dev_put(pdev);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NOTIFY_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct notifier_block platform_of_notifier = {
 | 
				
			||||||
 | 
						.notifier_call = of_platform_notify,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void of_platform_register_reconfig_notifier(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						WARN_ON(of_reconfig_notifier_register(&platform_of_notifier));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif /* CONFIG_OF_DYNAMIC */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* CONFIG_OF_ADDRESS */
 | 
					#endif /* CONFIG_OF_ADDRESS */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,4 +84,10 @@ static inline int of_platform_populate(struct device_node *root,
 | 
				
			||||||
static inline void of_platform_depopulate(struct device *parent) { }
 | 
					static inline void of_platform_depopulate(struct device *parent) { }
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_OF_DYNAMIC
 | 
				
			||||||
 | 
					extern void of_platform_register_reconfig_notifier(void);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					static inline void of_platform_register_reconfig_notifier(void) { }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif	/* _LINUX_OF_PLATFORM_H */
 | 
					#endif	/* _LINUX_OF_PLATFORM_H */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue