ARM: OMAP3: CPUidle: Make use of CPU PM notifiers
[pandora-kernel.git] / arch / arm / mach-omap2 / cpuidle34xx.c
index f4ba354..e8e8d39 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/sched.h>
 #include <linux/cpuidle.h>
 #include <linux/export.h>
+#include <linux/cpu_pm.h>
 
 #include <plat/prcm.h>
 #include <plat/irqs.h>
@@ -72,20 +73,6 @@ struct omap3_idle_statedata omap3_idle_data[OMAP3_NUM_STATES];
 
 struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
 
-static int _cpuidle_allow_idle(struct powerdomain *pwrdm,
-                               struct clockdomain *clkdm)
-{
-       clkdm_allow_idle(clkdm);
-       return 0;
-}
-
-static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
-                               struct clockdomain *clkdm)
-{
-       clkdm_deny_idle(clkdm);
-       return 0;
-}
-
 /**
  * omap3_enter_idle - Programs OMAP3 to enter the specified state
  * @dev: cpuidle device
@@ -109,7 +96,6 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
        getnstimeofday(&ts_preidle);
 
        local_irq_disable();
-       local_fiq_disable();
 
        pwrdm_set_next_pwrst(mpu_pd, mpu_state);
        pwrdm_set_next_pwrst(core_pd, core_state);
@@ -119,17 +105,31 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
 
        /* Deny idle for C1 */
        if (index == 0) {
-               pwrdm_for_each_clkdm(mpu_pd, _cpuidle_deny_idle);
-               pwrdm_for_each_clkdm(core_pd, _cpuidle_deny_idle);
+               clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]);
+               clkdm_deny_idle(core_pd->pwrdm_clkdms[0]);
        }
 
+       /*
+        * Call idle CPU PM enter notifier chain so that
+        * VFP context is saved.
+        */
+       if (mpu_state == PWRDM_POWER_OFF)
+               cpu_pm_enter();
+
        /* Execute ARM wfi */
        omap_sram_idle();
 
+       /*
+        * Call idle CPU PM enter notifier chain to restore
+        * VFP context.
+        */
+       if (pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
+               cpu_pm_exit();
+
        /* Re-allow idle for C1 */
        if (index == 0) {
-               pwrdm_for_each_clkdm(mpu_pd, _cpuidle_allow_idle);
-               pwrdm_for_each_clkdm(core_pd, _cpuidle_allow_idle);
+               clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
+               clkdm_allow_idle(core_pd->pwrdm_clkdms[0]);
        }
 
 return_sleep_time:
@@ -137,7 +137,6 @@ return_sleep_time:
        ts_idle = timespec_sub(ts_postidle, ts_preidle);
 
        local_irq_enable();
-       local_fiq_enable();
 
        idle_time = ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * \
                                                                USEC_PER_SEC;
@@ -239,23 +238,22 @@ static int next_valid_state(struct cpuidle_device *dev,
  * the device to the specified or a safer state.
  */
 static int omap3_enter_idle_bm(struct cpuidle_device *dev,
-                               struct cpuidle_driver *drv,
+                              struct cpuidle_driver *drv,
                               int index)
 {
        int new_state_idx;
-       u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
+       u32 core_next_state, per_next_state = 0, per_saved_state = 0;
        struct omap3_idle_statedata *cx;
        int ret;
 
        /*
-        * Prevent idle completely if CAM is active.
+        * Use only C1 if CAM is active.
         * CAM does not have wakeup capability in OMAP3.
         */
-       cam_state = pwrdm_read_pwrst(cam_pd);
-       if (cam_state == PWRDM_POWER_ON) {
+       if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON)
                new_state_idx = drv->safe_state_index;
-               goto select_state;
-       }
+       else
+               new_state_idx = next_valid_state(dev, drv, index);
 
        /*
         * FIXME: we currently manage device-specific idle states
@@ -265,24 +263,28 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
         *        its own code.
         */
 
-       /*
-        * Prevent PER off if CORE is not in retention or off as this
-        * would disable PER wakeups completely.
-        */
-       cx = cpuidle_get_statedata(&dev->states_usage[index]);
+       /* Program PER state */
+       cx = cpuidle_get_statedata(&dev->states_usage[new_state_idx]);
        core_next_state = cx->core_state;
        per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
-       if ((per_next_state == PWRDM_POWER_OFF) &&
-           (core_next_state > PWRDM_POWER_RET))
-               per_next_state = PWRDM_POWER_RET;
+       if (new_state_idx == 0) {
+               /* In C1 do not allow PER state lower than CORE state */
+               if (per_next_state < core_next_state)
+                       per_next_state = core_next_state;
+       } else {
+               /*
+                * Prevent PER OFF if CORE is not in RETention or OFF as this
+                * would disable PER wakeups completely.
+                */
+               if ((per_next_state == PWRDM_POWER_OFF) &&
+                   (core_next_state > PWRDM_POWER_RET))
+                       per_next_state = PWRDM_POWER_RET;
+       }
 
        /* Are we changing PER target state? */
        if (per_next_state != per_saved_state)
                pwrdm_set_next_pwrst(per_pd, per_next_state);
 
-       new_state_idx = next_valid_state(dev, drv, index);
-
-select_state:
        ret = omap3_enter_idle(dev, drv, new_state_idx);
 
        /* Restore original PER state if it was modified */