Commit 2b4adc03 authored by Santosh Shilimkar's avatar Santosh Shilimkar Committed by Jiri Slaby

ARM: OMAP4: Fix the boot regression with CPU_IDLE enabled

commit 4b353a70 upstream.

On OMAP4 panda board, there have been several bug reports about boot
hang and lock-ups with CPU_IDLE enabled. The root cause of the issue
is missing interrupts while in idle state. Commit cb7094e8 {cpuidle / omap4 :
use CPUIDLE_FLAG_TIMER_STOP flag} moved the broadcast notifiers to common
code for right reasons but on OMAP4 which suffers from a nasty ROM code
bug with GIC, commit ff999b8a {ARM: OMAP4460: Workaround for ROM bug ..},
we loose interrupts which leads to issues like lock-up, hangs etc.

Patch reverts commit cb7094 {cpuidle / omap4 : use CPUIDLE_FLAG_TIMER_STOP
flag} and 54769d65 {cpuidle: OMAP4: remove timer broadcast initialization} to
avoid the issue. With this change, OMAP4 panda boards, the mentioned
issues are getting fixed. We no longer loose interrupts which was the cause
of the regression.

Fixes: cb7094e8 (cpuidle / omap4 : use CPUIDLE_FLAG_TIMER_STOP flag)
Fixes: ff999b8a (cpuidle: OMAP4: remove timer broadcast initialization)
Cc: Roger Quadros <rogerq@ti.com>
Cc: Kevin Hilman <khilman@linaro.org>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Reported-tested-by: default avatarRoger Quadros <rogerq@ti.com>
Reported-tested-by: default avatarKevin Hilman <khilman@linaro.org>
Tested-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarSantosh Shilimkar <santosh.shilimkar@ti.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarJiri Slaby <jslaby@suse.cz>
parent bbcf0e59
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/cpu_pm.h> #include <linux/cpu_pm.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/clockchips.h>
#include <asm/cpuidle.h> #include <asm/cpuidle.h>
#include <asm/proc-fns.h> #include <asm/proc-fns.h>
...@@ -80,6 +81,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev, ...@@ -80,6 +81,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
int index) int index)
{ {
struct idle_statedata *cx = state_ptr + index; struct idle_statedata *cx = state_ptr + index;
int cpu_id = smp_processor_id();
/* /*
* CPU0 has to wait and stay ON until CPU1 is OFF state. * CPU0 has to wait and stay ON until CPU1 is OFF state.
...@@ -104,6 +106,8 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev, ...@@ -104,6 +106,8 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
} }
} }
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu_id);
/* /*
* Call idle CPU PM enter notifier chain so that * Call idle CPU PM enter notifier chain so that
* VFP and per CPU interrupt context is saved. * VFP and per CPU interrupt context is saved.
...@@ -147,6 +151,8 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev, ...@@ -147,6 +151,8 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
(cx->mpu_logic_state == PWRDM_POWER_OFF)) (cx->mpu_logic_state == PWRDM_POWER_OFF))
cpu_cluster_pm_exit(); cpu_cluster_pm_exit();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu_id);
fail: fail:
cpuidle_coupled_parallel_barrier(dev, &abort_barrier); cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
cpu_done[dev->cpu] = false; cpu_done[dev->cpu] = false;
...@@ -154,6 +160,16 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev, ...@@ -154,6 +160,16 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
return index; return index;
} }
/*
* For each cpu, setup the broadcast timer because local timers
* stops for the states above C1.
*/
static void omap_setup_broadcast_timer(void *arg)
{
int cpu = smp_processor_id();
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
}
static struct cpuidle_driver omap4_idle_driver = { static struct cpuidle_driver omap4_idle_driver = {
.name = "omap4_idle", .name = "omap4_idle",
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -171,8 +187,7 @@ static struct cpuidle_driver omap4_idle_driver = { ...@@ -171,8 +187,7 @@ static struct cpuidle_driver omap4_idle_driver = {
/* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */ /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
.exit_latency = 328 + 440, .exit_latency = 328 + 440,
.target_residency = 960, .target_residency = 960,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
CPUIDLE_FLAG_TIMER_STOP,
.enter = omap_enter_idle_coupled, .enter = omap_enter_idle_coupled,
.name = "C2", .name = "C2",
.desc = "CPUx OFF, MPUSS CSWR", .desc = "CPUx OFF, MPUSS CSWR",
...@@ -181,8 +196,7 @@ static struct cpuidle_driver omap4_idle_driver = { ...@@ -181,8 +196,7 @@ static struct cpuidle_driver omap4_idle_driver = {
/* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */ /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
.exit_latency = 460 + 518, .exit_latency = 460 + 518,
.target_residency = 1100, .target_residency = 1100,
.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED | .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED,
CPUIDLE_FLAG_TIMER_STOP,
.enter = omap_enter_idle_coupled, .enter = omap_enter_idle_coupled,
.name = "C3", .name = "C3",
.desc = "CPUx OFF, MPUSS OSWR", .desc = "CPUx OFF, MPUSS OSWR",
...@@ -213,5 +227,8 @@ int __init omap4_idle_init(void) ...@@ -213,5 +227,8 @@ int __init omap4_idle_init(void)
if (!cpu_clkdm[0] || !cpu_clkdm[1]) if (!cpu_clkdm[0] || !cpu_clkdm[1])
return -ENODEV; return -ENODEV;
/* 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(&omap4_idle_driver, cpu_online_mask);
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment