mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 02:50:43 +00:00
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: Santosh Shilimkar <santosh.shilimkar@ti.com> [ j-keerthy@ti.com rework on 3.14] Signed-off-by: Keerthy <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: Nishanth Menon <nm@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
This commit is contained in:
parent
cbf2642872
commit
7abdb0e23e
2 changed files with 80 additions and 2 deletions
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue