|
@@ -21,6 +21,7 @@
|
|
|
#include <linux/i8253.h>
|
|
|
#include <linux/init.h>
|
|
|
#include <linux/kernel_stat.h>
|
|
|
+#include <linux/math64.h>
|
|
|
#include <linux/sched.h>
|
|
|
#include <linux/spinlock.h>
|
|
|
#include <linux/interrupt.h>
|
|
@@ -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;
|
|
|
}
|
|
|
}
|