summaryrefslogtreecommitdiff
path: root/packages/linux/linux-omap2-git/beagleboard/02-gptimer_clear_tocr
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-omap2-git/beagleboard/02-gptimer_clear_tocr')
-rw-r--r--packages/linux/linux-omap2-git/beagleboard/02-gptimer_clear_tocr97
1 files changed, 97 insertions, 0 deletions
diff --git a/packages/linux/linux-omap2-git/beagleboard/02-gptimer_clear_tocr b/packages/linux/linux-omap2-git/beagleboard/02-gptimer_clear_tocr
new file mode 100644
index 0000000000..b92c5dd126
--- /dev/null
+++ b/packages/linux/linux-omap2-git/beagleboard/02-gptimer_clear_tocr
@@ -0,0 +1,97 @@
+OMAP2/3 GPTIMER: clear TOCR register after timer overflow interrupt and during load
+
+From: Paul Walmsley <paul@pwsan.com>
+
+There appears to be a hardware bug in the 1-ms tick generation section
+of the GPTIMER blocks on some OMAP3530 chips. TOCR is sometimes
+incremented when a a timer overflow event occurs and TPIR = TPNR =
+TOWR = TOCR = 0, in contradiction of 34xx TRM 16.2.4.2.1. When TOCR
+is incremented under these conditions, the timer will not generate any
+further overflow interrupts. (The kernel currently relies on overflow
+interrupts to generate ticks and drive the scheduler.)
+
+This patch works around the bug by clearing TOCR in the GPTIMER
+overflow ISR and in the timer load functions.
+
+The precise sequence of hardware events needed to reproduce this bug
+is still unknown. Without this patch, the bug is consistently
+observable on several BeagleBoards (including mine and Koen's) within
+a few minutes of boot. It's not clear whether this bug is present on
+all OMAP3 revisions, or whether it is simply specific to certain
+OMAP3530ES2.2 lots.
+
+This patch fixes the "serial hangs" reported by some BeagleBoard
+users. During these hangs, characters are still received from the
+serial port, so magic SysRq will still work; but characters are never
+delivered on to the underlying line discipline. This since
+tty_flip_buffer_push() uses schedule_delayed_work() to defer passing
+the input buffer to the line discipline, but the delayed work function
+is never called since no timer tick ever arrives.
+
+The patch should also fix some other sporadic boot hangs reported by
+BeagleBoard users that are due to timer interrupt non-delivery.
+---
+
+ arch/arm/mach-omap2/timer-gp.c | 1 +
+ arch/arm/plat-omap/dmtimer.c | 8 ++++++++
+ include/asm-arm/arch-omap/dmtimer.h | 2 ++
+ 3 files changed, 11 insertions(+), 0 deletions(-)
+
+diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c
+index edc0c9e..869fe14 100644
+--- a/arch/arm/mach-omap2/timer-gp.c
++++ b/arch/arm/mach-omap2/timer-gp.c
+@@ -44,6 +44,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_clear_ovf_cnt(gpt);
+ omap_dm_timer_write_status(gpt, OMAP_TIMER_INT_OVERFLOW);
+
+ evt->event_handler(evt);
+diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
+index f22506a..2cbb4cc 100644
+--- a/arch/arm/plat-omap/dmtimer.c
++++ b/arch/arm/plat-omap/dmtimer.c
+@@ -543,6 +543,8 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,
+ while (readl(timer->io_base + (OMAP_TIMER_WRITE_PEND_REG & 0xff)))
+ cpu_relax();
+
++ omap_dm_timer_write_reg(timer, OMAP_TIMER_TICK_INT_MASK_SET_REG, 0);
++
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0);
+ }
+
+@@ -561,6 +563,7 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,
+
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, load);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);
++ omap_dm_timer_write_reg(timer, OMAP_TIMER_TICK_INT_MASK_SET_REG, 0);
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);
+ }
+
+@@ -614,6 +617,11 @@ void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,
+ omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, value);
+ }
+
++void omap_dm_timer_clear_ovf_cnt(struct omap_dm_timer *timer)
++{
++ omap_dm_timer_write_reg(timer, OMAP_TIMER_TICK_INT_MASK_SET_REG, 0);
++}
++
+ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)
+ {
+ unsigned int l;
+diff --git a/include/asm-arm/arch-omap/dmtimer.h b/include/asm-arm/arch-omap/dmtimer.h
+index 02b29e8..7b1138b 100644
+--- a/include/asm-arm/arch-omap/dmtimer.h
++++ b/include/asm-arm/arch-omap/dmtimer.h
+@@ -73,6 +73,8 @@ void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler);
+
+ void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, unsigned int value);
+
++void omap_dm_timer_clear_ovf_cnt(struct omap_dm_timer *timer);
++
+ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer);
+ void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value);
+ unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer);