diff options
-rw-r--r-- | arch/arm/mach-exynos/cpuidle-exynos4.c | 79 |
1 files changed, 76 insertions, 3 deletions
diff --git a/arch/arm/mach-exynos/cpuidle-exynos4.c b/arch/arm/mach-exynos/cpuidle-exynos4.c index 88b941f..d58c3f1 100644 --- a/arch/arm/mach-exynos/cpuidle-exynos4.c +++ b/arch/arm/mach-exynos/cpuidle-exynos4.c @@ -515,6 +515,40 @@ static struct sleep_save exynos4210_set_clksrc[] = { { .reg = EXYNOS4_CLKSRC_MASK_LCD1 , .val = 0x00001111, }, }; + +static DEFINE_SPINLOCK(online_lock); +static int n_onlining_cpus_impl; + +static void add_onlininig_cpu(void) +{ + spin_lock(&online_lock); + ++n_onlining_cpus_impl; + spin_unlock(&online_lock); +} + +static void remove_onlininig_cpu(void) +{ + spin_lock(&online_lock); + --n_onlining_cpus_impl; + spin_unlock(&online_lock); +} + +static void init_onlining_cpus(void) +{ + spin_lock(&online_lock); + n_onlining_cpus_impl = num_online_cpus(); + spin_unlock(&online_lock); +} + +static int is_only_onlining_cpu(void) +{ + int result; + spin_lock(&online_lock); + result = n_onlining_cpus_impl == 1; + spin_unlock(&online_lock); + return result; +} + static int exynos4_check_enter(void) { unsigned int ret; @@ -840,7 +874,7 @@ static int exynos4_enter_idle(struct cpuidle_device *dev, spin_lock(&idle_lock); cpu_core |= (1 << cpu); - if ((cpu_core == 0x3) || (cpu_online(1) == 0)) { + if ((cpu_core == 0x3) || is_only_onlining_cpu()) { old_div = __raw_readl(EXYNOS4_CLKDIV_CPU); tmp = old_div; tmp |= ((0x7 << 28) | (0x7 << 0)); @@ -858,7 +892,7 @@ static int exynos4_enter_idle(struct cpuidle_device *dev, spin_lock(&idle_lock); - if ((cpu_core == 0x3) || (cpu_online(1) == 0)) { + if ((cpu_core == 0x3) || is_only_onlining_cpu()) { __raw_writel(old_div, EXYNOS4_CLKDIV_CPU); do { @@ -914,7 +948,12 @@ static int exynos4_enter_lowpower(struct cpuidle_device *dev, int ret; /* This mode only can be entered when only Core0 is online */ - if (num_online_cpus() != 1) { + if (use_clock_down == SW_CLK_DWN) { + enter_mode = is_only_onlining_cpu(); + } else { + enter_mode = num_online_cpus() == 1; + } + if (!enter_mode) { BUG_ON(!dev->safe_state); new_state = dev->safe_state; } @@ -975,6 +1014,31 @@ static struct notifier_block exynos4_cpuidle_notifier = { .notifier_call = exynos4_cpuidle_notifier_event, }; +static int exynos4_cpuidle_cpu_notifier_event(struct notifier_block *this, + unsigned long event, + void *hcpu) +{ + switch (event) { + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + add_onlininig_cpu(); + break; + + case CPU_UP_CANCELED: + case CPU_UP_CANCELED_FROZEN: + case CPU_DEAD: + case CPU_DEAD_FROZEN: + remove_onlininig_cpu(); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block exynos4_cpuidle_cpu_notifier = { + .notifier_call = exynos4_cpuidle_cpu_notifier_event, +}; + #ifdef CONFIG_EXYNOS4_ENABLE_CLOCK_DOWN static void __init exynos4_core_down_clk(void) { @@ -1147,7 +1211,16 @@ static int __init exynos4_init_cpuidle(void) return -EINVAL; } #endif + register_pm_notifier(&exynos4_cpuidle_notifier); + + if (use_clock_down == SW_CLK_DWN) { + get_online_cpus(); + init_onlining_cpus(); + register_cpu_notifier(&exynos4_cpuidle_cpu_notifier); + put_online_cpus(); + } + sys_pwr_conf_addr = (unsigned long)S5P_CENTRAL_SEQ_CONFIGURATION; /* Save register value for SCU */ |