OMAP: hwmod: Wait the idle status to be disabled
authorBenoit Cousson <b-cousson@ti.com>
Sun, 10 Jul 2011 11:56:30 +0000 (05:56 -0600)
committerPaul Walmsley <paul@pwsan.com>
Sun, 10 Jul 2011 11:56:30 +0000 (05:56 -0600)
It is mandatory to wait for a module to be in disabled state before
potentially disabling source clock or re-asserting a reset.

omap_hwmod_idle and omap_hwmod_shutdown does not wait for
the module to be fully idle.

Add a cm_xxx accessor to wait the clkctrl idle status to be disabled.
Fix hwmod_[idle|shutdown] to use this API.

Based on Rajendra's initial patch.

Please note that most interconnects hwmod will return one timeout because
it is impossible for them to be in idle since the processor is accessing
the registers though the interconnect.

Signed-off-by: Benoit Cousson <b-cousson@ti.com>
Signed-off-by: Rajendra Nayak <rnayak@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Todd Poynor <toddpoynor@google.com>
[paul@pwsan.com: move cpu_is_*() tests to the top of _wait_target_disable();
 incorporate some feedback from Todd]
Signed-off-by: Paul Walmsley <paul@pwsan.com>
arch/arm/mach-omap2/cminst44xx.c
arch/arm/mach-omap2/cminst44xx.h
arch/arm/mach-omap2/omap_hwmod.c

index 9033dd4937c19155c0145f2a59740469c616a879..0fe3f147f26299ff74168e781c28b00a231c965c 100644 (file)
@@ -284,3 +284,28 @@ int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs,
        return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
 }
 
+/**
+ * omap4_cminst_wait_module_idle - wait for a module to be in 'disabled'
+ * state
+ * @part: PRCM partition ID that the CM_CLKCTRL register exists in
+ * @inst: CM instance register offset (*_INST macro)
+ * @cdoffs: Clockdomain register offset (*_CDOFFS macro)
+ * @clkctrl_offs: Module clock control register offset (*_CLKCTRL macro)
+ *
+ * Wait for the module IDLEST to be disabled. Some PRCM transition,
+ * like reset assertion or parent clock de-activation must wait the
+ * module to be fully disabled.
+ */
+int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs)
+{
+       int i = 0;
+
+       if (!clkctrl_offs)
+               return 0;
+
+       omap_test_timeout((_clkctrl_idlest(part, inst, cdoffs, clkctrl_offs) ==
+                          CLKCTRL_IDLEST_DISABLED),
+                         MAX_MODULE_READY_TIME, i);
+
+       return (i < MAX_MODULE_READY_TIME) ? 0 : -EBUSY;
+}
index 8eba2ae87ad4edc27899b32e885dd795d8e6f565..a98540081f97d3a6ad9cf14b6cb198ac90a438b3 100644 (file)
@@ -18,6 +18,7 @@ extern void omap4_cminst_clkdm_force_sleep(u8 part, s16 inst, u16 cdoffs);
 extern void omap4_cminst_clkdm_force_wakeup(u8 part, s16 inst, u16 cdoffs);
 
 extern int omap4_cminst_wait_module_ready(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
+extern int omap4_cminst_wait_module_idle(u8 part, u16 inst, s16 cdoffs, u16 clkctrl_offs);
 
 /*
  * In an ideal world, we would not export these low-level functions,
index 00241ea5bf092c29a4dc2f470cacb2b9d6e2be79..d21f49b8764630a588e1ed390652472a4ac4f53f 100644 (file)
@@ -1101,6 +1101,36 @@ static int _wait_target_ready(struct omap_hwmod *oh)
        return ret;
 }
 
+/**
+ * _wait_target_disable - wait for a module to be disabled
+ * @oh: struct omap_hwmod *
+ *
+ * Wait for a module @oh to enter slave idle.  Returns 0 if the module
+ * does not have an IDLEST bit or if the module successfully enters
+ * slave idle; otherwise, pass along the return value of the
+ * appropriate *_cm*_wait_module_idle() function.
+ */
+static int _wait_target_disable(struct omap_hwmod *oh)
+{
+       /* TODO: For now just handle OMAP4+ */
+       if (cpu_is_omap24xx() || cpu_is_omap34xx())
+               return 0;
+
+       if (!oh)
+               return -EINVAL;
+
+       if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
+               return 0;
+
+       if (oh->flags & HWMOD_NO_IDLEST)
+               return 0;
+
+       return omap4_cminst_wait_module_idle(oh->clkdm->prcm_partition,
+                                            oh->clkdm->cm_inst,
+                                            oh->clkdm->clkdm_offs,
+                                            oh->prcm.omap4.clkctrl_offs);
+}
+
 /**
  * _lookup_hardreset - fill register bit info for this hwmod/reset line
  * @oh: struct omap_hwmod *
@@ -1410,6 +1440,8 @@ static int _enable(struct omap_hwmod *oh)
  */
 static int _idle(struct omap_hwmod *oh)
 {
+       int ret;
+
        pr_debug("omap_hwmod: %s: idling\n", oh->name);
 
        if (oh->_state != _HWMOD_STATE_ENABLED) {
@@ -1422,6 +1454,10 @@ static int _idle(struct omap_hwmod *oh)
                _idle_sysc(oh);
        _del_initiator_dep(oh, mpu_oh);
        _disable_clocks(oh);
+       ret = _wait_target_disable(oh);
+       if (ret)
+               pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
+                       oh->name);
 
        /* Mux pins for device idle if populated */
        if (oh->mux && oh->mux->pads_dynamic)
@@ -1514,6 +1550,10 @@ static int _shutdown(struct omap_hwmod *oh)
                _del_initiator_dep(oh, mpu_oh);
                /* XXX what about the other system initiators here? dma, dsp */
                _disable_clocks(oh);
+               ret = _wait_target_disable(oh);
+               if (ret)
+                       pr_warn("omap_hwmod: %s: _wait_target_disable failed\n",
+                               oh->name);
        }
        /* XXX Should this code also force-disable the optional clocks? */