OMAP2/3 system tick GPTIMER: use match interrupts rather than overflow interrupts

From: Paul Walmsley <paul@pwsan.com>

On some OMAP3 chips, GPTIMER1 will occasionally decline to interrupt
the MPU when a timer overflow event occurs.  The timer stops running;
and TOCR is sometimes incremented; but the MPU apparently never receives
the interrupt.  This patch was an experiment in using the GPTIMER
match interrupt to determine if it resolves the problem.
Unfortunately, it does not; the same problem occurs with match
interrupts; but this patch is preserved as the base for a
match+overflow interrupt workaround used in a following patch.
---

 arch/arm/mach-omap2/timer-gp.c |   32 ++++++++++----------------------
 1 files changed, 10 insertions(+), 22 deletions(-)

diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
index 557603f..51996ba 100644
--- a/arch/arm/mach-omap2/timer-gp.c
+++ b/arch/arm/mach-omap2/timer-gp.c
@@ -36,6 +36,8 @@
 #include <asm/mach/time.h>
 #include <asm/arch/dmtimer.h>
 
+#define GPTIMER_MATCH_VAL      0xffff0000
+
 static struct omap_dm_timer *gptimer;
 static struct clock_event_device clockevent_gpt;
 
@@ -44,7 +46,7 @@ static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
 	struct omap_dm_timer *gpt = (struct omap_dm_timer *)dev_id;
 	struct clock_event_device *evt = &clockevent_gpt;
 
-	omap_dm_timer_write_status(gpt, OMAP_TIMER_INT_OVERFLOW);
+	omap_dm_timer_write_status(gpt, OMAP_TIMER_INT_MATCH);
 
 	evt->event_handler(evt);
 	return IRQ_HANDLED;
@@ -59,7 +61,7 @@ static struct irqaction omap2_gp_timer_irq = {
 static int omap2_gp_timer_set_next_event(unsigned long cycles,
 					 struct clock_event_device *evt)
 {
-	omap_dm_timer_set_load_start(gptimer, 0, 0xffffffff - cycles);
+	omap_dm_timer_set_load_start(gptimer, 0, GPTIMER_MATCH_VAL - cycles);
 
 	return 0;
 }
@@ -67,29 +69,12 @@ static int omap2_gp_timer_set_next_event(unsigned long cycles,
 static void omap2_gp_timer_set_mode(enum clock_event_mode mode,
 				    struct clock_event_device *evt)
 {
-	u32 period;
-
 	omap_dm_timer_stop(gptimer);
-
-	switch (mode) {
-	case CLOCK_EVT_MODE_PERIODIC:
-		period = clk_get_rate(omap_dm_timer_get_fclk(gptimer)) / HZ;
-		period -= 1;
-
-		omap_dm_timer_set_load_start(gptimer, 1, 0xffffffff - period);
-		break;
-	case CLOCK_EVT_MODE_ONESHOT:
-		break;
-	case CLOCK_EVT_MODE_UNUSED:
-	case CLOCK_EVT_MODE_SHUTDOWN:
-	case CLOCK_EVT_MODE_RESUME:
-		break;
-	}
 }
 
 static struct clock_event_device clockevent_gpt = {
 	.name		= "gp timer",
-	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+	.features	= CLOCK_EVT_FEAT_ONESHOT,
 	.shift		= 32,
 	.set_next_event	= omap2_gp_timer_set_next_event,
 	.set_mode	= omap2_gp_timer_set_mode,
@@ -111,12 +96,15 @@ static void __init omap2_gp_clockevent_init(void)
 
 	omap2_gp_timer_irq.dev_id = (void *)gptimer;
 	setup_irq(omap_dm_timer_get_irq(gptimer), &omap2_gp_timer_irq);
-	omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_OVERFLOW);
+	omap_dm_timer_stop(gptimer);
+	/*	omap_dm_timer_set_load(gptimer, 0, 0);*/
+	omap_dm_timer_set_match(gptimer, 1, GPTIMER_MATCH_VAL);
+	omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_MATCH);
 
 	clockevent_gpt.mult = div_sc(tick_rate, NSEC_PER_SEC,
 				     clockevent_gpt.shift);
 	clockevent_gpt.max_delta_ns =
-		clockevent_delta2ns(0xffffffff, &clockevent_gpt);
+		clockevent_delta2ns(GPTIMER_MATCH_VAL, &clockevent_gpt);
 	clockevent_gpt.min_delta_ns =
 		clockevent_delta2ns(1, &clockevent_gpt);
 
