Merge tag 'iommu-config-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / drivers / of / platform.c
index b89caf8..5b33c6a 100644 (file)
@@ -139,7 +139,7 @@ struct platform_device *of_device_alloc(struct device_node *np,
        }
 
        dev->dev.of_node = of_node_get(np);
-       dev->dev.parent = parent;
+       dev->dev.parent = parent ? : &platform_bus;
 
        if (bus_id)
                dev_set_name(&dev->dev, "%s", bus_id);
@@ -293,7 +293,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
 
        /* setup generic device info */
        dev->dev.of_node = of_node_get(node);
-       dev->dev.parent = parent;
+       dev->dev.parent = parent ? : &platform_bus;
        dev->dev.platform_data = platform_data;
        if (bus_id)
                dev_set_name(&dev->dev, "%s", bus_id);
@@ -502,6 +502,7 @@ int of_platform_populate(struct device_node *root,
                if (rc)
                        break;
        }
+       of_node_set_flag(root, OF_POPULATED_BUS);
 
        of_node_put(root);
        return rc;
@@ -544,8 +545,66 @@ static int of_platform_device_destroy(struct device *dev, void *data)
  */
 void of_platform_depopulate(struct device *parent)
 {
-       device_for_each_child(parent, NULL, of_platform_device_destroy);
+       if (parent->of_node && of_node_check_flag(parent->of_node, OF_POPULATED_BUS)) {
+               device_for_each_child(parent, NULL, of_platform_device_destroy);
+               of_node_clear_flag(parent->of_node, OF_POPULATED_BUS);
+       }
 }
 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 */