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>
This commit is contained in:
Finn Thain 2018-12-01 11:53:10 +11:00 коммит произвёл Geert Uytterhoeven
Родитель 2ed16626f5
Коммит 4be2ba93cf
1 изменённых файлов: 24 добавлений и 16 удалений

Просмотреть файл

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