Merge tag 'for-v3.13-rc/hwmod-fixes-a' of git://git.kernel.org/pub/scm/linux/kernel...
authorKevin Hilman <khilman@linaro.org>
Tue, 10 Dec 2013 15:44:32 +0000 (07:44 -0800)
committerKevin Hilman <khilman@linaro.org>
Tue, 10 Dec 2013 15:44:48 +0000 (07:44 -0800)
From Paul Walmsley:
ARM: OMAP2+: hwmod code/data: fixes for v3.13-rc

Fix a few hwmod code problems involving recovery with bad data and bad
IP block OCP reset handling.  Also, fix the hwmod data to enable IP
block OCP reset for the OMAP USBHOST devices on OMAP3+.

Basic build, boot, and PM tests are available here:

http://www.pwsan.com/omap/testlogs/prcm_fixes_a_v3.13-rc/20131209030611/

* tag 'for-v3.13-rc/hwmod-fixes-a' of git://git.kernel.org/pub/scm/linux/kernel/git/pjw/omap-pending:
  ARM: OMAP2+: hwmod: Fix usage of invalid iclk / oclk when clock node is not present
  ARM: OMAP3: hwmod data: Don't prevent RESET of USB Host module
  ARM: OMAP2+: hwmod: Fix SOFTRESET logic
  ARM: OMAP4+: hwmod data: Don't prevent RESET of USB Host module

Signed-off-by: Kevin Hilman <khilman@linaro.org>
1  2 
arch/arm/mach-omap2/omap_hwmod.c

@@@ -399,7 -399,7 +399,7 @@@ static int _set_clockactivity(struct om
  }
  
  /**
-  * _set_softreset: set OCP_SYSCONFIG.CLOCKACTIVITY bits in @v
+  * _set_softreset: set OCP_SYSCONFIG.SOFTRESET bit in @v
   * @oh: struct omap_hwmod *
   * @v: pointer to register contents to modify
   *
@@@ -426,6 -426,36 +426,36 @@@ static int _set_softreset(struct omap_h
        return 0;
  }
  
+ /**
+  * _clear_softreset: clear OCP_SYSCONFIG.SOFTRESET bit in @v
+  * @oh: struct omap_hwmod *
+  * @v: pointer to register contents to modify
+  *
+  * Clear the SOFTRESET bit in @v for hwmod @oh.  Returns -EINVAL upon
+  * error or 0 upon success.
+  */
+ static int _clear_softreset(struct omap_hwmod *oh, u32 *v)
+ {
+       u32 softrst_mask;
+       if (!oh->class->sysc ||
+           !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET))
+               return -EINVAL;
+       if (!oh->class->sysc->sysc_fields) {
+               WARN(1,
+                    "omap_hwmod: %s: sysc_fields absent for sysconfig class\n",
+                    oh->name);
+               return -EINVAL;
+       }
+       softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift);
+       *v &= ~softrst_mask;
+       return 0;
+ }
  /**
   * _wait_softreset_complete - wait for an OCP softreset to complete
   * @oh: struct omap_hwmod * to wait on
@@@ -785,6 -815,7 +815,7 @@@ static int _init_interface_clks(struct 
                        pr_warning("omap_hwmod: %s: cannot clk_get interface_clk %s\n",
                                   oh->name, os->clk);
                        ret = -EINVAL;
+                       continue;
                }
                os->_clk = c;
                /*
@@@ -821,6 -852,7 +852,7 @@@ static int _init_opt_clks(struct omap_h
                        pr_warning("omap_hwmod: %s: cannot clk_get opt_clk %s\n",
                                   oh->name, oc->clk);
                        ret = -EINVAL;
+                       continue;
                }
                oc->_clk = c;
                /*
@@@ -1911,6 -1943,12 +1943,12 @@@ static int _ocp_softreset(struct omap_h
        ret = _set_softreset(oh, &v);
        if (ret)
                goto dis_opt_clks;
+       _write_sysconfig(v, oh);
+       ret = _clear_softreset(oh, &v);
+       if (ret)
+               goto dis_opt_clks;
        _write_sysconfig(v, oh);
  
        if (oh->class->sysc->srst_udelay)
@@@ -2326,80 -2364,38 +2364,80 @@@ static int _shutdown(struct omap_hwmod 
        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
   * -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;
                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;
        }
  
   */
  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);
@@@ -3227,6 -3207,11 +3265,11 @@@ int omap_hwmod_softreset(struct omap_hw
                goto error;
        _write_sysconfig(v, oh);
  
+       ret = _clear_softreset(oh, &v);
+       if (ret)
+               goto error;
+       _write_sysconfig(v, oh);
  error:
        return ret;
  }