aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-exynos/cpuidle-exynos4.c79
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 */