diff options
author | Finn Thain <fthain@telegraphics.com.au> | 2018-12-01 11:53:10 +1100 |
---|---|---|
committer | Geert Uytterhoeven <geert@linux-m68k.org> | 2019-03-25 10:22:24 +0100 |
commit | 4be2ba93cf2e7a7639e13605aebe0cd656ef9d93 (patch) | |
tree | b07ae9c0e3938e116f44bd7f4f4e17ed05c91bb7 | |
parent | 2ed16626f58f88892864ed0cd68f841872ed1ca1 (diff) | |
download | linux-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.c | 46 |
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) |