Commit f49f7007 authored by Russell King's avatar Russell King Committed by Greg Kroah-Hartman

ARM: OMAP2+: fix lack of timer interrupts on CPU1 after hotplug

[ Upstream commit 50d6b3cf ]

If we have a kernel configured for periodic timer interrupts, and we
have cpuidle enabled, then we end up with CPU1 losing timer interupts
after a hotplug.

This can manifest itself in RCU stall warnings, or userspace becoming
unresponsive.

The problem is that the kernel initially wants to use the TWD timer
for interrupts, but the TWD loses context when we enter the C3 cpuidle
state.  Nothing reprograms the TWD after idle.

We have solved this in the past by switching to broadcast timer ticks,
and cpuidle44xx switches to that mode at boot time.  However, there is
nothing to switch from periodic mode local timers after a hotplug
operation.

We call tick_broadcast_enter() in omap_enter_idle_coupled(), which one
would expect would take care of the issue, but internally this only
deals with one-shot local timers - tick_broadcast_enable() on the other
hand only deals with periodic local timers.  So, we need to call both.
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
[tony@atomide.com: just standardized the subject line]
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 8f07d764
...@@ -152,6 +152,10 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev, ...@@ -152,6 +152,10 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
mpuss_can_lose_context = (cx->mpu_state == PWRDM_POWER_RET) && mpuss_can_lose_context = (cx->mpu_state == PWRDM_POWER_RET) &&
(cx->mpu_logic_state == PWRDM_POWER_OFF); (cx->mpu_logic_state == PWRDM_POWER_OFF);
/* Enter broadcast mode for periodic timers */
tick_broadcast_enable();
/* Enter broadcast mode for one-shot timers */
tick_broadcast_enter(); tick_broadcast_enter();
/* /*
...@@ -218,15 +222,6 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev, ...@@ -218,15 +222,6 @@ 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)
{
tick_broadcast_enable();
}
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,
...@@ -319,8 +314,5 @@ int __init omap4_idle_init(void) ...@@ -319,8 +314,5 @@ 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(idle_driver, cpu_online_mask); return cpuidle_register(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