Commit 7abdb0e2 authored by Santosh Shilimkar's avatar Santosh Shilimkar Committed by Tony Lindgren

ARM: OMAP5: Add basic cpuidle MPU CSWR support

Add OMAP5 CPUIDLE support.

This patch adds MPUSS low power states in cpuidle.

        C1 - CPU0 WFI + CPU1 WFI + MPU ON
        C2 - CPU0 RET + CPU1 RET + MPU CSWR

Modified from TI kernel tree commit 605967fd2205 ("ARM: DRA7: PM:
cpuidle MPU CSWR support") except enable cpuidle for omap5 instead
of dra7.

According to Nishanth Menon <nm@ti.com>, cpuidle on dra7 is not
supported properly in the hardware so we don't want to enable it.
However, for omap5 this adds some nice power savings. Note that
the TI 3.8 based tree has other cpuidle states that we may be able
to enable later on.

On omap5-uevm, the power consumption eventually settles down to about
920mW with ehci-omap and ohci-omap3 unloaded compared to about 1.7W
without these patches. Note that it seems to take few minutes after
booting for the idle power to go down to 920mW from 1.3W, no idea so
far what might be causing that.
Signed-off-by: default avatarSantosh Shilimkar <santosh.shilimkar@ti.com>
[ j-keerthy@ti.com rework on 3.14]
Signed-off-by: default avatarKeerthy <j-keerthy@ti.com>
[nm@ti.com: updates based on profiling]
[tony@atomide.com: dropped CPUIDLE_FLAG_TIME_VALID no longer used,
changed for omap5 only as requested by Nishanth, updated comments]
Signed-off-by: default avatarNishanth Menon <nm@ti.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent cbf26428
......@@ -21,6 +21,7 @@
#include "common.h"
#include "pm.h"
#include "prm.h"
#include "soc.h"
#include "clockdomain.h"
#define MAX_CPUS 2
......@@ -30,6 +31,7 @@ struct idle_statedata {
u32 cpu_state;
u32 mpu_logic_state;
u32 mpu_state;
u32 mpu_state_vote;
};
static struct idle_statedata omap4_idle_data[] = {
......@@ -50,12 +52,26 @@ static struct idle_statedata omap4_idle_data[] = {
},
};
static struct idle_statedata omap5_idle_data[] = {
{
.cpu_state = PWRDM_POWER_ON,
.mpu_state = PWRDM_POWER_ON,
.mpu_logic_state = PWRDM_POWER_ON,
},
{
.cpu_state = PWRDM_POWER_RET,
.mpu_state = PWRDM_POWER_RET,
.mpu_logic_state = PWRDM_POWER_RET,
},
};
static struct powerdomain *mpu_pd, *cpu_pd[MAX_CPUS];
static struct clockdomain *cpu_clkdm[MAX_CPUS];
static atomic_t abort_barrier;
static bool cpu_done[MAX_CPUS];
static struct idle_statedata *state_ptr = &omap4_idle_data[0];
static DEFINE_RAW_SPINLOCK(mpu_lock);
/* Private functions */
......@@ -77,6 +93,32 @@ static int omap_enter_idle_simple(struct cpuidle_device *dev,
return index;
}
static int omap_enter_idle_smp(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct idle_statedata *cx = state_ptr + index;
unsigned long flag;
raw_spin_lock_irqsave(&mpu_lock, flag);
cx->mpu_state_vote++;
if (cx->mpu_state_vote == num_online_cpus()) {
pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
}
raw_spin_unlock_irqrestore(&mpu_lock, flag);
omap4_enter_lowpower(dev->cpu, cx->cpu_state);
raw_spin_lock_irqsave(&mpu_lock, flag);
if (cx->mpu_state_vote == num_online_cpus())
omap_set_pwrdm_state(mpu_pd, PWRDM_POWER_ON);
cx->mpu_state_vote--;
raw_spin_unlock_irqrestore(&mpu_lock, flag);
return index;
}
static int omap_enter_idle_coupled(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
......@@ -220,6 +262,32 @@ static struct cpuidle_driver omap4_idle_driver = {
.safe_state_index = 0,
};
static struct cpuidle_driver omap5_idle_driver = {
.name = "omap5_idle",
.owner = THIS_MODULE,
.states = {
{
/* C1 - CPU0 ON + CPU1 ON + MPU ON */
.exit_latency = 2 + 2,
.target_residency = 5,
.enter = omap_enter_idle_simple,
.name = "C1",
.desc = "CPUx WFI, MPUSS ON"
},
{
/* C2 - CPU0 RET + CPU1 RET + MPU CSWR */
.exit_latency = 48 + 60,
.target_residency = 100,
.flags = CPUIDLE_FLAG_TIMER_STOP,
.enter = omap_enter_idle_smp,
.name = "C2",
.desc = "CPUx CSWR, MPUSS CSWR",
},
},
.state_count = ARRAY_SIZE(omap5_idle_data),
.safe_state_index = 0,
};
/* Public functions */
/**
......@@ -230,6 +298,16 @@ static struct cpuidle_driver omap4_idle_driver = {
*/
int __init omap4_idle_init(void)
{
struct cpuidle_driver *idle_driver;
if (soc_is_omap54xx()) {
state_ptr = &omap5_idle_data[0];
idle_driver = &omap5_idle_driver;
} else {
state_ptr = &omap4_idle_data[0];
idle_driver = &omap4_idle_driver;
}
mpu_pd = pwrdm_lookup("mpu_pwrdm");
cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm");
cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm");
......@@ -244,5 +322,5 @@ int __init omap4_idle_init(void)
/* Configure the broadcast timer on each cpu */
on_each_cpu(omap_setup_broadcast_timer, NULL, 1);
return cpuidle_register(&omap4_idle_driver, cpu_online_mask);
return cpuidle_register(idle_driver, cpu_online_mask);
}
......@@ -287,7 +287,7 @@ int __init omap4_pm_init(void)
/* Overwrite the default cpu_do_idle() */
arm_pm_idle = omap_default_idle;
if (cpu_is_omap44xx())
if (cpu_is_omap44xx() || soc_is_omap54xx())
omap4_idle_init();
err2:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment