98 lines
3.4 KiB
Plaintext
98 lines
3.4 KiB
Plaintext
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);
|
|
|