* omap_hwmod implementation for OMAP2/3/4
  *
  * Copyright (C) 2009-2011 Nokia Corporation
- * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011-2012 Texas Instruments, Inc.
  *
  * Paul Walmsley, Benoît Cousson, Kevin Hilman
  *
  * @oh: struct omap_hwmod *
  *
  * Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit.  hwmod must be
- * enabled for this to work.  Returns -EINVAL if the hwmod cannot be
- * reset this way or if the hwmod is in the wrong state, -ETIMEDOUT if
- * the module did not reset in time, or 0 upon success.
+ * enabled for this to work.  Returns -ENOENT if the hwmod cannot be
+ * reset this way, -EINVAL if the hwmod is in the wrong state,
+ * -ETIMEDOUT if the module did not reset in time, or 0 upon success.
  *
  * In OMAP3 a specific SYSSTATUS register is used to get the reset status.
  * Starting in OMAP4, some IPs do not have SYSSTATUS registers and instead
 
        if (!oh->class->sysc ||
            !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET))
-               return -EINVAL;
+               return -ENOENT;
 
        /* clocks must be on for this operation */
        if (oh->_state != _HWMOD_STATE_ENABLED) {
  * _reset - reset an omap_hwmod
  * @oh: struct omap_hwmod *
  *
- * Resets an omap_hwmod @oh.  The default software reset mechanism for
- * most OMAP IP blocks is triggered via the OCP_SYSCONFIG.SOFTRESET
- * bit.  However, some hwmods cannot be reset via this method: some
- * are not targets and therefore have no OCP header registers to
- * access; others (like the IVA) have idiosyncratic reset sequences.
- * So for these relatively rare cases, custom reset code can be
- * supplied in the struct omap_hwmod_class .reset function pointer.
- * Passes along the return value from either _reset() or the custom
- * reset function - these must return -EINVAL if the hwmod cannot be
- * reset this way or if the hwmod is in the wrong state, -ETIMEDOUT if
- * the module did not reset in time, or 0 upon success.
+ * Resets an omap_hwmod @oh.  If the module has a custom reset
+ * function pointer defined, then call it to reset the IP block, and
+ * pass along its return value to the caller.  Otherwise, if the IP
+ * block has an OCP_SYSCONFIG register with a SOFTRESET bitfield
+ * associated with it, call a function to reset the IP block via that
+ * method, and pass along the return value to the caller.  Finally, if
+ * the IP block has some hardreset lines associated with it, assert
+ * all of those, but do _not_ deassert them. (This is because driver
+ * authors have expressed an apparent requirement to control the
+ * deassertion of the hardreset lines themselves.)
+ *
+ * The default software reset mechanism for most OMAP IP blocks is
+ * triggered via the OCP_SYSCONFIG.SOFTRESET bit.  However, some
+ * hwmods cannot be reset via this method.  Some are not targets and
+ * therefore have no OCP header registers to access.  Others (like the
+ * IVA) have idiosyncratic reset sequences.  So for these relatively
+ * rare cases, custom reset code can be supplied in the struct
+ * omap_hwmod_class .reset function pointer.  Passes along the return
+ * value from either _ocp_softreset() or the custom reset function -
+ * these must return -EINVAL if the hwmod cannot be reset this way or
+ * if the hwmod is in the wrong state, -ETIMEDOUT if the module did
+ * not reset in time, or 0 upon success.
  */
 static int _reset(struct omap_hwmod *oh)
 {
-       int ret;
+       int i, r;
 
        pr_debug("omap_hwmod: %s: resetting\n", oh->name);
 
+       if (oh->class->reset) {
+               r = oh->class->reset(oh);
+       } else {
+               if (oh->rst_lines_cnt > 0) {
+                       for (i = 0; i < oh->rst_lines_cnt; i++)
+                               _assert_hardreset(oh, oh->rst_lines[i].name);
+                       return 0;
+               } else {
+                       r = _ocp_softreset(oh);
+                       if (r == -ENOENT)
+                               r = 0;
+               }
+       }
+
        /*
-        * XXX We're not resetting modules with hardreset lines
-        * automatically here.  Should we do this also, or just expect
-        * those modules to define custom reset functions?
+        * OCP_SYSCONFIG bits need to be reprogrammed after a
+        * softreset.  The _enable() function should be split to avoid
+        * the rewrite of the OCP_SYSCONFIG register.
         */
-       ret = (oh->class->reset) ? oh->class->reset(oh) : _ocp_softreset(oh);
-
        if (oh->class->sysc) {
                _update_sysc_cache(oh);
                _enable_sysc(oh);
        }
 
-       return ret;
+       return r;
 }
 
 /**