summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2016-08-25 14:26:59 +0800
committerSasha Levin <alexander.levin@verizon.com>2016-08-31 22:05:44 -0400
commitc8ca488fa42069e07e2fce5789dcbddc4fb2f143 (patch)
tree1882fb497eb51159a2187345e4098de1ec3d18f7
parent82d1894c12e5b0f56254af46dd0b8bfec7930cfc (diff)
downloadlwn-c8ca488fa42069e07e2fce5789dcbddc4fb2f143.tar.gz
lwn-c8ca488fa42069e07e2fce5789dcbddc4fb2f143.zip
clocksource/drivers/sun4i: Clear interrupts after stopping timer in probe function
[ Upstream commit b53e7d000d9e6e9fd2c6eb6b82d2783c67fd599e ] The bootloader (U-boot) sometimes uses this timer for various delays. It uses it as a ongoing counter, and does comparisons on the current counter value. The timer counter is never stopped. In some cases when the user interacts with the bootloader, or lets it idle for some time before loading Linux, the timer may expire, and an interrupt will be pending. This results in an unexpected interrupt when the timer interrupt is enabled by the kernel, at which point the event_handler isn't set yet. This results in a NULL pointer dereference exception, panic, and no way to reboot. Clear any pending interrupts after we stop the timer in the probe function to avoid this. Cc: stable@vger.kernel.org Signed-off-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
-rw-r--r--drivers/clocksource/sun4i_timer.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index f4a9c0058b4d..501c95941882 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -120,12 +120,16 @@ static struct clock_event_device sun4i_clockevent = {
.set_next_event = sun4i_clkevt_next_event,
};
+static void sun4i_timer_clear_interrupt(void)
+{
+ writel(TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_ST_REG);
+}
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
- writel(0x1, timer_base + TIMER_IRQ_ST_REG);
+ sun4i_timer_clear_interrupt();
evt->event_handler(evt);
return IRQ_HANDLED;
@@ -182,6 +186,9 @@ static void __init sun4i_timer_init(struct device_node *node)
/* Make sure timer is stopped before playing with interrupts */
sun4i_clkevt_time_stop(0);
+ /* clear timer0 interrupt */
+ sun4i_timer_clear_interrupt();
+
sun4i_clockevent.cpumask = cpu_possible_mask;
sun4i_clockevent.irq = irq;