summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFinn Thain <fthain@telegraphics.com.au>2018-12-01 11:53:10 +1100
committerGeert Uytterhoeven <geert@linux-m68k.org>2019-03-25 10:22:24 +0100
commit4be2ba93cf2e7a7639e13605aebe0cd656ef9d93 (patch)
treeb07ae9c0e3938e116f44bd7f4f4e17ed05c91bb7
parent2ed16626f58f88892864ed0cd68f841872ed1ca1 (diff)
downloadlinux-4be2ba93cf2e7a7639e13605aebe0cd656ef9d93.tar.bz2
m68k: hp300: Handle timer counter overflow
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>
-rw-r--r--arch/m68k/hp300/time.c46
1 files 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)