diff options
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_ovf | 94 |
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 @@ | |||
1 | OMAP2/3 system tick GPTIMER: use overflow interrupts to detect missing match interrupts | ||
2 | |||
3 | From: Paul Walmsley <paul@pwsan.com> | ||
4 | |||
5 | GPTIMER1 on some OMAP3 chips occasionally misses match conditions | ||
6 | between the timer counter and the target register value, and does not | ||
7 | interrupt to the MPU. This patch adds another line of defense by | ||
8 | setting the timer to generate an overflow interrupt 0.5 seconds after the | ||
9 | timer passes the original comparison value. | ||
10 | |||
11 | If interrupts are masked for a long period of time, one would expect | ||
12 | both a match and an overflow interrupt to be logged. This is considered | ||
13 | a normal condition. However, if only an overflow interrupt is logged, | ||
14 | this is considered evidence of a hardware bug and the kernel will issue | ||
15 | a warning. | ||
16 | |||
17 | This workaround is unlikely to be 100% effective, since GPTIMER1 has | ||
18 | also been observed to lose overflow interrupts occasionally. It is | ||
19 | hoped that the probability of losing both will be significantly lower | ||
20 | than 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 | |||
26 | diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c | ||
27 | index 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); | ||