Commit 4be2ba93 authored by Finn Thain's avatar Finn Thain Committed by Geert Uytterhoeven
Browse files

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: default avatarFinn Thain <fthain@telegraphics.com.au>
Signed-off-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
parent 2ed16626
Loading
Loading
Loading
Loading
+27 −19
Original line number Diff line number Diff line
@@ -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);

@@ -71,20 +75,24 @@ 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;
	unsigned char lsb, msb, msb_new;
	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;
	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;