m68k: hp300: 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)
Because hp300_read_clk() never checks the timer interrupt flag it may
fail to notice that the timer has wrapped, allowing the clock to jump
backwards. This is not a new problem.

This is resolved by checking the interrupt flag and, if need be,
taking wrap-around into account. The interrupt handler clears the flag
when it eventually executes.

Signed-off-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
arch/m68k/hp300/time.c

index 9098280..bfee13e 100644 (file)
@@ -30,7 +30,7 @@ static struct clocksource hp300_clk = {
        .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
-static u32 clk_total;
+static u32 clk_total, clk_offset;
 
 /* Clock hardware definitions */
 
@@ -41,9 +41,12 @@ static u32 clk_total;
 #define        CLKCR3          CLKCR1
 #define        CLKSR           CLKCR2
 #define        CLKMSB1         0x5
+#define        CLKLSB1         0x7
 #define        CLKMSB2         0x9
 #define        CLKMSB3         0xD
 
+#define        CLKSR_INT1      BIT(0)
+
 /* This is for machines which generate the exact clock. */
 
 #define HP300_TIMER_CLOCK_FREQ 250000
@@ -60,6 +63,7 @@ static irqreturn_t hp300_tick(int irq, void *dev_id)
        in_8(CLOCKBASE + CLKSR);
        asm volatile ("movpw %1@(5),%0" : "=d" (tmp) : "a" (CLOCKBASE));
        clk_total += INTVAL;
+       clk_offset = 0;
        timer_routine(0, NULL);
        local_irq_restore(flags);
 
@@ -70,24 +74,28 @@ static irqreturn_t hp300_tick(int irq, void *dev_id)
 
 static u64 hp300_read_clk(struct clocksource *cs)
 {
-  unsigned long flags;
-  unsigned char lsb, msb1, msb2;
-  u32 ticks;
-
-  local_irq_save(flags);
-  /* Read current timer 1 value */
-  msb1 = in_8(CLOCKBASE + 5);
-  lsb = in_8(CLOCKBASE + 7);
-  msb2 = in_8(CLOCKBASE + 5);
-  if (msb1 != msb2)
-    /* A carry happened while we were reading.  Read it again */
-    lsb = in_8(CLOCKBASE + 7);
-
-  ticks = INTVAL - ((msb2 << 8) | lsb);
-  ticks += clk_total;
-  local_irq_restore(flags);
-
-  return ticks;
+       unsigned long flags;
+       unsigned char lsb, msb, msb_new;
+       u32 ticks;
+
+       local_irq_save(flags);
+       /* Read current timer 1 value */
+       msb = in_8(CLOCKBASE + CLKMSB1);
+again:
+       if ((in_8(CLOCKBASE + CLKSR) & CLKSR_INT1) && msb > 0)
+               clk_offset = INTVAL;
+       lsb = in_8(CLOCKBASE + CLKLSB1);
+       msb_new = in_8(CLOCKBASE + CLKMSB1);
+       if (msb_new != msb) {
+               msb = msb_new;
+               goto again;
+       }
+
+       ticks = INTVAL - ((msb << 8) | lsb);
+       ticks += clk_offset + clk_total;
+       local_irq_restore(flags);
+
+       return ticks;
 }
 
 void __init hp300_sched_init(irq_handler_t vector)