|
@@ -550,4 +550,59 @@ void of_platform_depopulate(struct device *parent)
|
|
|
}
|
|
|
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 */
|