]> Pileus Git - ~andy/linux/blobdiff - arch/arm/mach-omap2/omap_hwmod.c
ARM: OMAP2+: Fix overwriting hwmod data with data from device tree
[~andy/linux] / arch / arm / mach-omap2 / omap_hwmod.c
index e3f0ecaf87dd76c3b075c389173ac89faff04a6a..ee655dab672f87ebd20d00e4046ea9d0f5264277 100644 (file)
@@ -2326,38 +2326,80 @@ static int _shutdown(struct omap_hwmod *oh)
        return 0;
 }
 
+static int of_dev_find_hwmod(struct device_node *np,
+                            struct omap_hwmod *oh)
+{
+       int count, i, res;
+       const char *p;
+
+       count = of_property_count_strings(np, "ti,hwmods");
+       if (count < 1)
+               return -ENODEV;
+
+       for (i = 0; i < count; i++) {
+               res = of_property_read_string_index(np, "ti,hwmods",
+                                                   i, &p);
+               if (res)
+                       continue;
+               if (!strcmp(p, oh->name)) {
+                       pr_debug("omap_hwmod: dt %s[%i] uses hwmod %s\n",
+                                np->name, i, oh->name);
+                       return i;
+               }
+       }
+
+       return -ENODEV;
+}
+
 /**
  * of_dev_hwmod_lookup - look up needed hwmod from dt blob
  * @np: struct device_node *
  * @oh: struct omap_hwmod *
+ * @index: index of the entry found
+ * @found: struct device_node * found or NULL
  *
  * Parse the dt blob and find out needed hwmod. Recursive function is
  * implemented to take care hierarchical dt blob parsing.
- * Return: The device node on success or NULL on failure.
+ * Return: Returns 0 on success, -ENODEV when not found.
  */
-static struct device_node *of_dev_hwmod_lookup(struct device_node *np,
-                                               struct omap_hwmod *oh)
+static int of_dev_hwmod_lookup(struct device_node *np,
+                              struct omap_hwmod *oh,
+                              int *index,
+                              struct device_node **found)
 {
-       struct device_node *np0 = NULL, *np1 = NULL;
-       const char *p;
+       struct device_node *np0 = NULL;
+       int res;
+
+       res = of_dev_find_hwmod(np, oh);
+       if (res >= 0) {
+               *found = np;
+               *index = res;
+               return 0;
+       }
 
        for_each_child_of_node(np, np0) {
-               if (of_find_property(np0, "ti,hwmods", NULL)) {
-                       p = of_get_property(np0, "ti,hwmods", NULL);
-                       if (!strcmp(p, oh->name))
-                               return np0;
-                       np1 = of_dev_hwmod_lookup(np0, oh);
-                       if (np1)
-                               return np1;
+               struct device_node *fc;
+               int i;
+
+               res = of_dev_hwmod_lookup(np0, oh, &i, &fc);
+               if (res == 0) {
+                       *found = fc;
+                       *index = i;
+                       return 0;
                }
        }
-       return NULL;
+
+       *found = NULL;
+       *index = 0;
+
+       return -ENODEV;
 }
 
 /**
  * _init_mpu_rt_base - populate the virtual address for a hwmod
  * @oh: struct omap_hwmod * to locate the virtual address
  * @data: (unused, caller should pass NULL)
+ * @index: index of the reg entry iospace in device tree
  * @np: struct device_node * of the IP block's device node in the DT data
  *
  * Cache the virtual address used by the MPU to access this IP block's
@@ -2368,7 +2410,7 @@ static struct device_node *of_dev_hwmod_lookup(struct device_node *np,
  * -ENXIO on absent or invalid register target address space.
  */
 static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data,
-                                   struct device_node *np)
+                                   int index, struct device_node *np)
 {
        struct omap_hwmod_addr_space *mem;
        void __iomem *va_start = NULL;
@@ -2390,13 +2432,17 @@ static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data,
                if (!np)
                        return -ENXIO;
 
-               va_start = of_iomap(np, oh->mpu_rt_idx);
+               va_start = of_iomap(np, index + oh->mpu_rt_idx);
        } else {
                va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start);
        }
 
        if (!va_start) {
-               pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name);
+               if (mem)
+                       pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name);
+               else
+                       pr_err("omap_hwmod: %s: Missing dt reg%i for %s\n",
+                              oh->name, index, np->full_name);
                return -ENXIO;
        }
 
@@ -2422,17 +2468,29 @@ static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data,
  */
 static int __init _init(struct omap_hwmod *oh, void *data)
 {
-       int r;
+       int r, index;
        struct device_node *np = NULL;
 
        if (oh->_state != _HWMOD_STATE_REGISTERED)
                return 0;
 
-       if (of_have_populated_dt())
-               np = of_dev_hwmod_lookup(of_find_node_by_name(NULL, "ocp"), oh);
+       if (of_have_populated_dt()) {
+               struct device_node *bus;
+
+               bus = of_find_node_by_name(NULL, "ocp");
+               if (!bus)
+                       return -ENODEV;
+
+               r = of_dev_hwmod_lookup(bus, oh, &index, &np);
+               if (r)
+                       pr_debug("omap_hwmod: %s missing dt data\n", oh->name);
+               else if (np && index)
+                       pr_warn("omap_hwmod: %s using broken dt data from %s\n",
+                               oh->name, np->name);
+       }
 
        if (oh->class->sysc) {
-               r = _init_mpu_rt_base(oh, NULL, np);
+               r = _init_mpu_rt_base(oh, NULL, index, np);
                if (r < 0) {
                        WARN(1, "omap_hwmod: %s: doesn't have mpu register target base\n",
                             oh->name);