OMAP2/3 system tick GPTIMER: use match interrupts rather than overflow interrupts From: Paul Walmsley 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 #include +#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);