Skip to content

Commit

Permalink
ARM: OMAP4: CPUidle: Use coupled cpuidle states to implement SMP cpui…
Browse files Browse the repository at this point in the history
…dle.

OMAP4 CPUDILE driver is converted mainly based on notes from the
coupled cpuidle patch series.

The changes include :
- Register both CPUs and C-states to cpuidle driver.
- Set struct cpuidle_device.coupled_cpus
- Set struct cpuidle_device.safe_state to non coupled state.
- Set CPUIDLE_FLAG_COUPLED in struct cpuidle_state.flags for each
  state that affects multiple cpus.
- Separate ->enter hooks for coupled & simple idle.
- CPU0 wait loop for CPU1 power transition.
- CPU1 wakeup mechanism for the idle exit.
- Enabling ARCH_NEEDS_CPU_IDLE_COUPLED for OMAP4.

Thanks to Kevin Hilman and Colin Cross on the suggestions/fixes
on the intermediate version of this patch.

Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
  • Loading branch information
Santosh Shilimkar authored and Kevin Hilman committed Jul 25, 2012
1 parent 11d6ec2 commit dd3ad97
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 46 deletions.
1 change: 1 addition & 0 deletions arch/arm/mach-omap2/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ config ARCH_OMAP4
select PM_OPP if PM
select USB_ARCH_HAS_EHCI if USB_SUPPORT
select ARM_CPU_SUSPEND if PM
select ARCH_NEEDS_CPU_IDLE_COUPLED

comment "OMAP Core Type"
depends on ARCH_OMAP2
Expand Down
112 changes: 66 additions & 46 deletions arch/arm/mach-omap2/cpuidle44xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "common.h"
#include "pm.h"
#include "prm.h"
#include "clockdomain.h"

#ifdef CONFIG_CPU_IDLE

Expand Down Expand Up @@ -49,10 +50,11 @@ static struct omap4_idle_statedata omap4_idle_data[] = {
},
};

static struct powerdomain *mpu_pd, *cpu0_pd, *cpu1_pd;
static struct powerdomain *mpu_pd, *cpu_pd[NR_CPUS];
static struct clockdomain *cpu_clkdm[NR_CPUS];

/**
* omap4_enter_idle - Programs OMAP4 to enter the specified state
* omap4_enter_idle_coupled_[simple/coupled] - OMAP4 cpuidle entry functions
* @dev: cpuidle device
* @drv: cpuidle driver
* @index: the index of state to be entered
Expand All @@ -61,60 +63,71 @@ static struct powerdomain *mpu_pd, *cpu0_pd, *cpu1_pd;
* specified low power state selected by the governor.
* Returns the amount of time spent in the low power state.
*/
static int omap4_enter_idle(struct cpuidle_device *dev,
static int omap4_enter_idle_simple(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
local_fiq_disable();
omap_do_wfi();
local_fiq_enable();

return index;
}

static int omap4_enter_idle_coupled(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
struct omap4_idle_statedata *cx = &omap4_idle_data[index];
u32 cpu1_state;
int cpu_id = smp_processor_id();

local_fiq_disable();

/*
* CPU0 has to stay ON (i.e in C1) until CPU1 is OFF state.
* CPU0 has to wait and stay ON until CPU1 is OFF state.
* This is necessary to honour hardware recommondation
* of triggeing all the possible low power modes once CPU1 is
* out of coherency and in OFF mode.
* Update dev->last_state so that governor stats reflects right
* data.
*/
cpu1_state = pwrdm_read_pwrst(cpu1_pd);
if (cpu1_state != PWRDM_POWER_OFF) {
index = drv->safe_state_index;
cx = &omap4_idle_data[index];
if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
while (pwrdm_read_pwrst(cpu_pd[1]) != PWRDM_POWER_OFF)
cpu_relax();
}

if (index > 0)
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);

/*
* Call idle CPU PM enter notifier chain so that
* VFP and per CPU interrupt context is saved.
*/
if (cx->cpu_state == PWRDM_POWER_OFF)
cpu_pm_enter();

pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
omap_set_pwrdm_state(mpu_pd, cx->mpu_state);

/*
* Call idle CPU cluster PM enter notifier chain
* to save GIC and wakeupgen context.
*/
if ((cx->mpu_state == PWRDM_POWER_RET) &&
(cx->mpu_logic_state == PWRDM_POWER_OFF))
cpu_cluster_pm_enter();
cpu_pm_enter();

if (dev->cpu == 0) {
pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
omap_set_pwrdm_state(mpu_pd, cx->mpu_state);

/*
* Call idle CPU cluster PM enter notifier chain
* to save GIC and wakeupgen context.
*/
if ((cx->mpu_state == PWRDM_POWER_RET) &&
(cx->mpu_logic_state == PWRDM_POWER_OFF))
cpu_cluster_pm_enter();
}

omap4_enter_lowpower(dev->cpu, cx->cpu_state);

/* Wakeup CPU1 only if it is not offlined */
if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
clkdm_wakeup(cpu_clkdm[1]);
clkdm_allow_idle(cpu_clkdm[1]);
}

/*
* Call idle CPU PM exit notifier chain to restore
* VFP and per CPU IRQ context. Only CPU0 state is
* considered since CPU1 is managed by CPU hotplug.
* VFP and per CPU IRQ context.
*/
if (pwrdm_read_prev_pwrst(cpu0_pd) == PWRDM_POWER_OFF)
cpu_pm_exit();
cpu_pm_exit();

/*
* Call idle CPU cluster PM exit notifier chain
Expand All @@ -123,8 +136,7 @@ static int omap4_enter_idle(struct cpuidle_device *dev,
if (omap4_mpuss_read_prev_context_state())
cpu_cluster_pm_exit();

if (index > 0)
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);

local_fiq_enable();

Expand All @@ -143,25 +155,25 @@ struct cpuidle_driver omap4_idle_driver = {
.exit_latency = 2 + 2,
.target_residency = 5,
.flags = CPUIDLE_FLAG_TIME_VALID,
.enter = omap4_enter_idle,
.enter = omap4_enter_idle_simple,
.name = "C1",
.desc = "MPUSS ON"
},
{
/* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
.exit_latency = 328 + 440,
.target_residency = 960,
.flags = CPUIDLE_FLAG_TIME_VALID,
.enter = omap4_enter_idle,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
.enter = omap4_enter_idle_coupled,
.name = "C2",
.desc = "MPUSS CSWR",
},
{
/* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
.exit_latency = 460 + 518,
.target_residency = 1100,
.flags = CPUIDLE_FLAG_TIME_VALID,
.enter = omap4_enter_idle,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
.enter = omap4_enter_idle_coupled,
.name = "C3",
.desc = "MPUSS OSWR",
},
Expand All @@ -182,19 +194,27 @@ int __init omap4_idle_init(void)
unsigned int cpu_id = 0;

mpu_pd = pwrdm_lookup("mpu_pwrdm");
cpu0_pd = pwrdm_lookup("cpu0_pwrdm");
cpu1_pd = pwrdm_lookup("cpu1_pwrdm");
if ((!mpu_pd) || (!cpu0_pd) || (!cpu1_pd))
cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm");
cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm");
if ((!mpu_pd) || (!cpu_pd[0]) || (!cpu_pd[1]))
return -ENODEV;

cpu_clkdm[0] = clkdm_lookup("mpu0_clkdm");
cpu_clkdm[1] = clkdm_lookup("mpu1_clkdm");
if (!cpu_clkdm[0] || !cpu_clkdm[1])
return -ENODEV;

dev = &per_cpu(omap4_idle_dev, cpu_id);
dev->cpu = cpu_id;
for_each_cpu(cpu_id, cpu_online_mask) {
dev = &per_cpu(omap4_idle_dev, cpu_id);
dev->cpu = cpu_id;
dev->coupled_cpus = *cpu_online_mask;

cpuidle_register_driver(&omap4_idle_driver);
cpuidle_register_driver(&omap4_idle_driver);

if (cpuidle_register_device(dev)) {
pr_err("%s: CPUidle register device failed\n", __func__);
return -EIO;
if (cpuidle_register_device(dev)) {
pr_err("%s: CPUidle register failed\n", __func__);
return -EIO;
}
}

return 0;
Expand Down

0 comments on commit dd3ad97

Please sign in to comment.