]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/of/platform.c
Revert "of/platform: Disable generic device linking code for PowerPC"
[linux.git] / drivers / of / platform.c
index 7801e25e68954e6b323354df7d745c0d4c084641..a3cd69772264b89911c5f341636ecb9fa7789564 100644 (file)
@@ -37,11 +37,6 @@ static const struct of_device_id of_skipped_node_table[] = {
        {} /* Empty terminated list */
 };
 
-static int of_dev_node_match(struct device *dev, const void *data)
-{
-       return dev->of_node == data;
-}
-
 /**
  * of_find_device_by_node - Find the platform_device associated with a node
  * @np: Pointer to device tree node
@@ -55,7 +50,7 @@ struct platform_device *of_find_device_by_node(struct device_node *np)
 {
        struct device *dev;
 
-       dev = bus_find_device(&platform_bus_type, NULL, np, of_dev_node_match);
+       dev = bus_find_device_by_of_node(&platform_bus_type, np);
        return dev ? to_platform_device(dev) : NULL;
 }
 EXPORT_SYMBOL(of_find_device_by_node);
@@ -485,6 +480,7 @@ int of_platform_populate(struct device_node *root,
        pr_debug("%s()\n", __func__);
        pr_debug(" starting at: %pOF\n", root);
 
+       device_links_supplier_sync_state_pause();
        for_each_child_of_node(root, child) {
                rc = of_platform_bus_create(child, matches, lookup, parent, true);
                if (rc) {
@@ -492,6 +488,8 @@ int of_platform_populate(struct device_node *root,
                        break;
                }
        }
+       device_links_supplier_sync_state_resume();
+
        of_node_set_flag(root, OF_POPULATED_BUS);
 
        of_node_put(root);
@@ -508,6 +506,183 @@ int of_platform_default_populate(struct device_node *root,
 }
 EXPORT_SYMBOL_GPL(of_platform_default_populate);
 
+static bool of_link_is_valid(struct device_node *con, struct device_node *sup)
+{
+       of_node_get(sup);
+       /*
+        * Don't allow linking a device node as a consumer of one of its
+        * descendant nodes. By definition, a child node can't be a functional
+        * dependency for the parent node.
+        */
+       while (sup) {
+               if (sup == con) {
+                       of_node_put(sup);
+                       return false;
+               }
+               sup = of_get_next_parent(sup);
+       }
+       return true;
+}
+
+static int of_link_to_phandle(struct device *dev, struct device_node *sup_np)
+{
+       struct platform_device *sup_dev;
+       u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER;
+       int ret = 0;
+
+       /*
+        * Since we are trying to create device links, we need to find
+        * the actual device node that owns this supplier phandle.
+        * Often times it's the same node, but sometimes it can be one
+        * of the parents. So walk up the parent till you find a
+        * device.
+        */
+       while (sup_np && !of_find_property(sup_np, "compatible", NULL))
+               sup_np = of_get_next_parent(sup_np);
+       if (!sup_np)
+               return 0;
+
+       if (!of_link_is_valid(dev->of_node, sup_np)) {
+               of_node_put(sup_np);
+               return 0;
+       }
+       sup_dev = of_find_device_by_node(sup_np);
+       of_node_put(sup_np);
+       if (!sup_dev)
+               return -ENODEV;
+       if (!device_link_add(dev, &sup_dev->dev, dl_flags))
+               ret = -ENODEV;
+       put_device(&sup_dev->dev);
+       return ret;
+}
+
+static struct device_node *parse_prop_cells(struct device_node *np,
+                                           const char *prop, int index,
+                                           const char *binding,
+                                           const char *cell)
+{
+       struct of_phandle_args sup_args;
+
+       /* Don't need to check property name for every index. */
+       if (!index && strcmp(prop, binding))
+               return NULL;
+
+       if (of_parse_phandle_with_args(np, binding, cell, index, &sup_args))
+               return NULL;
+
+       return sup_args.np;
+}
+
+static struct device_node *parse_clocks(struct device_node *np,
+                                       const char *prop, int index)
+{
+       return parse_prop_cells(np, prop, index, "clocks", "#clock-cells");
+}
+
+static struct device_node *parse_interconnects(struct device_node *np,
+                                              const char *prop, int index)
+{
+       return parse_prop_cells(np, prop, index, "interconnects",
+                               "#interconnect-cells");
+}
+
+static int strcmp_suffix(const char *str, const char *suffix)
+{
+       unsigned int len, suffix_len;
+
+       len = strlen(str);
+       suffix_len = strlen(suffix);
+       if (len <= suffix_len)
+               return -1;
+       return strcmp(str + len - suffix_len, suffix);
+}
+
+static struct device_node *parse_regulators(struct device_node *np,
+                                           const char *prop, int index)
+{
+       if (index || strcmp_suffix(prop, "-supply"))
+               return NULL;
+
+       return of_parse_phandle(np, prop, 0);
+}
+
+/**
+ * struct supplier_bindings - Information for parsing supplier DT binding
+ *
+ * @parse_prop:                If the function cannot parse the property, return NULL.
+ *                     Otherwise, return the phandle listed in the property
+ *                     that corresponds to the index.
+ */
+struct supplier_bindings {
+       struct device_node *(*parse_prop)(struct device_node *np,
+                                         const char *name, int index);
+};
+
+static const struct supplier_bindings bindings[] = {
+       { .parse_prop = parse_clocks, },
+       { .parse_prop = parse_interconnects, },
+       { .parse_prop = parse_regulators, },
+       { },
+};
+
+static int of_link_property(struct device *dev, struct device_node *con_np,
+                            const char *prop)
+{
+       struct device_node *phandle;
+       const struct supplier_bindings *s = bindings;
+       unsigned int i = 0;
+       bool done = true, matched = false;
+
+       while (!matched && s->parse_prop) {
+               while ((phandle = s->parse_prop(con_np, prop, i))) {
+                       matched = true;
+                       i++;
+                       if (of_link_to_phandle(dev, phandle))
+                               /*
+                                * Don't stop at the first failure. See
+                                * Documentation for bus_type.add_links for
+                                * more details.
+                                */
+                               done = false;
+               }
+               s++;
+       }
+       return done ? 0 : -ENODEV;
+}
+
+static int __of_link_to_suppliers(struct device *dev,
+                                 struct device_node *con_np)
+{
+       struct device_node *child;
+       struct property *p;
+       bool done = true;
+
+       for_each_property_of_node(con_np, p)
+               if (of_link_property(dev, con_np, p->name))
+                       done = false;
+
+       for_each_child_of_node(con_np, child)
+               if (__of_link_to_suppliers(dev, child))
+                       done = false;
+
+       return done ? 0 : -ENODEV;
+}
+
+static bool of_devlink;
+core_param(of_devlink, of_devlink, bool, 0);
+
+static int of_link_to_suppliers(struct device *dev)
+{
+       if (!of_devlink)
+               return 0;
+       if (unlikely(!dev->of_node))
+               return 0;
+       if (of_match_node(of_default_bus_match_table, dev->of_node))
+               return 0;
+
+       return __of_link_to_suppliers(dev, dev->of_node);
+}
+
 #ifndef CONFIG_PPC
 static const struct of_device_id reserved_mem_matches[] = {
        { .compatible = "qcom,rmtfs-mem" },
@@ -523,6 +698,8 @@ static int __init of_platform_default_populate_init(void)
        if (!of_have_populated_dt())
                return -ENODEV;
 
+       platform_bus_type.add_links = of_link_to_suppliers;
+       device_links_supplier_sync_state_pause();
        /*
         * Handle certain compatibles explicitly, since we don't want to create
         * platform_devices for every node in /reserved-memory with a
@@ -543,6 +720,14 @@ static int __init of_platform_default_populate_init(void)
        return 0;
 }
 arch_initcall_sync(of_platform_default_populate_init);
+
+static int __init of_platform_sync_state_init(void)
+{
+       if (of_have_populated_dt())
+               device_links_supplier_sync_state_resume();
+       return 0;
+}
+late_initcall_sync(of_platform_sync_state_init);
 #endif
 
 int of_platform_device_destroy(struct device *dev, void *data)