#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>
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
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);
/* 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:
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;
core_deepest_state = PWRDM_POWER_OFF;
}
+ if (!omap_uart_can_sleep())
+ core_deepest_state = PWRDM_POWER_RET;
+
/* Check if current state is valid */
if ((cx->valid) &&
(cx->mpu_state >= mpu_deepest_state) &&
* 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;
- if (!omap3_can_sleep()) {
- new_state_idx = drv->safe_state_index;
- goto select_state;
- }
-
/*
- * 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
* 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 */