aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Neumüller <cn00@gmx.at>2015-12-15 14:51:23 +0100
committerCaio Schnepper <caioschnepper@gmail.com>2016-02-05 12:34:47 -0800
commit43eae3bb1ab429b1b88bd8c8558b685987f09521 (patch)
treebaa0f5e872834ab7332230d056ed4f117f135cde
parent27e5503365652148d4c8b147043ccb638eaa1090 (diff)
downloadkernel_samsung_smdk4412-43eae3bb1ab429b1b88bd8c8558b685987f09521.zip
kernel_samsung_smdk4412-43eae3bb1ab429b1b88bd8c8558b685987f09521.tar.gz
kernel_samsung_smdk4412-43eae3bb1ab429b1b88bd8c8558b685987f09521.tar.bz2
cpuidle-exynos4: Don't get stuck at 150 MHz.
The following scenario leads to the i9100 getting stuck at 150 MHz: 1. Initially, only CPU0 is online. 2. CPU0 starts to bring CPU1 online. 3. CPU0 is waiting for CPU1 to come online, and meanwhile enters idle. Since it is the only online CPU, it lowers the CPU speed to save power. 4. CPU1 goes online (NOTE: While CPU0 is still idling!) Note that local_irq_disable does not help here because CPU1 could already be starting up (most_likely idle could be entered on CPU0 while in "wait_for_completion_timeout(&cpu_running, ...)" in arch/arm/kernel/smp.c:126. This patch treats CPUs as online ("onlining") as soon as the CPU_UP_PREPARE notifier is fired. Change-Id: I7964ec3ea79084c3050db79d7d6c89db9f378326 (cherry picked from commit c96e5ebbfa00c07a7867b68799ac4c24a7a1535f)
-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 */