summaryrefslogtreecommitdiffstats
path: root/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/03-gptimer_match_plus_ovf
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-kernel/linux/linux-omap2-git/beagleboard/03-gptimer_match_plus_ovf')
-rw-r--r--meta/recipes-kernel/linux/linux-omap2-git/beagleboard/03-gptimer_match_plus_ovf94
1 files changed, 94 insertions, 0 deletions
diff --git a/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/03-gptimer_match_plus_ovf b/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/03-gptimer_match_plus_ovf
new file mode 100644
index 0000000000..3de6e05042
--- /dev/null
+++ b/meta/recipes-kernel/linux/linux-omap2-git/beagleboard/03-gptimer_match_plus_ovf
@@ -0,0 +1,94 @@
1OMAP2/3 system tick GPTIMER: use overflow interrupts to detect missing match interrupts
2
3From: Paul Walmsley <paul@pwsan.com>
4
5GPTIMER1 on some OMAP3 chips occasionally misses match conditions
6between the timer counter and the target register value, and does not
7interrupt to the MPU. This patch adds another line of defense by
8setting the timer to generate an overflow interrupt 0.5 seconds after the
9timer passes the original comparison value.
10
11If interrupts are masked for a long period of time, one would expect
12both a match and an overflow interrupt to be logged. This is considered
13a normal condition. However, if only an overflow interrupt is logged,
14this is considered evidence of a hardware bug and the kernel will issue
15a warning.
16
17This workaround is unlikely to be 100% effective, since GPTIMER1 has
18also been observed to lose overflow interrupts occasionally. It is
19hoped that the probability of losing both will be significantly lower
20than the probability of losing either one.
21---
22
23 arch/arm/mach-omap2/timer-gp.c | 36 ++++++++++++++++++++++++++++++++----
24 1 files changed, 32 insertions(+), 4 deletions(-)
25
26diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
27index 51996ba..ce5c2b4 100644
28--- a/arch/arm/mach-omap2/timer-gp.c
29+++ b/arch/arm/mach-omap2/timer-gp.c
30@@ -36,17 +36,43 @@
31 #include <asm/mach/time.h>
32 #include <asm/arch/dmtimer.h>
33
34-#define GPTIMER_MATCH_VAL 0xffff0000
35+/*
36+ * The number of timer ticks to delay will be subtracted from
37+ * GPTIMER_MATCH_VAL before loading into the timer. So GPTIMER_MATCH_VAL
38+ * constrains the longest delay that can be generated with the timer.
39+ * Since the current code uses overflow interrupts as protection against
40+ * missed comparison interrupts, this value should also be sufficiently
41+ * large such that there is not an excessively long delay between ticks
42+ * if the comparison interrupt fails to arrive. The 0xfffff800 value
43+ * below results in a half-second delay in such a case when using
44+ * the 32kHz timer as source.
45+ */
46+#define GPTIMER_MATCH_VAL (0xffffffff - (32768/2))
47
48 static struct omap_dm_timer *gptimer;
49 static struct clock_event_device clockevent_gpt;
50
51+static u32 last_load;
52+
53 static irqreturn_t omap2_gp_timer_interrupt(int irq, void *dev_id)
54 {
55 struct omap_dm_timer *gpt = (struct omap_dm_timer *)dev_id;
56 struct clock_event_device *evt = &clockevent_gpt;
57-
58- omap_dm_timer_write_status(gpt, OMAP_TIMER_INT_MATCH);
59+ u32 v;
60+
61+ v = omap_dm_timer_read_status(gpt);
62+ if ((v & OMAP_TIMER_INT_OVERFLOW) && !(v & OMAP_TIMER_INT_MATCH)) {
63+ /*
64+ * Should never happen. Current belief is that this is
65+ * due to a hardware bug in the GPTIMER block on some
66+ * OMAP3 revisions.
67+ */
68+ pr_err("*** GPTIMER missed match interrupt! last load: %08x\n",
69+ last_load);
70+ WARN_ON(1);
71+ }
72+
73+ omap_dm_timer_write_status(gpt, v);
74
75 evt->event_handler(evt);
76 return IRQ_HANDLED;
77@@ -61,6 +87,7 @@ static struct irqaction omap2_gp_timer_irq = {
78 static int omap2_gp_timer_set_next_event(unsigned long cycles,
79 struct clock_event_device *evt)
80 {
81+ last_load = GPTIMER_MATCH_VAL - cycles;
82 omap_dm_timer_set_load_start(gptimer, 0, GPTIMER_MATCH_VAL - cycles);
83
84 return 0;
85@@ -99,7 +126,8 @@ static void __init omap2_gp_clockevent_init(void)
86 omap_dm_timer_stop(gptimer);
87 /* omap_dm_timer_set_load(gptimer, 0, 0);*/
88 omap_dm_timer_set_match(gptimer, 1, GPTIMER_MATCH_VAL);
89- omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_MATCH);
90+ omap_dm_timer_set_int_enable(gptimer, OMAP_TIMER_INT_MATCH |
91+ OMAP_TIMER_INT_OVERFLOW);
92
93 clockevent_gpt.mult = div_sc(tick_rate, NSEC_PER_SEC,
94 clockevent_gpt.shift);