]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/arm/mach-omap2/omap_hwmod.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / arch / arm / mach-omap2 / omap_hwmod.c
index 5a30658444d0f84d8a8c8bbb7553ee9db07a5959..e282e35769fd7efe383e4be33116d8e8ca492e91 100644 (file)
  * - 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 "prm.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 softreset */
 #define MAX_MODULE_SOFTRESET_WAIT      10000
 /* 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;
 
@@ -209,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_write(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);
 }
 
 /**
@@ -388,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) {
@@ -403,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 */
 
@@ -421,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) {
@@ -436,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 */
 
@@ -675,7 +679,7 @@ static void _disable_optional_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;
@@ -709,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;
@@ -786,11 +790,11 @@ static void _enable_sysc(struct omap_hwmod *oh)
            (sf & SYSC_HAS_CLOCKACTIVITY))
                _set_clockactivity(oh, oh->class->sysc->clockact, &v);
 
-       _write_sysconfig(v, oh);
-
        /* If slave is in SMARTIDLE, also enable wakeup */
        if ((sf & SYSC_HAS_SIDLEMODE) && !(oh->flags & HWMOD_SWSUP_SIDLE))
-               _enable_wakeup(oh);
+               _enable_wakeup(oh, &v);
+
+       _write_sysconfig(v, oh);
 
        /*
         * Set the autoidle bit only after setting the smartidle bit
@@ -836,6 +840,10 @@ static void _idle_sysc(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);
 }
 
@@ -874,7 +882,6 @@ static void _shutdown_sysc(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)
 {
@@ -1089,7 +1096,7 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
 }
 
 /**
- * _reset - reset an omap_hwmod
+ * _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
@@ -1098,12 +1105,13 @@ static int _read_hardreset(struct omap_hwmod *oh, const char *name)
  * 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 does not have SYSSTATUS register and instead
+ * 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 does have a reset control but no reset 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 v;
        int c = 0;
@@ -1124,7 +1132,7 @@ static int _reset(struct omap_hwmod *oh)
        if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)
                _enable_optional_clocks(oh);
 
-       pr_debug("omap_hwmod: %s: resetting\n", oh->name);
+       pr_debug("omap_hwmod: %s: resetting via OCP SOFTRESET\n", oh->name);
 
        v = oh->_sysc_cache;
        ret = _set_softreset(oh, &v);
@@ -1164,17 +1172,41 @@ dis_opt_clks:
 }
 
 /**
- * _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.  (This function has a full name --
- * _omap_hwmod_enable() rather than simply _enable() -- because it is
- * currently required by the pm34xx.c idle loop.)  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;
 
@@ -1197,7 +1229,9 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
             oh->_state == _HWMOD_STATE_DISABLED) && oh->rst_lines_cnt == 1)
                _deassert_hardreset(oh, oh->rst_lines[0].name);
 
-       /* XXX mux balls */
+       /* 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);
@@ -1213,6 +1247,7 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
                        _enable_sysc(oh);
                }
        } else {
+               _disable_clocks(oh);
                pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
                         oh->name, r);
        }
@@ -1221,16 +1256,14 @@ int _omap_hwmod_enable(struct omap_hwmod *oh)
 }
 
 /**
- * _omap_hwmod_idle - idle an omap_hwmod
+ * _idle - idle an omap_hwmod
  * @oh: struct omap_hwmod *
  *
  * Idles an omap_hwmod @oh.  This should be called once the hwmod has
- * no further work.  (This function has a full name --
- * _omap_hwmod_idle() rather than simply _idle() -- because it is
- * currently required by the pm34xx.c idle loop.)  Returns -EINVAL if
- * the hwmod is in the wrong state or returns 0.
+ * 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 "
@@ -1245,6 +1278,10 @@ int _omap_hwmod_idle(struct omap_hwmod *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;
@@ -1261,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 "
@@ -1270,6 +1310,18 @@ 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)
                _shutdown_sysc(oh);
 
@@ -1288,7 +1340,9 @@ static int _shutdown(struct omap_hwmod *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;
 
@@ -1298,23 +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.  @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) {
@@ -1334,7 +1380,6 @@ static int _setup(struct omap_hwmod *oh, void *data)
                }
        }
 
-       mutex_init(&oh->_mutex);
        oh->_state = _HWMOD_STATE_INITIALIZED;
 
        /*
@@ -1347,7 +1392,7 @@ static int _setup(struct omap_hwmod *oh, void *data)
        if ((oh->flags & HWMOD_INIT_NO_RESET) && oh->rst_lines_cnt == 1)
                return 0;
 
-       r = _omap_hwmod_enable(oh);
+       r = _enable(oh);
        if (r) {
                pr_warning("omap_hwmod: %s: cannot be enabled (%d)\n",
                           oh->name, oh->_state);
@@ -1359,7 +1404,7 @@ static int _setup(struct omap_hwmod *oh, void *data)
 
                /*
                 * OCP_SYSCONFIG bits need to be reprogrammed after a softreset.
-                * The _omap_hwmod_enable() function should be split to
+                * The _enable() function should be split to
                 * avoid the rewrite of the OCP_SYSCONFIG register.
                 */
                if (oh->class->sysc) {
@@ -1368,12 +1413,77 @@ static int _setup(struct omap_hwmod *oh, void *data)
                }
        }
 
-       if (!(oh->flags & HWMOD_INIT_NO_IDLE) && !skip_setup_idle)
-               _omap_hwmod_idle(oh);
+       postsetup_state = oh->_postsetup_state;
+       if (postsetup_state == _HWMOD_STATE_UNKNOWN)
+               postsetup_state = _HWMOD_STATE_ENABLED;
+
+       /*
+        * 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 0;
 }
 
+/**
+ * _register - register a struct omap_hwmod
+ * @oh: struct omap_hwmod *
+ *
+ * Registers the omap_hwmod @oh.  Returns -EEXIST if an omap_hwmod
+ * already has been registered by the same name; -EINVAL if the
+ * omap_hwmod is in the wrong state, if @oh is NULL, if the
+ * omap_hwmod's class field is NULL; if the omap_hwmod is missing a
+ * name, or if the omap_hwmod's class is missing a name; or 0 upon
+ * success.
+ *
+ * XXX The data should be copied into bootmem, so the original data
+ * should be marked __initdata and freed after init.  This would allow
+ * unneeded omap_hwmods to be freed on multi-OMAP configurations.  Note
+ * that the copy process would be relatively complex due to the large number
+ * of substructures.
+ */
+static int __init _register(struct omap_hwmod *oh)
+{
+       int ret, ms_id;
+
+       if (!oh || !oh->name || !oh->class || !oh->class->name ||
+           (oh->_state != _HWMOD_STATE_UNKNOWN))
+               return -EINVAL;
+
+       pr_debug("omap_hwmod: %s: registering\n", oh->name);
+
+       if (_lookup(oh->name))
+               return -EEXIST;
+
+       ms_id = _find_mpu_port_index(oh);
+       if (!IS_ERR_VALUE(ms_id)) {
+               oh->_mpu_port_index = ms_id;
+               oh->_mpu_rt_va = _find_mpu_rt_base(oh, oh->_mpu_port_index);
+       } else {
+               oh->_int_flags |= _HWMOD_NO_MPU_PORT;
+       }
+
+       list_add_tail(&oh->node, &omap_hwmod_list);
+
+       spin_lock_init(&oh->_lock);
+
+       oh->_state = _HWMOD_STATE_REGISTERED;
+
+       ret = 0;
+
+       return ret;
+}
 
 
 /* Public functions */
@@ -1426,59 +1536,6 @@ int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode)
        return retval;
 }
 
-/**
- * omap_hwmod_register - register a struct omap_hwmod
- * @oh: struct omap_hwmod *
- *
- * Registers the omap_hwmod @oh.  Returns -EEXIST if an omap_hwmod
- * already has been registered by the same name; -EINVAL if the
- * omap_hwmod is in the wrong state, if @oh is NULL, if the
- * omap_hwmod's class field is NULL; if the omap_hwmod is missing a
- * name, or if the omap_hwmod's class is missing a name; or 0 upon
- * success.
- *
- * XXX The data should be copied into bootmem, so the original data
- * should be marked __initdata and freed after init.  This would allow
- * unneeded omap_hwmods to be freed on multi-OMAP configurations.  Note
- * that the copy process would be relatively complex due to the large number
- * of substructures.
- */
-int omap_hwmod_register(struct omap_hwmod *oh)
-{
-       int ret, ms_id;
-
-       if (!oh || !oh->name || !oh->class || !oh->class->name ||
-           (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;
-       }
-
-       ms_id = _find_mpu_port_index(oh);
-       if (!IS_ERR_VALUE(ms_id)) {
-               oh->_mpu_port_index = ms_id;
-               oh->_mpu_rt_va = _find_mpu_rt_base(oh, oh->_mpu_port_index);
-       } else {
-               oh->_int_flags |= _HWMOD_NO_MPU_PORT;
-       }
-
-       list_add_tail(&oh->node, &omap_hwmod_list);
-
-       oh->_state = _HWMOD_STATE_REGISTERED;
-
-       ret = 0;
-
-ohr_unlock:
-       mutex_unlock(&omap_hwmod_mutex);
-       return ret;
-}
-
 /**
  * omap_hwmod_lookup - look up a registered omap_hwmod by name
  * @name: name of the omap_hwmod to look up
@@ -1493,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;
 }
@@ -1521,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;
 }
@@ -1542,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;
@@ -1558,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;
@@ -1570,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;
 
@@ -1588,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;
 }
@@ -1632,18 +1655,18 @@ int omap_hwmod_unregister(struct omap_hwmod *oh)
 int omap_hwmod_enable(struct omap_hwmod *oh)
 {
        int r;
+       unsigned long flags;
 
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&oh->_mutex);
-       r = _omap_hwmod_enable(oh);
-       mutex_unlock(&oh->_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 *
@@ -1653,12 +1676,14 @@ int omap_hwmod_enable(struct omap_hwmod *oh)
  */
 int omap_hwmod_idle(struct omap_hwmod *oh)
 {
+       unsigned long flags;
+
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&oh->_mutex);
-       _omap_hwmod_idle(oh);
-       mutex_unlock(&oh->_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
+       _idle(oh);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1673,12 +1698,14 @@ int omap_hwmod_idle(struct omap_hwmod *oh)
  */
 int omap_hwmod_shutdown(struct omap_hwmod *oh)
 {
+       unsigned long flags;
+
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&oh->_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
        _shutdown(oh);
-       mutex_unlock(&oh->_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1691,9 +1718,11 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh)
  */
 int omap_hwmod_enable_clocks(struct omap_hwmod *oh)
 {
-       mutex_lock(&oh->_mutex);
+       unsigned long flags;
+
+       spin_lock_irqsave(&oh->_lock, flags);
        _enable_clocks(oh);
-       mutex_unlock(&oh->_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1706,9 +1735,11 @@ int omap_hwmod_enable_clocks(struct omap_hwmod *oh)
  */
 int omap_hwmod_disable_clocks(struct omap_hwmod *oh)
 {
-       mutex_lock(&oh->_mutex);
+       unsigned long flags;
+
+       spin_lock_irqsave(&oh->_lock, flags);
        _disable_clocks(oh);
-       mutex_unlock(&oh->_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return 0;
 }
@@ -1752,13 +1783,14 @@ void omap_hwmod_ocp_barrier(struct omap_hwmod *oh)
 int omap_hwmod_reset(struct omap_hwmod *oh)
 {
        int r;
+       unsigned long flags;
 
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&oh->_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
        r = _reset(oh);
-       mutex_unlock(&oh->_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return r;
 }
@@ -1955,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(&oh->_mutex);
-       _enable_wakeup(oh);
-       mutex_unlock(&oh->_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;
 }
@@ -1980,13 +2017,18 @@ 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(&oh->_mutex);
-       _disable_wakeup(oh);
-       mutex_unlock(&oh->_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;
 }
@@ -2006,13 +2048,14 @@ int omap_hwmod_disable_wakeup(struct omap_hwmod *oh)
 int omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name)
 {
        int ret;
+       unsigned long flags;
 
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&oh->_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
        ret = _assert_hardreset(oh, name);
-       mutex_unlock(&oh->_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return ret;
 }
@@ -2032,13 +2075,14 @@ int omap_hwmod_assert_hardreset(struct omap_hwmod *oh, const char *name)
 int omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name)
 {
        int ret;
+       unsigned long flags;
 
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&oh->_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
        ret = _deassert_hardreset(oh, name);
-       mutex_unlock(&oh->_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return ret;
 }
@@ -2057,13 +2101,14 @@ int omap_hwmod_deassert_hardreset(struct omap_hwmod *oh, const char *name)
 int omap_hwmod_read_hardreset(struct omap_hwmod *oh, const char *name)
 {
        int ret;
+       unsigned long flags;
 
        if (!oh)
                return -EINVAL;
 
-       mutex_lock(&oh->_mutex);
+       spin_lock_irqsave(&oh->_lock, flags);
        ret = _read_hardreset(oh, name);
-       mutex_unlock(&oh->_mutex);
+       spin_unlock_irqrestore(&oh->_lock, flags);
 
        return ret;
 }
@@ -2075,9 +2120,8 @@ int omap_hwmod_read_hardreset(struct omap_hwmod *oh, const char *name)
  * @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.
@@ -2096,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",
@@ -2108,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);
@@ -2117,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;
+}