From: Brian Maly <bmaly@redhat.com> Date: Thu, 28 Aug 2008 15:30:00 -0400 Subject: [x86] hpet: consolidate assignment of hpet_period Message-id: 48B6FCB8.9090900@redhat.com O-Subject: Re: [RHEL5 patch] hpet: consolidate assignment of hpet_period Bugzilla: 435726 RH-Acked-by: Alan Cox <alan@redhat.com> Resolves BZ 435726 AMD Griffin RS780/SB700 platforms have spread spectrum capabilities. When down spread is enabled, the mean frequency of the HPET runs slower... 99.75 MHz instead of 100 MHz. The BIOS works around this issue by dynamically re-calculating hpet_period (after spread spectrum is engaged) and storing this temporary value in BIOS RAM during boot. A fake base address for the register is generated by the BIOS which causes the kernel to read the dynamically generated hpet_period from BIOS RAM. Once the hpet_period is read, the BIOS resets the register base address to the real address. Any reads of the hpet_period register after this point will return the actual statically coded period of 100MHz. In RHEL5 this causes timekeeping to skew badly. hpet_period is read twice during boot, so on the first read we get the actual hpet_period (the dynamically calculated one) and on the second read we get the theoretical hpet_period (the statically coded one). This results is that the timekeeping math uses an inaccurate value and returns an inaccurate result. This patch resolves this issue by consolidating the assignment of hpet_period to one point in the code, as we do upstream. With the patch we only do only one read of hpet_period and use that value throughout. This patch has been tested on the affected hardware and resolves the issue. Brian Here is a followup patch. With this patch we will now have only a single read of HPET_PERIOD register in i386, so as to be consistent with the single read in x86_64 and the single read in Xen. Brian diff --git a/arch/i386/kernel/hpet.c b/arch/i386/kernel/hpet.c index 17647a5..3f12168 100644 --- a/arch/i386/kernel/hpet.c +++ b/arch/i386/kernel/hpet.c @@ -12,6 +12,7 @@ /* FSEC = 10^-15 NSEC = 10^-9 */ #define FSEC_PER_NSEC 1000000 +extern unsigned long hpet_period; static void *hpet_ptr; static cycle_t read_hpet(void) @@ -31,7 +32,6 @@ static struct clocksource clocksource_hpet = { static int __init init_hpet_clocksource(void) { - unsigned long hpet_period; void __iomem* hpet_base; u64 tmp; @@ -43,9 +43,6 @@ static int __init init_hpet_clocksource(void) (void __iomem*)ioremap_nocache(hpet_address, HPET_MMAP_SIZE); hpet_ptr = hpet_base + HPET_COUNTER; - /* calculate the frequency: */ - hpet_period = readl(hpet_base + HPET_PERIOD); - /* * hpet period is in femto seconds per cycle * so we need to convert this to ns/cyc units diff --git a/arch/i386/kernel/time_hpet.c b/arch/i386/kernel/time_hpet.c index 27210db..cf206c8 100644 --- a/arch/i386/kernel/time_hpet.c +++ b/arch/i386/kernel/time_hpet.c @@ -22,7 +22,7 @@ #include <asm/hpet.h> #include <linux/hpet.h> -static unsigned long hpet_period; /* fsecs / HPET clock */ +unsigned long hpet_period; /* fsecs / HPET clock */ unsigned long hpet_tick; /* hpet clks count per tick */ unsigned long hpet_tick_real; /* hpet clocks per interrupt */ unsigned long hpet_address; /* hpet memory map physical address */