diff --git a/arch/mips/mti-malta/malta-time.c b/arch/mips/mti-malta/malta-time.c index 2539687b77f6..7407da04f8d6 100644 --- a/arch/mips/mti-malta/malta-time.c +++ b/arch/mips/mti-malta/malta-time.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,8 @@ static void __init estimate_frequencies(void) { unsigned long flags; unsigned int count, start; + unsigned char secs1, secs2, ctrl; + int secs; cycle_t giccount = 0, gicstart = 0; #if defined(CONFIG_KVM_GUEST) && CONFIG_KVM_GUEST_TIMER_FREQ @@ -84,29 +87,48 @@ static void __init estimate_frequencies(void) if (gic_present) gic_start_count(); - /* Read counter exactly on falling edge of update flag. */ + /* + * Read counters exactly on rising edge of update flag. + * This helps get an accurate reading under virtualisation. + */ while (CMOS_READ(RTC_REG_A) & RTC_UIP); while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); - start = read_c0_count(); if (gic_present) gicstart = gic_read_count(); - /* Read counter exactly on falling edge of update flag. */ + /* Wait for falling edge before reading RTC. */ while (CMOS_READ(RTC_REG_A) & RTC_UIP); - while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); + secs1 = CMOS_READ(RTC_SECONDS); + /* Read counters again exactly on rising edge of update flag. */ + while (!(CMOS_READ(RTC_REG_A) & RTC_UIP)); count = read_c0_count(); if (gic_present) giccount = gic_read_count(); + /* Wait for falling edge before reading RTC again. */ + while (CMOS_READ(RTC_REG_A) & RTC_UIP); + secs2 = CMOS_READ(RTC_SECONDS); + + ctrl = CMOS_READ(RTC_CONTROL); + local_irq_restore(flags); + if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + secs1 = bcd2bin(secs1); + secs2 = bcd2bin(secs2); + } + secs = secs2 - secs1; + if (secs < 1) + secs += 60; + count -= start; + count /= secs; mips_hpt_frequency = count; if (gic_present) { - giccount -= gicstart; + giccount = div_u64(giccount - gicstart, secs); gic_frequency = giccount; } }