]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/arm/mach-omap2/omap_hwmod.c
Merge branch 'master' into tk71
[mv-sheeva.git] / arch / arm / mach-omap2 / omap_hwmod.c
index cb911d7d1a3c1535ba88eda74577ea6ef2e1ecd0..e282e35769fd7efe383e4be33116d8e8ca492e91 100644 (file)
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- * This code manages "OMAP modules" (on-chip devices) and their
- * integration with Linux device driver and bus code.
- *
- * References:
+ * Introduction
+ * ------------
+ * One way to view an OMAP SoC is as a collection of largely unrelated
+ * IP blocks connected by interconnects.  The IP blocks include
+ * devices such as ARM processors, audio serial interfaces, UARTs,
+ * etc.  Some of these devices, like the DSP, are created by TI;
+ * others, like the SGX, largely originate from external vendors.  In
+ * TI's documentation, on-chip devices are referred to as "OMAP
+ * modules."  Some of these IP blocks are identical across several
+ * OMAP versions.  Others are revised frequently.
+ *
+ * These OMAP modules are tied together by various interconnects.
+ * Most of the address and data flow between modules is via OCP-based
+ * interconnects such as the L3 and L4 buses; but there are other
+ * interconnects that distribute the hardware clock tree, handle idle
+ * and reset signaling, supply power, and connect the modules to
+ * various pads or balls on the OMAP package.
+ *
+ * OMAP hwmod provides a consistent way to describe the on-chip
+ * hardware blocks and their integration into the rest of the chip.
+ * This description can be automatically generated from the TI
+ * hardware database.  OMAP hwmod provides a standard, consistent API
+ * to reset, enable, idle, and disable these hardware blocks.  And
+ * hwmod provides a way for other core code, such as the Linux device
+ * code or the OMAP power management and address space mapping code,
+ * to query the hardware database.
+ *
+ * Using hwmod
+ * -----------
+ * Drivers won't call hwmod functions directly.  That is done by the
+ * omap_device code, and in rare occasions, by custom integration code
+ * in arch/arm/ *omap*.  The omap_device code includes functions to
+ * build a struct platform_device using omap_hwmod data, and that is
+ * currently how hwmod data is communicated to drivers and to the
+ * Linux driver model.  Most drivers will call omap_hwmod functions only
+ * indirectly, via pm_runtime*() functions.
+ *
+ * From a layering perspective, here is where the OMAP hwmod code
+ * fits into the kernel software stack:
+ *
+ *            +-------------------------------+
+ *            |      Device driver code       |
+ *            |      (e.g., drivers/)         |
+ *            +-------------------------------+
+ *            |      Linux driver model       |
+ *            |     (platform_device /        |
+ *            |  platform_driver data/code)   |
+ *            +-------------------------------+
+ *            | OMAP core-driver integration  |
+ *            |(arch/arm/mach-omap2/devices.c)|
+ *            +-------------------------------+
+ *            |      omap_device code         |
+ *            | (../plat-omap/omap_device.c)  |
+ *            +-------------------------------+
+ *   ---->    |    omap_hwmod code/data       |    <-----
+ *            | (../mach-omap2/omap_hwmod*)   |
+ *            +-------------------------------+
+ *            | OMAP clock/PRCM/register fns  |
+ *            | (__raw_{read,write}l, clk*)   |
+ *            +-------------------------------+
+ *
+ * Device drivers should not contain any OMAP-specific code or data in
+ * them.  They should only contain code to operate the IP block that
+ * the driver is responsible for.  This is because these IP blocks can
+ * also appear in other SoCs, either from TI (such as DaVinci) or from
+ * other manufacturers; and drivers should be reusable across other
+ * platforms.
+ *
+ * The OMAP hwmod code also will attempt to reset and idle all on-chip
+ * devices upon boot.  The goal here is for the kernel to be
+ * completely self-reliant and independent from bootloaders.  This is
+ * to ensure a repeatable configuration, both to ensure consistent
+ * runtime behavior, and to make it easier for others to reproduce
+ * bugs.
+ *
+ * OMAP module activity states
+ * ---------------------------
+ * The hwmod code considers modules to be in one of several activity
+ * states.  IP blocks start out in an UNKNOWN state, then once they
+ * are registered via the hwmod code, proceed to the REGISTERED state.
+ * Once their clock names are resolved to clock pointers, the module
+ * enters the CLKS_INITED state; and finally, once the module has been
+ * reset and the integration registers programmed, the INITIALIZED state
+ * is entered.  The hwmod code will then place the module into either
+ * the IDLE state to save power, or in the case of a critical system
+ * module, the ENABLED state.
+ *
+ * OMAP core integration code can then call omap_hwmod*() functions
+ * directly to move the module between the IDLE, ENABLED, and DISABLED
+ * states, as needed.  This is done during both the PM idle loop, and
+ * in the OMAP core integration code's implementation of the PM runtime
+ * functions.
+ *
+ * References
+ * ----------
+ * This is a partial list.
  * - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064)
  * - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090)
  * - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108)
  * - Open Core Protocol Specification 2.2
  *
  * To do:
- * - pin mux handling
  * - handle IO mapping
  * - bus throughput & module latency measurement code
  *
 #include <linux/err.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
+#include <linux/spinlock.h>
 
 #include <plat/common.h>
 #include <plat/cpu.h>
-#include <plat/clockdomain.h>
-#include <plat/powerdomain.h>
+#include "clockdomain.h"
+#include "powerdomain.h"
 #include <plat/clock.h>
 #include <plat/omap_hwmod.h>
+#include <plat/prcm.h>
 
-#include "cm.h"
+#include "cm2xxx_3xxx.h"
+#include "cm44xx.h"
+#include "prm2xxx_3xxx.h"
+#include "prm44xx.h"
+#include "mux.h"
 
-/* Maximum microseconds to wait for OMAP module to reset */
-#define MAX_MODULE_RESET_WAIT          10000
+/* Maximum microseconds to wait for OMAP module to softreset */
+#define MAX_MODULE_SOFTRESET_WAIT      10000
 
 /* Name of the OMAP hwmod for the MPU */
 #define MPU_INITIATOR_NAME             "mpu"
 /* omap_hwmod_list contains all registered struct omap_hwmods */
 static LIST_HEAD(omap_hwmod_list);
 
-static DEFINE_MUTEX(omap_hwmod_mutex);
-
 /* mpu_oh: used to add/remove MPU initiator from sleepdep list */
 static struct omap_hwmod *mpu_oh;
 
@@ -90,7 +185,7 @@ static int _update_sysc_cache(struct omap_hwmod *oh)
 
        /* XXX ensure module interface clock is up */
 
-       oh->_sysc_cache = omap_hwmod_readl(oh, oh->class->sysc->sysc_offs);
+       oh->_sysc_cache = omap_hwmod_read(oh, oh->class->sysc->sysc_offs);
 
        if (!(oh->class->sysc->sysc_flags & SYSC_NO_CACHE))
                oh->_int_flags |= _HWMOD_SYSCONFIG_LOADED;
@@ -115,10 +210,9 @@ static void _write_sysconfig(u32 v, struct omap_hwmod *oh)
 
        /* XXX ensure module interface clock is up */
 
-       if (oh->_sysc_cache != v) {
-               oh->_sysc_cache = v;
-               omap_hwmod_writel(v, oh, oh->class->sysc->sysc_offs);
-       }
+       /* Module might have lost context, always update cache and register */
+       oh->_sysc_cache = v;
+       omap_hwmod_write(v, oh, oh->class->sysc->sysc_offs);
 }
 
 /**
@@ -294,12 +388,13 @@ static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle,
  * Allow the hardware module @oh to send wakeups.  Returns -EINVAL
  * upon error or 0 upon success.
  */
-static int _enable_wakeup(struct omap_hwmod *oh)
+static int _enable_wakeup(struct omap_hwmod *oh, u32 *v)
 {
-       u32 v, wakeup_mask;
+       u32 wakeup_mask;
 
        if (!oh->class->sysc ||
-           !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
+           !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) ||
+             (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)))
                return -EINVAL;
 
        if (!oh->class->sysc->sysc_fields) {
@@ -309,9 +404,10 @@ static int _enable_wakeup(struct omap_hwmod *oh)
 
        wakeup_mask = (0x1 << oh->class->sysc->sysc_fields->enwkup_shift);
 
-       v = oh->_sysc_cache;
-       v |= wakeup_mask;
-       _write_sysconfig(v, oh);
+       *v |= wakeup_mask;
+
+       if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)
+               _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART_WKUP, v);
 
        /* XXX test pwrdm_get_wken for this hwmod's subsystem */
 
@@ -327,12 +423,13 @@ static int _enable_wakeup(struct omap_hwmod *oh)
  * Prevent the hardware module @oh to send wakeups.  Returns -EINVAL
  * upon error or 0 upon success.
  */
-static int _disable_wakeup(struct omap_hwmod *oh)
+static int _disable_wakeup(struct omap_hwmod *oh, u32 *v)
 {
-       u32 v, wakeup_mask;
+       u32 wakeup_mask;
 
        if (!oh->class->sysc ||
-           !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
+           !((oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP) ||
+             (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)))
                return -EINVAL;
 
        if (!oh->class->sysc->sysc_fields) {
@@ -342,9 +439,10 @@ static int _disable_wakeup(struct omap_hwmod *oh)
 
        wakeup_mask = (0x1 << oh->class->sysc->sysc_fields->enwkup_shift);
 
-       v = oh->_sysc_cache;
-       v &= ~wakeup_mask;
-       _write_sysconfig(v, oh);
+       *v &= ~wakeup_mask;
+
+       if (oh->class->sysc->idlemodes & SIDLE_SMART_WKUP)
+               _set_slave_idlemode(oh, HWMOD_IDLEMODE_SMART, v);
 
        /* XXX test pwrdm_get_wken for this hwmod's subsystem */
 
@@ -544,6 +642,36 @@ static int _disable_clocks(struct omap_hwmod *oh)
        return 0;
 }
 
+static void _enable_optional_clocks(struct omap_hwmod *oh)
+{
+       struct omap_hwmod_opt_clk *oc;
+       int i;
+
+       pr_debug("omap_hwmod: %s: enabling optional clocks\n", oh->name);
+
+       for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
+               if (oc->_clk) {
+                       pr_debug("omap_hwmod: enable %s:%s\n", oc->role,
+                                oc->_clk->name);
+                       clk_enable(oc->_clk);
+               }
+}
+
+static void _disable_optional_clocks(struct omap_hwmod *oh)
+{
+       struct omap_hwmod_opt_clk *oc;
+       int i;
+
+       pr_debug("omap_hwmod: %s: disabling optional clocks\n", oh->name);
+
+       for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++)
+               if (oc->_clk) {
+                       pr_debug("omap_hwmod: disable %s:%s\n", oc->role,
+                                oc->_clk->name);
+                       clk_disable(oc->_clk);
+               }
+}
+
 /**
  * _find_mpu_port_index - find hwmod OCP slave port ID intended for MPU use
  * @oh: struct omap_hwmod *
@@ -551,7 +679,7 @@ static int _disable_clocks(struct omap_hwmod *oh)
  * Returns the array index of the OCP slave port that the MPU
  * addresses the device on, or -EINVAL upon error or not found.
  */
-static int _find_mpu_port_index(struct omap_hwmod *oh)
+static int __init _find_mpu_port_index(struct omap_hwmod *oh)
 {
        int i;
        int found = 0;
@@ -585,7 +713,7 @@ static int _find_mpu_port_index(struct omap_hwmod *oh)
  * Return the virtual address of the base of the register target of
  * device @oh, or NULL on error.
  */
-static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index)
+static void __iomem * __init _find_mpu_rt_base(struct omap_hwmod *oh, u8 index)
 {
        struct omap_hwmod_ocp_if *os;
        struct omap_hwmod_addr_space *mem;
@@ -622,7 +750,7 @@ static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index)
 }
 
 /**
- * _sysc_enable - try to bring a module out of idle via OCP_SYSCONFIG
+ * _enable_sysc - try to bring a module out of idle via OCP_SYSCONFIG
  * @oh: struct omap_hwmod *
  *
  * If module is marked as SWSUP_SIDLE, force the module out of slave
@@ -630,7 +758,7 @@ static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index)
  * as SWSUP_MSUSPEND, force the module out of master standby;
  * otherwise, configure it for smart-standby.  No return value.
  */
-static void _sysc_enable(struct omap_hwmod *oh)
+static void _enable_sysc(struct omap_hwmod *oh)
 {
        u8 idlemode, sf;
        u32 v;
@@ -653,14 +781,6 @@ static void _sysc_enable(struct omap_hwmod *oh)
                _set_master_standbymode(oh, idlemode, &v);
        }
 
-       if (sf & SYSC_HAS_AUTOIDLE) {
-               idlemode = (oh->flags & HWMOD_NO_OCP_AUTOIDLE) ?
-                       0 : 1;
-               _set_module_autoidle(oh, idlemode, &v);
-       }
-
-       /* XXX OCP ENAWAKEUP bit? */
-
        /*
         * XXX The clock framework should handle this, by
         * calling into this code.  But this must wait until the
@@ -670,11 +790,26 @@ static void _sysc_enable(struct omap_hwmod *oh)
            (sf & SYSC_HAS_CLOCKACTIVITY))
                _set_clockactivity(oh, oh->class->sysc->clockact, &v);
 
+       /* If slave is in SMARTIDLE, also enable wakeup */
+       if ((sf & SYSC_HAS_SIDLEMODE) && !(oh->flags & HWMOD_SWSUP_SIDLE))
+               _enable_wakeup(oh, &v);
+
        _write_sysconfig(v, oh);
+
+       /*
+        * Set the autoidle bit only after setting the smartidle bit
+        * Setting this will not have any impact on the other modules.
+        */
+       if (sf & SYSC_HAS_AUTOIDLE) {
+               idlemode = (oh->flags & HWMOD_NO_OCP_AUTOIDLE) ?
+                       0 : 1;
+               _set_module_autoidle(oh, idlemode, &v);
+               _write_sysconfig(v, oh);
+       }
 }
 
 /**
- * _sysc_idle - try to put a module into idle via OCP_SYSCONFIG
+ * _idle_sysc - try to put a module into idle via OCP_SYSCONFIG
  * @oh: struct omap_hwmod *
  *
  * If module is marked as SWSUP_SIDLE, force the module into slave
@@ -682,7 +817,7 @@ static void _sysc_enable(struct omap_hwmod *oh)
  * as SWSUP_MSUSPEND, force the module into master standby; otherwise,
  * configure it for smart-standby.  No return value.
  */
-static void _sysc_idle(struct omap_hwmod *oh)
+static void _idle_sysc(struct omap_hwmod *oh)
 {
        u8 idlemode, sf;
        u32 v;
@@ -705,17 +840,21 @@ static void _sysc_idle(struct omap_hwmod *oh)
                _set_master_standbymode(oh, idlemode, &v);
        }
 
+       /* If slave is in SMARTIDLE, also enable wakeup */
+       if ((sf & SYSC_HAS_SIDLEMODE) && !(oh->flags & HWMOD_SWSUP_SIDLE))
+               _enable_wakeup(oh, &v);
+
        _write_sysconfig(v, oh);
 }
 
 /**
- * _sysc_shutdown - force a module into idle via OCP_SYSCONFIG
+ * _shutdown_sysc - force a module into idle via OCP_SYSCONFIG
  * @oh: struct omap_hwmod *
  *
  * Force the module into slave idle and master suspend. No return
  * value.
  */
-static void _sysc_shutdown(struct omap_hwmod *oh)
+static void _shutdown_sysc(struct omap_hwmod *oh)
 {
        u32 v;
        u8 sf;
@@ -743,7 +882,6 @@ static void _sysc_shutdown(struct omap_hwmod *oh)
  * @name: find an omap_hwmod by name
  *
  * Return a pointer to an omap_hwmod by name, or NULL if not found.
- * Caller must hold omap_hwmod_mutex.
  */
 static struct omap_hwmod *_lookup(const char *name)
 {
@@ -767,10 +905,10 @@ static struct omap_hwmod *_lookup(const char *name)
  * @data: not used; pass NULL
  *
  * Called by omap_hwmod_late_init() (after omap2_clk_init()).
- * Resolves all clock names embedded in the hwmod.  Must be called
- * with omap_hwmod_mutex held.  Returns -EINVAL if the omap_hwmod
- * has not yet been registered or if the clocks have already been
- * initialized, 0 on success, or a non-zero error on failure.
+ * Resolves all clock names embedded in the hwmod.  Returns -EINVAL if
+ * the omap_hwmod has not yet been registered or if the clocks have
+ * already been initialized, 0 on success, or a non-zero error on
+ * failure.
  */
 static int _init_clocks(struct omap_hwmod *oh, void *data)
 {
@@ -834,68 +972,241 @@ static int _wait_target_ready(struct omap_hwmod *oh)
 }
 
 /**
- * _reset - reset an omap_hwmod
+ * _lookup_hardreset - return the register bit shift for this hwmod/reset line
+ * @oh: struct omap_hwmod *
+ * @name: name of the reset line in the context of this hwmod
+ *
+ * Return the bit position of the reset line that match the
+ * input name. Return -ENOENT if not found.
+ */
+static u8 _lookup_hardreset(struct omap_hwmod *oh, const char *name)
+{
+       int i;
+
+       for (i = 0; i < oh->rst_lines_cnt; i++) {
+               const char *rst_line = oh->rst_lines[i].name;
+               if (!strcmp(rst_line, name)) {
+                       u8 shift = oh->rst_lines[i].rst_shift;
+                       pr_debug("omap_hwmod: %s: _lookup_hardreset: %s: %d\n",
+                                oh->name, rst_line, shift);
+
+                       return shift;
+               }
+       }
+
+       return -ENOENT;
+}
+
+/**
+ * _assert_hardreset - assert the HW reset line of submodules
+ * contained in the hwmod module.
+ * @oh: struct omap_hwmod *
+ * @name: name of the reset line to lookup and assert
+ *
+ * Some IP like dsp, ipu or iva contain processor that require
+ * an HW reset line to be assert / deassert in order to enable fully
+ * the IP.
+ */
+static int _assert_hardreset(struct omap_hwmod *oh, const char *name)
+{
+       u8 shift;
+
+       if (!oh)
+               return -EINVAL;
+
+       shift = _lookup_hardreset(oh, name);
+       if (IS_ERR_VALUE(shift))
+               return shift;
+
+       if (cpu_is_omap24xx() || cpu_is_omap34xx())
+               return omap2_prm_assert_hardreset(oh->prcm.omap2.module_offs,
+                                                 shift);
+       else if (cpu_is_omap44xx())
+               return omap4_prm_assert_hardreset(oh->prcm.omap4.rstctrl_reg,
+                                                 shift);
+       else
+               return -EINVAL;
+}
+
+/**
+ * _deassert_hardreset - deassert the HW reset line of submodules contained
+ * in the hwmod module.
+ * @oh: struct omap_hwmod *
+ * @name: name of the reset line to look up and deassert
+ *
+ * Some IP like dsp, ipu or iva contain processor that require
+ * an HW reset line to be assert / deassert in order to enable fully
+ * the IP.
+ */
+static int _deassert_hardreset(struct omap_hwmod *oh, const char *name)
+{
+       u8 shift;
+       int r;
+
+       if (!oh)
+               return -EINVAL;
+
+       shift = _lookup_hardreset(oh, name);
+       if (IS_ERR_VALUE(shift))
+               return shift;
+
+       if (cpu_is_omap24xx() || cpu_is_omap34xx())
+               r = omap2_prm_deassert_hardreset(oh->prcm.omap2.module_offs,
+                                                shift);
+       else if (cpu_is_omap44xx())
+               r = omap4_prm_deassert_hardreset(oh->prcm.omap4.rstctrl_reg,
+                                                shift);
+       else
+               return -EINVAL;
+
+       if (r == -EBUSY)
+               pr_warning("omap_hwmod: %s: failed to hardreset\n", oh->name);
+
+       return r;
+}
+
+/**
+ * _read_hardreset - read the HW reset line state of submodules
+ * contained in the hwmod module
+ * @oh: struct omap_hwmod *
+ * @name: name of the reset line to look up and read
+ *
+ * Return the state of the reset line.
+ */
+static int _read_hardreset(struct omap_hwmod *oh, const char *name)
+{
+       u8 shift;
+
+       if (!oh)
+               return -EINVAL;
+
+       shift = _lookup_hardreset(oh, name);
+       if (IS_ERR_VALUE(shift))
+               return shift;
+
+       if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
+               return omap2_prm_is_hardreset_asserted(oh->prcm.omap2.module_offs,
+                                                      shift);
+       } else if (cpu_is_omap44xx()) {
+               return omap4_prm_is_hardreset_asserted(oh->prcm.omap4.rstctrl_reg,
+                                                      shift);
+       } else {
+               return -EINVAL;
+       }
+}
+
+/**
+ * _ocp_softreset - reset an omap_hwmod via the OCP_SYSCONFIG bit
  * @oh: struct omap_hwmod *
  *
  * Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit.  hwmod must be
- * enabled for this to work.  Must be called with omap_hwmod_mutex
- * held.  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 -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.
+ *
+ * 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
+ * use the SYSCONFIG softreset bit to provide the status.
+ *
+ * Note that some IP like McBSP do have reset control but don't have
+ * reset status.
  */
-static int _reset(struct omap_hwmod *oh)
+static int _ocp_softreset(struct omap_hwmod *oh)
 {
-       u32 r, v;
+       u32 v;
        int c = 0;
+       int ret = 0;
 
        if (!oh->class->sysc ||
-           !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET) ||
-           (oh->class->sysc->sysc_flags & SYSS_MISSING))
+           !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET))
                return -EINVAL;
 
        /* clocks must be on for this operation */
        if (oh->_state != _HWMOD_STATE_ENABLED) {
-               WARN(1, "omap_hwmod: %s: reset can only be entered from "
-                    "enabled state\n", oh->name);
+               pr_warning("omap_hwmod: %s: reset can only be entered from "
+                          "enabled state\n", oh->name);
                return -EINVAL;
        }
 
-       pr_debug("omap_hwmod: %s: resetting\n", oh->name);
+       /* For some modules, all optionnal clocks need to be enabled as well */
+       if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
+               _enable_optional_clocks(oh);
+
+       pr_debug("omap_hwmod: %s: resetting via OCP SOFTRESET\n", oh->name);
 
        v = oh->_sysc_cache;
-       r = _set_softreset(oh, &v);
-       if (r)
-               return r;
+       ret = _set_softreset(oh, &v);
+       if (ret)
+               goto dis_opt_clks;
        _write_sysconfig(v, oh);
 
-       omap_test_timeout((omap_hwmod_readl(oh, oh->class->sysc->syss_offs) &
-                          SYSS_RESETDONE_MASK),
-                         MAX_MODULE_RESET_WAIT, c);
-
-       if (c == MAX_MODULE_RESET_WAIT)
-               WARN(1, "omap_hwmod: %s: failed to reset in %d usec\n",
-                    oh->name, MAX_MODULE_RESET_WAIT);
+       if (oh->class->sysc->sysc_flags & SYSS_HAS_RESET_STATUS)
+               omap_test_timeout((omap_hwmod_read(oh,
+                                                   oh->class->sysc->syss_offs)
+                                  & SYSS_RESETDONE_MASK),
+                                 MAX_MODULE_SOFTRESET_WAIT, c);
+       else if (oh->class->sysc->sysc_flags & SYSC_HAS_RESET_STATUS)
+               omap_test_timeout(!(omap_hwmod_read(oh,
+                                                    oh->class->sysc->sysc_offs)
+                                  & SYSC_TYPE2_SOFTRESET_MASK),
+                                 MAX_MODULE_SOFTRESET_WAIT, c);
+
+       if (c == MAX_MODULE_SOFTRESET_WAIT)
+               pr_warning("omap_hwmod: %s: softreset failed (waited %d usec)\n",
+                          oh->name, MAX_MODULE_SOFTRESET_WAIT);
        else
-               pr_debug("omap_hwmod: %s: reset in %d usec\n", oh->name, c);
+               pr_debug("omap_hwmod: %s: softreset in %d usec\n", oh->name, c);
 
        /*
         * XXX add _HWMOD_STATE_WEDGED for modules that don't come back from
         * _wait_target_ready() or _reset()
         */
 
-       return (c == MAX_MODULE_RESET_WAIT) ? -ETIMEDOUT : 0;
+       ret = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0;
+
+dis_opt_clks:
+       if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
+               _disable_optional_clocks(oh);
+
+       return ret;
 }
 
 /**
- * _omap_hwmod_enable - enable an omap_hwmod
+ * _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.
+ */
+static int _reset(struct omap_hwmod *oh)
+{
+       int ret;
+
+       pr_debug("omap_hwmod: %s: resetting\n", oh->name);
+
+       ret = (oh->class->reset) ? oh->class->reset(oh) : _ocp_softreset(oh);
+
+       return ret;
+}
+
+/**
+ * _enable - enable an omap_hwmod
  * @oh: struct omap_hwmod *
  *
  * Enables an omap_hwmod @oh such that the MPU can access the hwmod's
- * register target.  Must be called with omap_hwmod_mutex held.
- * Returns -EINVAL if the hwmod is in the wrong state or passes along
- * the return value of _wait_target_ready().
+ * register target.  Returns -EINVAL if the hwmod is in the wrong
+ * state or passes along the return value of _wait_target_ready().
  */
-int _omap_hwmod_enable(struct omap_hwmod *oh)
+static int _enable(struct omap_hwmod *oh)
 {
        int r;
 
@@ -909,7 +1220,18 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
 
        pr_debug("omap_hwmod: %s: enabling\n", oh->name);
 
-       /* XXX mux balls */
+       /*
+        * If an IP contains only one HW reset line, then de-assert it in order
+        * to allow to enable the clocks. Otherwise the PRCM will return
+        * Intransition status, and the init will failed.
+        */
+       if ((oh->_state == _HWMOD_STATE_INITIALIZED ||
+            oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1)
+               _deassert_hardreset(oh, oh->rst_lines[0].name);
+
+       /* Mux pins for device runtime if populated */
+       if (oh->mux)
+               omap_hwmod_mux(oh->mux, _HWMOD_STATE_ENABLED);
 
        _add_initiator_dep(oh, mpu_oh);
        _enable_clocks(oh);
@@ -922,9 +1244,10 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
                if (oh->class->sysc) {
                        if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
                                _update_sysc_cache(oh);
-                       _sysc_enable(oh);
+                       _enable_sysc(oh);
                }
        } else {
+               _disable_clocks(oh);
                pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
                         oh->name, r);
        }
@@ -940,7 +1263,7 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
  * no further work.  Returns -EINVAL if the hwmod is in the wrong
  * state or returns 0.
  */
-int _omap_hwmod_idle(struct omap_hwmod *oh)
+static int _idle(struct omap_hwmod *oh)
 {
        if (oh->_state != _HWMOD_STATE_ENABLED) {
                WARN(1, "omap_hwmod: %s: idle state can only be entered from "
@@ -951,10 +1274,14 @@ int _omap_hwmod_idle(struct omap_hwmod *oh)
        pr_debug("omap_hwmod: %s: idling\n", oh->name);
 
        if (oh->class->sysc)
-               _sysc_idle(oh);
+               _idle_sysc(oh);
        _del_initiator_dep(oh, mpu_oh);
        _disable_clocks(oh);
 
+       /* Mux pins for device idle if populated */
+       if (oh->mux)
+               omap_hwmod_mux(oh->mux, _HWMOD_STATE_IDLE);
+
        oh->_state = _HWMOD_STATE_IDLE;
 
        return 0;
@@ -971,6 +1298,9 @@ int _omap_hwmod_idle(struct omap_hwmod *oh)
  */
 static int _shutdown(struct omap_hwmod *oh)
 {
+       int ret;
+       u8 prev_state;
+
        if (oh->_state != _HWMOD_STATE_IDLE &&
            oh->_state != _HWMOD_STATE_ENABLED) {
                WARN(1, "omap_hwmod: %s: disabled state can only be entered "
@@ -980,14 +1310,39 @@ static int _shutdown(struct omap_hwmod *oh)
 
        pr_debug("omap_hwmod: %s: disabling\n", oh->name);
 
+       if (oh->class->pre_shutdown) {
+               prev_state = oh->_state;
+               if (oh->_state == _HWMOD_STATE_IDLE)
+                       _enable(oh);
+               ret = oh->class->pre_shutdown(oh);
+               if (ret) {
+                       if (prev_state == _HWMOD_STATE_IDLE)
+                               _idle(oh);
+                       return ret;
+               }
+       }
+
        if (oh->class->sysc)
-               _sysc_shutdown(oh);
-       _del_initiator_dep(oh, mpu_oh);
-       /* XXX what about the other system initiators here? DMA, tesla, d2d */
-       _disable_clocks(oh);
+               _shutdown_sysc(oh);
+
+       /*
+        * If an IP contains only one HW reset line, then assert it
+        * before disabling the clocks and shutting down the IP.
+        */
+       if (oh->rst_lines_cnt == 1)
+               _assert_hardreset(oh, oh->rst_lines[0].name);
+
+       /* clocks and deps are already disabled in idle */
+       if (oh->_state == _HWMOD_STATE_ENABLED) {
+               _del_initiator_dep(oh, mpu_oh);
+               /* XXX what about the other system initiators here? dma, dsp */
+               _disable_clocks(oh);
+       }
        /* XXX Should this code also force-disable the optional clocks? */
 
-       /* XXX mux any associated balls to safe mode */
+       /* Mux pins to safe mode or use populated off mode values */
+       if (oh->mux)
+               omap_hwmod_mux(oh->mux, _HWMOD_STATE_DISABLED);
 
        oh->_state = _HWMOD_STATE_DISABLED;
 
@@ -997,24 +1352,15 @@ static int _shutdown(struct omap_hwmod *oh)
 /**
  * _setup - do initial configuration of omap_hwmod
  * @oh: struct omap_hwmod *
- * @skip_setup_idle_p: do not idle hwmods at the end of the fn if 1
  *
  * Writes the CLOCKACTIVITY bits @clockact to the hwmod @oh
- * OCP_SYSCONFIG register.  Must be called with omap_hwmod_mutex held.
- * @skip_setup_idle is intended to be used on a system that will not
- * call omap_hwmod_enable() to enable devices (e.g., a system without
- * PM runtime).  Returns -EINVAL if the hwmod is in the wrong state or
- * returns 0.
+ * OCP_SYSCONFIG register.  Returns -EINVAL if the hwmod is in the
+ * wrong state or returns 0.
  */
 static int _setup(struct omap_hwmod *oh, void *data)
 {
        int i, r;
-       u8 skip_setup_idle;
-
-       if (!oh || !data)
-               return -EINVAL;
-
-       skip_setup_idle = *(u8 *)data;
+       u8 postsetup_state;
 
        /* Set iclk autoidle mode */
        if (oh->slaves_cnt > 0) {
@@ -1036,7 +1382,17 @@ static int _setup(struct omap_hwmod *oh, void *data)
 
        oh->_state = _HWMOD_STATE_INITIALIZED;
 
-       r = _omap_hwmod_enable(oh);
+       /*
+        * In the case of hwmod with hardreset that should not be
+        * de-assert at boot time, we have to keep the module
+        * initialized, because we cannot enable it properly with the
+        * reset asserted. Exit without warning because that behavior is
+        * expected.
+        */
+       if ((oh->flags & HWMOD_INIT_NO_RESET) && oh->rst_lines_cnt == 1)
+               return 0;
+
+       r = _enable(oh);
        if (r) {
                pr_warning("omap_hwmod: %s: cannot be enabled (%d)\n",
                           oh->name, oh->_state);
@@ -1044,73 +1400,44 @@ static int _setup(struct omap_hwmod *oh, void *data)
        }
 
        if (!(oh->flags & HWMOD_INIT_NO_RESET)) {
+               _reset(oh);
+
                /*
-                * XXX Do the OCP_SYSCONFIG bits need to be
-                * reprogrammed after a reset?  If not, then this can
-                * be removed.  If they do, then probably the
-                * _omap_hwmod_enable() function should be split to avoid the
-                * rewrite of the OCP_SYSCONFIG register.
+                * 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.
                 */
                if (oh->class->sysc) {
                        _update_sysc_cache(oh);
-                       _sysc_enable(oh);
+                       _enable_sysc(oh);
                }
        }
 
-       if (!(oh->flags & HWMOD_INIT_NO_IDLE) && !skip_setup_idle)
-               _omap_hwmod_idle(oh);
-
-       return 0;
-}
-
-
-
-/* Public functions */
-
-u32 omap_hwmod_readl(struct omap_hwmod *oh, u16 reg_offs)
-{
-       return __raw_readl(oh->_mpu_rt_va + reg_offs);
-}
-
-void omap_hwmod_writel(u32 v, struct omap_hwmod *oh, u16 reg_offs)
-{
-       __raw_writel(v, oh->_mpu_rt_va + reg_offs);
-}
+       postsetup_state = oh->_postsetup_state;
+       if (postsetup_state == _HWMOD_STATE_UNKNOWN)
+               postsetup_state = _HWMOD_STATE_ENABLED;
 
-/**
- * omap_hwmod_set_slave_idlemode - set the hwmod's OCP slave idlemode
- * @oh: struct omap_hwmod *
- * @idlemode: SIDLEMODE field bits (shifted to bit 0)
- *
- * Sets the IP block's OCP slave idlemode in hardware, and updates our
- * local copy.  Intended to be used by drivers that have some erratum
- * that requires direct manipulation of the SIDLEMODE bits.  Returns
- * -EINVAL if @oh is null, or passes along the return value from
- * _set_slave_idlemode().
- *
- * XXX Does this function have any current users?  If not, we should
- * remove it; it is better to let the rest of the hwmod code handle this.
- * Any users of this function should be scrutinized carefully.
- */
-int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode)
-{
-       u32 v;
-       int retval = 0;
-
-       if (!oh)
-               return -EINVAL;
-
-       v = oh->_sysc_cache;
-
-       retval = _set_slave_idlemode(oh, idlemode, &v);
-       if (!retval)
-               _write_sysconfig(v, oh);
+       /*
+        * XXX HWMOD_INIT_NO_IDLE does not belong in hwmod data -
+        * it should be set by the core code as a runtime flag during startup
+        */
+       if ((oh->flags & HWMOD_INIT_NO_IDLE) &&
+           (postsetup_state == _HWMOD_STATE_IDLE))
+               postsetup_state = _HWMOD_STATE_ENABLED;
+
+       if (postsetup_state == _HWMOD_STATE_IDLE)
+               _idle(oh);
+       else if (postsetup_state == _HWMOD_STATE_DISABLED)
+               _shutdown(oh);
+       else if (postsetup_state != _HWMOD_STATE_ENABLED)
+               WARN(1, "hwmod: %s: unknown postsetup state %d! defaulting to enabled\n",
+                    oh->name, postsetup_state);
 
-       return retval;
+       return 0;
 }
 
 /**
- * omap_hwmod_register - register a struct omap_hwmod
+ * _register - register a struct omap_hwmod
  * @oh: struct omap_hwmod *
  *
  * Registers the omap_hwmod @oh.  Returns -EEXIST if an omap_hwmod
@@ -1126,7 +1453,7 @@ int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode)
  * that the copy process would be relatively complex due to the large number
  * of substructures.
  */
-int omap_hwmod_register(struct omap_hwmod *oh)
+static int __init _register(struct omap_hwmod *oh)
 {
        int ret, ms_id;
 
@@ -1134,14 +1461,10 @@ int omap_hwmod_register(struct omap_hwmod *oh)
            (oh->_state != _HWMOD_STATE_UNKNOWN))
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
-
        pr_debug("omap_hwmod: %s: registering\n", oh->name);
 
-       if (_lookup(oh->name)) {
-               ret = -EEXIST;
-               goto ohr_unlock;
-       }
+       if (_lookup(oh->name))
+               return -EEXIST;
 
        ms_id = _find_mpu_port_index(oh);
        if (!IS_ERR_VALUE(ms_id)) {
@@ -1153,15 +1476,66 @@ int omap_hwmod_register(struct omap_hwmod *oh)
 
        list_add_tail(&oh->node, &omap_hwmod_list);
 
+       spin_lock_init(&oh->_lock);
+
        oh->_state = _HWMOD_STATE_REGISTERED;
 
        ret = 0;
 
-ohr_unlock:
-       mutex_unlock(&omap_hwmod_mutex);
        return ret;
 }
 
+
+/* Public functions */
+
+u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs)
+{
+       if (oh->flags & HWMOD_16BIT_REG)
+               return __raw_readw(oh->_mpu_rt_va + reg_offs);
+       else
+               return __raw_readl(oh->_mpu_rt_va + reg_offs);
+}
+
+void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs)
+{
+       if (oh->flags & HWMOD_16BIT_REG)
+               __raw_writew(v, oh->_mpu_rt_va + reg_offs);
+       else
+               __raw_writel(v, oh->_mpu_rt_va + reg_offs);
+}
+
+/**
+ * omap_hwmod_set_slave_idlemode - set the hwmod's OCP slave idlemode
+ * @oh: struct omap_hwmod *
+ * @idlemode: SIDLEMODE field bits (shifted to bit 0)
+ *
+ * Sets the IP block's OCP slave idlemode in hardware, and updates our
+ * local copy.  Intended to be used by drivers that have some erratum
+ * that requires direct manipulation of the SIDLEMODE bits.  Returns
+ * -EINVAL if @oh is null, or passes along the return value from
+ * _set_slave_idlemode().
+ *
+ * XXX Does this function have any current users?  If not, we should
+ * remove it; it is better to let the rest of the hwmod code handle this.
+ * Any users of this function should be scrutinized carefully.
+ */
+int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode)
+{
+       u32 v;
+       int retval = 0;
+
+       if (!oh)
+               return -EINVAL;
+
+       v = oh->_sysc_cache;
+
+       retval = _set_slave_idlemode(oh, idlemode, &v);
+       if (!retval)
+               _write_sysconfig(v, oh);
+
+       return retval;
+}
+
 /**
  * omap_hwmod_lookup - look up a registered omap_hwmod by name
  * @name: name of the omap_hwmod to look up
@@ -1176,9 +1550,7 @@ struct omap_hwmod *omap_hwmod_lookup(const char *name)
        if (!name)
                return NULL;
 
-       mutex_lock(&omap_hwmod_mutex);
        oh = _lookup(name);
-       mutex_unlock(&omap_hwmod_mutex);
 
        return oh;
 }
@@ -1204,13 +1576,11 @@ int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data),
        if (!fn)
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
        list_for_each_entry(temp_oh, &omap_hwmod_list, node) {
                ret = (*fn)(temp_oh, data);
                if (ret)
                        break;
        }
-       mutex_unlock(&omap_hwmod_mutex);
 
        return ret;
 }
@@ -1225,7 +1595,7 @@ int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data),
  * listed in @ohs that are valid for this chip.  Returns -EINVAL if
  * omap_hwmod_init() has already been called or 0 otherwise.
  */
-int omap_hwmod_init(struct omap_hwmod **ohs)
+int __init omap_hwmod_init(struct omap_hwmod **ohs)
 {
        struct omap_hwmod *oh;
        int r;
@@ -1241,8 +1611,8 @@ int omap_hwmod_init(struct omap_hwmod **ohs)
        oh = *ohs;
        while (oh) {
                if (omap_chip_is(oh->omap_chip)) {
-                       r = omap_hwmod_register(oh);
-                       WARN(r, "omap_hwmod: %s: omap_hwmod_register returned "
+                       r = _register(oh);
+                       WARN(r, "omap_hwmod: %s: _register returned "
                             "%d\n", oh->name, r);
                }
                oh = *++ohs;
@@ -1253,13 +1623,12 @@ int omap_hwmod_init(struct omap_hwmod **ohs)
 
 /**
  * omap_hwmod_late_init - do some post-clock framework initialization
- * @skip_setup_idle: if 1, do not idle hwmods in _setup()
  *
  * Must be called after omap2_clk_init().  Resolves the struct clk names
  * to struct clk pointers for each registered omap_hwmod.  Also calls
  * _setup() on each hwmod.  Returns 0.
  */
-int omap_hwmod_late_init(u8 skip_setup_idle)
+int omap_hwmod_late_init(void)
 {
        int r;
 
@@ -1271,36 +1640,7 @@ int omap_hwmod_late_init(u8 skip_setup_idle)
        WARN(!mpu_oh, "omap_hwmod: could not find MPU initiator hwmod %s\n",
             MPU_INITIATOR_NAME);
 
-       if (skip_setup_idle)
-               pr_debug("omap_hwmod: will leave hwmods enabled during setup\n");
-
-       omap_hwmod_for_each(_setup, &skip_setup_idle);
-
-       return 0;
-}
-
-/**
- * omap_hwmod_unregister - unregister an omap_hwmod
- * @oh: struct omap_hwmod *
- *
- * Unregisters a previously-registered omap_hwmod @oh.  There's probably
- * no use case for this, so it is likely to be removed in a later version.
- *
- * XXX Free all of the bootmem-allocated structures here when that is
- * implemented.  Make it clear that core code is the only code that is
- * expected to unregister modules.
- */
-int omap_hwmod_unregister(struct omap_hwmod *oh)
-{
-       if (!oh)
-               return -EINVAL;
-
-       pr_debug("omap_hwmod: %s: unregistering\n", oh->name);
-
-       mutex_lock(&omap_hwmod_mutex);
-       iounmap(oh->_mpu_rt_va);
-       list_del(&oh->node);
-       mutex_unlock(&omap_hwmod_mutex);
+       omap_hwmod_for_each(_setup, NULL);
 
        return 0;
 }
@@ -1309,39 +1649,41 @@ int omap_hwmod_unregister(struct omap_hwmod *oh)
  * omap_hwmod_enable - enable an omap_hwmod
  * @oh: struct omap_hwmod *
  *
- * Enable an omap_hwomd @oh.  Intended to be called by omap_device_enable().
+ * Enable an omap_hwmod @oh.  Intended to be called by omap_device_enable().
  * Returns -EINVAL on error or passes along the return value from _enable().
  */
 int omap_hwmod_enable(struct omap_hwmod *oh)
 {
        int r;
+       unsigned long flags;
 
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
-       r = _omap_hwmod_enable(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
+       r = _enable(oh);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return r;
 }
 
-
 /**
  * omap_hwmod_idle - idle an omap_hwmod
  * @oh: struct omap_hwmod *
  *
- * Idle an omap_hwomd @oh.  Intended to be called by omap_device_idle().
+ * Idle an omap_hwmod @oh.  Intended to be called by omap_device_idle().
  * Returns -EINVAL on error or passes along the return value from _idle().
  */
 int omap_hwmod_idle(struct omap_hwmod *oh)
 {
+       unsigned long flags;
+
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
-       _omap_hwmod_idle(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
+       _idle(oh);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1350,18 +1692,20 @@ int omap_hwmod_idle(struct omap_hwmod *oh)
  * omap_hwmod_shutdown - shutdown an omap_hwmod
  * @oh: struct omap_hwmod *
  *
- * Shutdown an omap_hwomd @oh.  Intended to be called by
+ * Shutdown an omap_hwmod @oh.  Intended to be called by
  * omap_device_shutdown().  Returns -EINVAL on error or passes along
  * the return value from _shutdown().
  */
 int omap_hwmod_shutdown(struct omap_hwmod *oh)
 {
+       unsigned long flags;
+
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
        _shutdown(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1374,9 +1718,11 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh)
  */
 int omap_hwmod_enable_clocks(struct omap_hwmod *oh)
 {
-       mutex_lock(&omap_hwmod_mutex);
+       unsigned long flags;
+
+       spin_lock_irqsave(&oh->_lock, flags);
        _enable_clocks(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1389,9 +1735,11 @@ int omap_hwmod_enable_clocks(struct omap_hwmod *oh)
  */
 int omap_hwmod_disable_clocks(struct omap_hwmod *oh)
 {
-       mutex_lock(&omap_hwmod_mutex);
+       unsigned long flags;
+
+       spin_lock_irqsave(&oh->_lock, flags);
        _disable_clocks(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1421,7 +1769,7 @@ void omap_hwmod_ocp_barrier(struct omap_hwmod *oh)
         * Forces posted writes to complete on the OCP thread handling
         * register writes
         */
-       omap_hwmod_readl(oh, oh->class->sysc->sysc_offs);
+       omap_hwmod_read(oh, oh->class->sysc->sysc_offs);
 }
 
 /**
@@ -1430,20 +1778,19 @@ void omap_hwmod_ocp_barrier(struct omap_hwmod *oh)
  *
  * Under some conditions, a driver may wish to reset the entire device.
  * Called from omap_device code.  Returns -EINVAL on error or passes along
- * the return value from _reset()/_enable().
+ * the return value from _reset().
  */
 int omap_hwmod_reset(struct omap_hwmod *oh)
 {
        int r;
+       unsigned long flags;
 
-       if (!oh || !(oh->_state & _HWMOD_STATE_ENABLED))
+       if (!oh)
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
        r = _reset(oh);
-       if (!r)
-               r = _omap_hwmod_enable(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return r;
 }
@@ -1468,7 +1815,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh)
 {
        int ret, i;
 
-       ret = oh->mpu_irqs_cnt + oh->sdma_chs_cnt;
+       ret = oh->mpu_irqs_cnt + oh->sdma_reqs_cnt;
 
        for (i = 0; i < oh->slaves_cnt; i++)
                ret += oh->slaves[i]->addr_cnt;
@@ -1501,10 +1848,10 @@ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res)
                r++;
        }
 
-       for (i = 0; i < oh->sdma_chs_cnt; i++) {
-               (res + r)->name = (oh->sdma_chs + i)->name;
-               (res + r)->start = (oh->sdma_chs + i)->dma_ch;
-               (res + r)->end = (oh->sdma_chs + i)->dma_ch;
+       for (i = 0; i < oh->sdma_reqs_cnt; i++) {
+               (res + r)->name = (oh->sdma_reqs + i)->name;
+               (res + r)->start = (oh->sdma_reqs + i)->dma_req;
+               (res + r)->end = (oh->sdma_reqs + i)->dma_req;
                (res + r)->flags = IORESOURCE_DMA;
                r++;
        }
@@ -1640,13 +1987,18 @@ int omap_hwmod_del_initiator_dep(struct omap_hwmod *oh,
  */
 int omap_hwmod_enable_wakeup(struct omap_hwmod *oh)
 {
+       unsigned long flags;
+       u32 v;
+
        if (!oh->class->sysc ||
            !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
-       _enable_wakeup(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
+       v = oh->_sysc_cache;
+       _enable_wakeup(oh, &v);
+       _write_sysconfig(v, oh);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1665,26 +2017,111 @@ int omap_hwmod_enable_wakeup(struct omap_hwmod *oh)
  */
 int omap_hwmod_disable_wakeup(struct omap_hwmod *oh)
 {
+       unsigned long flags;
+       u32 v;
+
        if (!oh->class->sysc ||
            !(oh->class->sysc->sysc_flags & SYSC_HAS_ENAWAKEUP))
                return -EINVAL;
 
-       mutex_lock(&omap_hwmod_mutex);
-       _disable_wakeup(oh);
-       mutex_unlock(&omap_hwmod_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
+       v = oh->_sysc_cache;
+       _disable_wakeup(oh, &v);
+       _write_sysconfig(v, oh);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
 
+/**
+ * omap_hwmod_assert_hardreset - assert the HW reset line of submodules
+ * contained in the hwmod module.
+ * @oh: struct omap_hwmod *
+ * @name: name of the reset line to lookup and assert
+ *
+ * Some IP like dsp, ipu or iva contain processor that require
+ * an HW reset line to be assert / deassert in order to enable fully
+ * the IP.  Returns -EINVAL if @oh is null or if the operation is not
+ * yet supported on this OMAP; otherwise, passes along the return value
+ * from _assert_hardreset().
+ */
+int omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name)
+{
+       int ret;
+       unsigned long flags;
+
+       if (!oh)
+               return -EINVAL;
+
+       spin_lock_irqsave(&oh->_lock, flags);
+       ret = _assert_hardreset(oh, name);
+       spin_unlock_irqrestore(&oh->_lock, flags);
+
+       return ret;
+}
+
+/**
+ * omap_hwmod_deassert_hardreset - deassert the HW reset line of submodules
+ * contained in the hwmod module.
+ * @oh: struct omap_hwmod *
+ * @name: name of the reset line to look up and deassert
+ *
+ * Some IP like dsp, ipu or iva contain processor that require
+ * an HW reset line to be assert / deassert in order to enable fully
+ * the IP.  Returns -EINVAL if @oh is null or if the operation is not
+ * yet supported on this OMAP; otherwise, passes along the return value
+ * from _deassert_hardreset().
+ */
+int omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name)
+{
+       int ret;
+       unsigned long flags;
+
+       if (!oh)
+               return -EINVAL;
+
+       spin_lock_irqsave(&oh->_lock, flags);
+       ret = _deassert_hardreset(oh, name);
+       spin_unlock_irqrestore(&oh->_lock, flags);
+
+       return ret;
+}
+
+/**
+ * omap_hwmod_read_hardreset - read the HW reset line state of submodules
+ * contained in the hwmod module
+ * @oh: struct omap_hwmod *
+ * @name: name of the reset line to look up and read
+ *
+ * Return the current state of the hwmod @oh's reset line named @name:
+ * returns -EINVAL upon parameter error or if this operation
+ * is unsupported on the current OMAP; otherwise, passes along the return
+ * value from _read_hardreset().
+ */
+int omap_hwmod_read_hardreset(struct omap_hwmod *oh, const char *name)
+{
+       int ret;
+       unsigned long flags;
+
+       if (!oh)
+               return -EINVAL;
+
+       spin_lock_irqsave(&oh->_lock, flags);
+       ret = _read_hardreset(oh, name);
+       spin_unlock_irqrestore(&oh->_lock, flags);
+
+       return ret;
+}
+
+
 /**
  * omap_hwmod_for_each_by_class - call @fn for each hwmod of class @classname
  * @classname: struct omap_hwmod_class name to search for
  * @fn: callback function pointer to call for each hwmod in class @classname
  * @user: arbitrary context data to pass to the callback function
  *
- * For each omap_hwmod of class @classname, call @fn.  Takes
- * omap_hwmod_mutex to prevent the hwmod list from changing during the
- * iteration.  If the callback function returns something other than
+ * For each omap_hwmod of class @classname, call @fn.
+ * If the callback function returns something other than
  * zero, the iterator is terminated, and the callback function's return
  * value is passed back to the caller.  Returns 0 upon success, -EINVAL
  * if @classname or @fn are NULL, or passes back the error code from @fn.
@@ -1703,8 +2140,6 @@ int omap_hwmod_for_each_by_class(const char *classname,
        pr_debug("omap_hwmod: %s: looking for modules of class %s\n",
                 __func__, classname);
 
-       mutex_lock(&omap_hwmod_mutex);
-
        list_for_each_entry(temp_oh, &omap_hwmod_list, node) {
                if (!strcmp(temp_oh->class->name, classname)) {
                        pr_debug("omap_hwmod: %s: %s: calling callback fn\n",
@@ -1715,8 +2150,6 @@ int omap_hwmod_for_each_by_class(const char *classname,
                }
        }
 
-       mutex_unlock(&omap_hwmod_mutex);
-
        if (ret)
                pr_debug("omap_hwmod: %s: iterator terminated early: %d\n",
                         __func__, ret);
@@ -1724,3 +2157,64 @@ int omap_hwmod_for_each_by_class(const char *classname,
        return ret;
 }
 
+/**
+ * omap_hwmod_set_postsetup_state - set the post-_setup() state for this hwmod
+ * @oh: struct omap_hwmod *
+ * @state: state that _setup() should leave the hwmod in
+ *
+ * Sets the hwmod state that @oh will enter at the end of _setup() (called by
+ * omap_hwmod_late_init()).  Only valid to call between calls to
+ * omap_hwmod_init() and omap_hwmod_late_init().  Returns 0 upon success or
+ * -EINVAL if there is a problem with the arguments or if the hwmod is
+ * in the wrong state.
+ */
+int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state)
+{
+       int ret;
+       unsigned long flags;
+
+       if (!oh)
+               return -EINVAL;
+
+       if (state != _HWMOD_STATE_DISABLED &&
+           state != _HWMOD_STATE_ENABLED &&
+           state != _HWMOD_STATE_IDLE)
+               return -EINVAL;
+
+       spin_lock_irqsave(&oh->_lock, flags);
+
+       if (oh->_state != _HWMOD_STATE_REGISTERED) {
+               ret = -EINVAL;
+               goto ohsps_unlock;
+       }
+
+       oh->_postsetup_state = state;
+       ret = 0;
+
+ohsps_unlock:
+       spin_unlock_irqrestore(&oh->_lock, flags);
+
+       return ret;
+}
+
+/**
+ * omap_hwmod_get_context_loss_count - get lost context count
+ * @oh: struct omap_hwmod *
+ *
+ * Query the powerdomain of of @oh to get the context loss
+ * count for this device.
+ *
+ * Returns the context loss count of the powerdomain assocated with @oh
+ * upon success, or zero if no powerdomain exists for @oh.
+ */
+u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh)
+{
+       struct powerdomain *pwrdm;
+       int ret = 0;
+
+       pwrdm = omap_hwmod_get_pwrdm(oh);
+       if (pwrdm)
+               ret = pwrdm_get_context_loss_count(pwrdm);
+
+       return ret;
+}