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 <fth...@telegraphics.com.au>
---
TODO: find a spare counter for the clocksource, rather than hanging
it off the HZ timer.

Changed since v3:
 - Use clk_offset variable to track the offset when the irq check is skipped.
---
 arch/m68k/hp300/time.c | 46 +++++++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 19 deletions(-)

diff --git a/arch/m68k/hp300/time.c b/arch/m68k/hp300/time.c
index 90982803a629..bfee13e1d0fe 100644
--- a/arch/m68k/hp300/time.c
+++ b/arch/m68k/hp300/time.c
@@ -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)
-- 
2.18.1

Reply via email to