m68k: mvme147: Handle timer counter overflow
authorFinn Thain <fthain@telegraphics.com.au>
Sat, 1 Dec 2018 00:53:10 +0000 (11:53 +1100)
committerGeert Uytterhoeven <geert@linux-m68k.org>
Mon, 25 Mar 2019 09:22:24 +0000 (10:22 +0100)
Reading the timer counter races with timer overflow (and the
corresponding interrupt). This is resolved by reading the overflow
register and taking this value into account. The interrupt handler
must clear the overflow register when it eventually executes.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
arch/m68k/include/asm/mvme147hw.h
arch/m68k/mvme147/config.c

index 7c3dd513128e33d549c63193dd4b1e2dad1691e1..257b29184af913f8613b3cbcacb91a08e14ef924 100644 (file)
@@ -66,6 +66,7 @@ struct pcc_regs {
 #define PCC_INT_ENAB           0x08
 
 #define PCC_TIMER_INT_CLR      0x80
+#define PCC_TIMER_CLR_OVF      0x04
 
 #define PCC_LEVEL_ABORT                0x07
 #define PCC_LEVEL_SERIAL       0x04
index c44a254e8a8c385a2a0cd9795b1521eb9aa3665e..545a1fe0e1194695953b066ebd0837d4bbd2d26d 100644 (file)
@@ -118,7 +118,7 @@ static irqreturn_t mvme147_timer_int (int irq, void *dev_id)
 
        local_irq_save(flags);
        m147_pcc->t1_int_cntrl = PCC_TIMER_INT_CLR;
-       m147_pcc->t1_int_cntrl = PCC_INT_ENAB|PCC_LEVEL_TIMER1;
+       m147_pcc->t1_cntrl = PCC_TIMER_CLR_OVF;
        clk_total += PCC_TIMER_CYCLES;
        timer_routine(0, NULL);
        local_irq_restore(flags);
@@ -144,21 +144,22 @@ void mvme147_sched_init (irq_handler_t timer_routine)
        clocksource_register_hz(&mvme147_clk, PCC_TIMER_CLOCK_FREQ);
 }
 
-/* XXX There are race hazards in this code XXX */
 static u64 mvme147_read_clk(struct clocksource *cs)
 {
        unsigned long flags;
-       volatile unsigned short *cp = (volatile unsigned short *)0xfffe1012;
-       unsigned short n;
+       u8 overflow, tmp;
+       u16 count;
        u32 ticks;
 
        local_irq_save(flags);
-       n = *cp;
-       while (n != *cp)
-               n = *cp;
-
-       n -= PCC_TIMER_PRELOAD;
-       ticks = clk_total + n;
+       tmp = m147_pcc->t1_cntrl >> 4;
+       count = m147_pcc->t1_count;
+       overflow = m147_pcc->t1_cntrl >> 4;
+       if (overflow != tmp)
+               count = m147_pcc->t1_count;
+       count -= PCC_TIMER_PRELOAD;
+       ticks = count + overflow * PCC_TIMER_CYCLES;
+       ticks += clk_total;
        local_irq_restore(flags);
 
        return ticks;