From 4be2ba93cf2e7a7639e13605aebe0cd656ef9d93 Mon Sep 17 00:00:00 2001 From: Finn Thain Date: Sat, 1 Dec 2018 11:53:10 +1100 Subject: 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 Signed-off-by: Geert Uytterhoeven --- arch/m68k/hp300/time.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) (limited to 'arch') 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) -- cgit v1.2.3-58-ga151