95 lines
3.6 KiB
Plaintext
95 lines
3.6 KiB
Plaintext
OMAP2/3 system tick GPTIMER: use overflow interrupts to detect missing match interrupts
|
|
|
|
From: Paul Walmsley <paul@pwsan.com>
|
|
|
|
GPTIMER1 on some OMAP3 chips occasionally misses match conditions
|
|
between the timer counter and the target register value, and does not
|
|
interrupt to the MPU. This patch adds another line of defense by
|
|
setting the timer to generate an overflow interrupt 0.5 seconds after the
|
|
timer passes the original comparison value.
|
|
|
|
If interrupts are masked for a long period of time, one would expect
|
|
both a match and an overflow interrupt to be logged. This is considered
|
|
a normal condition. However, if only an overflow interrupt is logged,
|
|
this is considered evidence of a hardware bug and the kernel will issue
|
|
a warning.
|
|
|
|
This workaround is unlikely to be 100% effective, since GPTIMER1 has
|
|
also been observed to lose overflow interrupts occasionally. It is
|
|
hoped that the probability of losing both will be significantly lower
|
|
than the probability of losing either one.
|
|
---
|
|
|
|
arch/arm/mach-omap2/timer-gp.c | 36 ++++++++++++++++++++++++++++++++----
|
|
1 files changed, 32 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
|
|
index 51996ba..ce5c2b4 100644
|
|
--- a/arch/arm/mach-omap2/timer-gp.c
|
|
+++ b/arch/arm/mach-omap2/timer-gp.c
|
|
@@ -36,17 +36,43 @@
|
|
#include <asm/mach/time.h>
|
|
#include <asm/arch/dmtimer.h>
|
|
|
|
-#define GPTIMER_MATCH_VAL 0xffff0000
|
|
+/*
|
|
+ * The number of timer ticks to delay will be subtracted from
|
|
+ * GPTIMER_MATCH_VAL before loading into the timer. So GPTIMER_MATCH_VAL
|
|
+ * constrains the longest delay that can be generated with the timer.
|
|
+ * Since the current code uses overflow interrupts as protection against
|
|
+ * missed comparison interrupts, this value should also be sufficiently
|
|
+ * large such that there is not an excessively long delay between ticks
|
|
+ * if the comparison interrupt fails to arrive. The 0xfffff800 value
|
|
+ * below results in a half-second delay in such a case when using
|
|
+ * the 32kHz timer as source.
|
|
+ */
|
|
+#define GPTIMER_MATCH_VAL (0xffffffff - (32768/2))
|
|
|
|
static struct omap_dm_timer *gptimer;
|
|
static struct clock_event_device clockevent_gpt;
|
|
|
|
+static u32 last_load;
|
|
+
|
|
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_MATCH);
|
|
+ u32 v;
|
|
+
|
|
+ v = omap_dm_timer_read_status(gpt);
|
|
+ if ((v & OMAP_TIMER_INT_OVERFLOW) && !(v & OMAP_TIMER_INT_MATCH)) {
|
|
+ /*
|
|
+ * Should never happen. Current belief is that this is
|
|
+ * due to a hardware bug in the GPTIMER block on some
|
|
+ * OMAP3 revisions.
|
|
+ */
|
|
+ pr_err("*** GPTIMER missed match interrupt! last load: %08x\n",
|
|
+ last_load);
|
|
+ WARN_ON(1);
|
|
+ }
|
|
+
|
|
+ omap_dm_timer_write_status(gpt, v);
|
|
|
|
evt->event_handler(evt);
|
|
return IRQ_HANDLED;
|
|
@@ -61,6 +87,7 @@ static struct irqaction omap2_gp_timer_irq = {
|
|
static int omap2_gp_timer_set_next_event(unsigned long cycles,
|
|
struct clock_event_device *evt)
|
|
{
|
|
+ last_load = GPTIMER_MATCH_VAL - cycles;
|
|
omap_dm_timer_set_load_start(gptimer, 0, GPTIMER_MATCH_VAL - cycles);
|
|
|
|
return 0;
|
|
@@ -99,7 +126,8 @@ static void __init omap2_gp_clockevent_init(void)
|
|
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);
|
|
+ omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_MATCH |
|
|
+ OMAP_TIMER_INT_OVERFLOW);
|
|
|
|
clockevent_gpt.mult = div_sc(tick_rate, NSEC_PER_SEC,
|
|
clockevent_gpt.shift);
|