SOURCES (LINUX_2_6): linux-2.6-kernel-clock-is-running-3-times-too...

pluto pluto at pld-linux.org
Wed Nov 16 11:35:18 CET 2005


Author: pluto                        Date: Wed Nov 16 10:35:18 2005 GMT
Module: SOURCES                       Tag: LINUX_2_6
---- Log message:
- fix for HP notebooks.

---- Files affected:
SOURCES:
   linux-2.6-kernel-clock-is-running-3-times-too-fast.patch (NONE -> 1.1.2.1)  (NEW)

---- Diffs:

================================================================
Index: SOURCES/linux-2.6-kernel-clock-is-running-3-times-too-fast.patch
diff -u /dev/null SOURCES/linux-2.6-kernel-clock-is-running-3-times-too-fast.patch:1.1.2.1
--- /dev/null	Wed Nov 16 11:35:18 2005
+++ SOURCES/linux-2.6-kernel-clock-is-running-3-times-too-fast.patch	Wed Nov 16 11:35:13 2005
@@ -0,0 +1,408 @@
+diff -Naur 2.6.14/arch/i386/kernel/apm.c 2.6.14-pmtmr/arch/i386/kernel/apm.c
+--- 2.6.14/arch/i386/kernel/apm.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/i386/kernel/apm.c	2005-10-30 11:02:30.000000000 +0100
+@@ -239,6 +239,8 @@
+ extern int (*console_blank_hook)(int);
+ #endif
+ 
++extern u16 timer_mult;
++
+ /*
+  * The apm_bios device is one of the misc char devices.
+  * This is its minor number.
+@@ -1167,9 +1169,9 @@
+ 	/* set the clock to 100 Hz */
+ 	outb_p(0x34, PIT_MODE);		/* binary, mode 2, LSB/MSB, ch 0 */
+ 	udelay(10);
+-	outb_p(LATCH & 0xff, PIT_CH0);	/* LSB */
++	outb_p((LATCH * timer_mult / 1000) & 0xff, PIT_CH0);	/* LSB */
+ 	udelay(10);
+-	outb(LATCH >> 8, PIT_CH0);	/* MSB */
++	outb((LATCH * timer_mult / 1000) >> 8, PIT_CH0);	/* MSB */
+ 	udelay(10);
+ 	spin_unlock_irqrestore(&i8253_lock, flags);
+ #endif
+diff -Naur 2.6.14/arch/i386/kernel/time.c 2.6.14-pmtmr/arch/i386/kernel/time.c
+--- 2.6.14/arch/i386/kernel/time.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/i386/kernel/time.c	2005-10-30 11:02:30.000000000 +0100
+@@ -86,6 +86,12 @@
+ DEFINE_SPINLOCK(rtc_lock);
+ EXPORT_SYMBOL(rtc_lock);
+ 
++/* 
++ * timer_mult is a mutiplier used to work around some very buggy
++ * hardware where the PIT timer runs way too fast.
++ */
++u16 timer_mult = 1000;
++
+ #include <asm/i8253.h>
+ 
+ DEFINE_SPINLOCK(i8253_lock);
+diff -Naur 2.6.14/arch/i386/kernel/timers/common.c 2.6.14-pmtmr/arch/i386/kernel/timers/common.c
+--- 2.6.14/arch/i386/kernel/timers/common.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/i386/kernel/timers/common.c	2005-10-30 11:02:30.000000000 +0100
+@@ -23,7 +23,9 @@
+  * device.
+  */
+ 
+-#define CALIBRATE_TIME	(5 * 1000020/HZ)
++#define CALIBRATE_TIME	(5 * 1000020/(HZ * timer_mult / 1000))
++
++extern u16 timer_mult;
+ 
+ unsigned long calibrate_tsc(void)
+ {
+diff -Naur 2.6.14/arch/i386/kernel/timers/timer_pit.c 2.6.14-pmtmr/arch/i386/kernel/timers/timer_pit.c
+--- 2.6.14/arch/i386/kernel/timers/timer_pit.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/i386/kernel/timers/timer_pit.c	2005-10-30 11:02:30.000000000 +0100
+@@ -19,6 +19,8 @@
+ #include "do_timer.h"
+ #include "io_ports.h"
+ 
++extern u16 timer_mult;
++
+ static int count_p; /* counter in get_offset_pit() */
+ 
+ static int __init init_pit(char* override)
+@@ -115,8 +117,8 @@
+         /* VIA686a test code... reset the latch if count > max + 1 */
+         if (count > LATCH) {
+                 outb_p(0x34, PIT_MODE);
+-                outb_p(LATCH & 0xff, PIT_CH0);
+-                outb(LATCH >> 8, PIT_CH0);
++                outb_p((LATCH * timer_mult / 1000) & 0xff, PIT_CH0);
++                outb((LATCH * timer_mult / 1000) >> 8, PIT_CH0);
+                 count = LATCH - 1;
+         }
+ 	
+@@ -169,8 +171,8 @@
+ 	spin_lock_irqsave(&i8253_lock, flags);
+ 	outb_p(0x34,PIT_MODE);		/* binary, mode 2, LSB/MSB, ch 0 */
+ 	udelay(10);
+-	outb_p(LATCH & 0xff , PIT_CH0);	/* LSB */
++	outb_p((LATCH * timer_mult) & 0xff , PIT_CH0);	/* LSB */
+ 	udelay(10);
+-	outb(LATCH >> 8 , PIT_CH0);	/* MSB */
++	outb((LATCH * timer_mult) >> 8 , PIT_CH0);	/* MSB */
+ 	spin_unlock_irqrestore(&i8253_lock, flags);
+ }
+diff -Naur 2.6.14/arch/i386/kernel/timers/timer_pm.c 2.6.14-pmtmr/arch/i386/kernel/timers/timer_pm.c
+--- 2.6.14/arch/i386/kernel/timers/timer_pm.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/i386/kernel/timers/timer_pm.c	2005-10-30 11:02:30.000000000 +0100
+@@ -23,6 +23,7 @@
+ 
+ #include <linux/timex.h>
+ #include "mach_timer.h"
++#include "io_ports.h"
+ 
+ /* Number of PMTMR ticks expected during calibration run */
+ #define PMTMR_TICKS_PER_SEC 3579545
+@@ -35,6 +36,10 @@
+  * in arch/i386/acpi/boot.c */
+ u32 pmtmr_ioport = 0;
+ 
++extern u16 timer_mult;
++
++extern spinlock_t i8259A_lock;
++extern spinlock_t i8253_lock;
+ 
+ /* value of the Power timer at last timer interrupt */
+ static u32 offset_tick;
+@@ -70,10 +75,11 @@
+  * Some boards have the PMTMR running way too fast. We check
+  * the PMTMR rate against PIT channel 2 to catch these cases.
+  */
+-static int verify_pmtmr_rate(void)
++static int verify_pmtmr_rate(char* override)
+ {
+ 	u32 value1, value2;
+ 	unsigned long count, delta;
++	unsigned long flags;
+ 
+ 	mach_prepare_counter();
+ 	value1 = read_pmtmr();
+@@ -81,6 +87,19 @@
+ 	value2 = read_pmtmr();
+ 	delta = (value2 - value1) & ACPI_PM_MASK;
+ 
++	/* Assume PM timer is right if the user has specified an override clock */
++	if (override[0]) {
++		timer_mult = PMTMR_EXPECTED_RATE * 1000 / delta;
++
++		if (timer_mult != 1000) {
++			printk(KERN_INFO "PIT running at invalid rate, workarround multiplier set to %d.%d\n",  timer_mult / 1000, timer_mult % 1000);
++			spin_lock_irqsave(&i8253_lock, flags);
++			outb_p(0x34, PIT_MODE);		/* binary, mode 2, LSB/MSB, ch 0 */
++			outb_p((LATCH * timer_mult / 1000) & 0xff , PIT_CH0);	/* LSB */
++			outb((LATCH * timer_mult / 1000) >> 8 , PIT_CH0);	/* MSB */
++			spin_unlock_irqrestore(&i8253_lock, flags);
++		}
++	} else
+ 	/* Check that the PMTMR delta is within 5% of what we expect */
+ 	if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
+ 	    delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
+@@ -124,7 +143,7 @@
+ 	return -ENODEV;
+ 
+ pm_good:
+-	if (verify_pmtmr_rate() != 0)
++	if (verify_pmtmr_rate(override) != 0)
+ 		return -ENODEV;
+ 
+ 	init_cpu_khz();
+diff -Naur 2.6.14/arch/i386/kernel/timers/timer_tsc.c 2.6.14-pmtmr/arch/i386/kernel/timers/timer_tsc.c
+--- 2.6.14/arch/i386/kernel/timers/timer_tsc.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/i386/kernel/timers/timer_tsc.c	2005-10-30 11:02:30.000000000 +0100
+@@ -26,6 +26,8 @@
+ #include <asm/hpet.h>
+ #include <asm/i8253.h>
+ 
++extern u16 timer_mult;
++
+ #ifdef CONFIG_HPET_TIMER
+ static unsigned long hpet_usec_quotient;
+ static unsigned long hpet_last;
+@@ -379,8 +381,8 @@
+ 	 */
+ 	if (count > LATCH) {
+ 		outb_p(0x34, PIT_MODE);
+-		outb_p(LATCH & 0xff, PIT_CH0);
+-		outb(LATCH >> 8, PIT_CH0);
++		outb_p((LATCH * timer_mult / 1000) & 0xff, PIT_CH0);
++		outb((LATCH * timer_mult / 1000) >> 8, PIT_CH0);
+ 		count = LATCH - 1;
+ 	}
+ 
+diff -Naur 2.6.14/arch/x86_64/kernel/i8259.c 2.6.14-pmtmr/arch/x86_64/kernel/i8259.c
+--- 2.6.14/arch/x86_64/kernel/i8259.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/x86_64/kernel/i8259.c	2005-10-30 11:02:30.000000000 +0100
+@@ -24,6 +24,8 @@
+ #include <asm/desc.h>
+ #include <asm/apic.h>
+ 
++extern u16 timer_mult;
++
+ /*
+  * Common place to define all x86 IRQ vectors
+  *
+@@ -498,9 +500,9 @@
+ {
+ 	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
+ 	udelay(10);
+-	outb_p(LATCH & 0xff , 0x40);	/* LSB */
++	outb_p((LATCH * timer_mult / 1000) & 0xff , 0x40);	/* LSB */
+ 	udelay(10);
+-	outb(LATCH >> 8 , 0x40);	/* MSB */
++	outb((LATCH * timer_mult / 1000) >> 8 , 0x40);		/* MSB */
+ }
+ 
+ static int timer_resume(struct sys_device *dev)
+diff -Naur 2.6.14/arch/x86_64/kernel/pmtimer.c 2.6.14-pmtmr/arch/x86_64/kernel/pmtimer.c
+--- 2.6.14/arch/x86_64/kernel/pmtimer.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/x86_64/kernel/pmtimer.c	2005-10-30 11:02:30.000000000 +0100
+@@ -29,12 +29,76 @@
+  * in arch/i386/kernel/acpi/boot.c */
+ u32 pmtmr_ioport;
+ 
++extern u16 timer_mult;
++
+ /* value of the Power timer at last timer interrupt */
+ static u32 offset_delay;
+ static u32 last_pmtmr_tick;
+ 
+ #define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
+ 
++#define PMTMR_TICKS_PER_SEC 3579545
++#define PMTMR_EXPECTED_RATE \
++  ((LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (CLOCK_TICK_RATE>>10))
++
++/*helper function to safely read acpi pm timesource*/
++static inline u32 read_pmtmr(void)
++{
++	u32 v1=0,v2=0,v3=0;
++	/* It has been reported that because of various broken
++	 * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time
++	 * source is not latched, so you must read it multiple
++	 * times to insure a safe value is read.
++	 */
++	do {
++		v1 = inl(pmtmr_ioport);
++		v2 = inl(pmtmr_ioport);
++		v3 = inl(pmtmr_ioport);
++	} while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
++			|| (v3 > v1 && v3 < v2));
++
++	/* mask the output to 24 bits */
++	return v2 & ACPI_PM_MASK;
++}
++
++int verify_pmtmr_rate(int override)
++{
++	u32 value1, value2;
++	unsigned long delta;
++	unsigned long count = 0;
++	unsigned long flags;
++
++	outb((inb(0x61) & ~0x02) | 0x01, 0x61);
++	outb(0xb0, 0x43);
++	outb_p(LATCH & 0xff, 0x42);
++	outb_p(LATCH >> 8, 0x42);
++
++	value1 = read_pmtmr();
++
++	do {
++		count++;
++	} while ((inb_p(0x61) & 0x20) == 0);
++
++	value2 = read_pmtmr();
++	delta = (value2 - value1) & ACPI_PM_MASK;
++
++	if (override) {
++		timer_mult = PMTMR_EXPECTED_RATE * 1000 / delta;
++
++		if (timer_mult != 1000)
++			printk(KERN_INFO "PIT running at invalid rate, workarround multiplier set to %d.%d\n",  timer_mult / 1000, timer_mult % 1000);
++	} else
++	/* Check that the PMTMR delta is within 5% of what we expect */
++	if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
++	    delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
++		printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% of normal - aborting.\n", 100UL * delta / PMTMR_EXPECTED_RATE);
++		return -1;
++	}
++
++	return 0;
++}
++
++
+ static inline u32 cyc2us(u32 cycles)
+ {
+ 	/* The Power Management Timer ticks at 3.579545 ticks per microsecond.
+diff -Naur 2.6.14/arch/x86_64/kernel/time.c 2.6.14-pmtmr/arch/x86_64/kernel/time.c
+--- 2.6.14/arch/x86_64/kernel/time.c	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/arch/x86_64/kernel/time.c	2005-10-30 11:02:30.000000000 +0100
+@@ -46,6 +46,12 @@
+ 
+ EXPORT_SYMBOL(jiffies_64);
+ 
++/* 
++ * timer_mult is a mutiplier used to work around some very buggy
++ * hardware where the PIT timer runs way too fast.
++ */
++u16 timer_mult = 1000;
++
+ #ifdef CONFIG_CPU_FREQ
+ static void cpufreq_delayed_get(void);
+ #endif
+@@ -57,6 +63,7 @@
+ 
+ static int nohpet __initdata = 0;
+ static int notsc __initdata = 0;
++static int use_pmtimer __initdata = 0;
+ 
+ #undef HPET_HACK_ENABLE_DANGEROUS
+ 
+@@ -728,6 +735,11 @@
+ 	outb((inb(0x61) & ~0x02) | 0x01, 0x61);
+ 
+ 	outb(0xb0, 0x43);
++
++	/* 
++	 * (PIT_TICK_RATE / (1000 / 50)) is 59659,
++	 *  so we can't use the timer_mult here.
++	 */
+ 	outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
+ 	outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42);
+ 	rdtscll(start);
+@@ -738,7 +750,11 @@
+ 
+ 	spin_unlock_irqrestore(&i8253_lock, flags);
+ 	
+-	return (end - start) / 50;
++	/* 
++	 * It's safer to use timer_mult on the
++	 * computed value.
++	 */
++	return timer_mult * (end - start) / (50 * 1000);
+ }
+ 
+ #ifdef	CONFIG_HPET
+@@ -867,8 +883,8 @@
+ 
+ 	spin_lock_irqsave(&i8253_lock, flags);
+ 	outb_p(0x34, 0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
+-	outb_p(LATCH & 0xff, 0x40);	/* LSB */
+-	outb_p(LATCH >> 8, 0x40);	/* MSB */
++	outb_p((LATCH * timer_mult / 1000) & 0xff, 0x40);	/* LSB */
++	outb_p((LATCH * timer_mult / 1000) >> 8, 0x40);		/* MSB */
+ 	spin_unlock_irqrestore(&i8253_lock, flags);
+ }
+ 
+@@ -913,11 +929,11 @@
+                 vxtime_hz = (1000000000000000L + hpet_period / 2) /
+ 			hpet_period;
+ 
+-	if (hpet_use_timer) {
++	if (hpet_use_timer && !use_pmtimer) {
+ 		cpu_khz = hpet_calibrate_tsc();
+ 		timename = "HPET";
+ #ifdef CONFIG_X86_PM_TIMER
+-	} else if (pmtmr_ioport) {
++	} else if ((pmtmr_ioport) && verify_pmtmr_rate(use_pmtimer)) {
+ 		vxtime_hz = PM_TIMER_FREQUENCY;
+ 		timename = "PM";
+ 		pit_init();
+@@ -1304,4 +1320,13 @@
+ 
+ __setup("notsc", notsc_setup);
+ 
++static int __init pmtmr_setup(char *s)
++{
++	use_pmtimer = 1;
++	return 0;
++}
++
++__setup("pmtmr", pmtmr_setup);
++
++
+ 
+diff -Naur 2.6.14/Documentation/kernel-parameters.txt 2.6.14-pmtmr/Documentation/kernel-parameters.txt
+--- 2.6.14/Documentation/kernel-parameters.txt	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/Documentation/kernel-parameters.txt	2005-10-30 11:03:58.000000000 +0100
+@@ -333,6 +333,10 @@
+ 			Forces specified timesource (if avaliable) to be used
+ 			when calculating gettimeofday(). If specicified
+ 			timesource is not avalible, it defaults to PIT.
++ 			When timesource is set to pmtmr manually, the PM timer is
++ 			assumed to be a reliable time source and the PIT/TSC is
++ 			adjusted accordingly. This can work around some very buggy 
++ 			hardware where the PIT timer runs way too fast.
+ 			Format: { pit | tsc | cyclone | pmtmr }
+ 
+ 	hpet=		[IA-32,HPET] option to disable HPET and use PIT.
+diff -Naur 2.6.14/Documentation/x86_64/boot-options.txt 2.6.14-pmtmr/Documentation/x86_64/boot-options.txt
+--- 2.6.14/Documentation/x86_64/boot-options.txt	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/Documentation/x86_64/boot-options.txt	2005-10-30 11:02:30.000000000 +0100
+@@ -59,6 +59,12 @@
+   This can be used to work around timing problems on multiprocessor systems
+   with not properly synchronized CPUs.
+ 
++
++  pmtmr
++  Assume the PM timer is a reliable time source and adjust the PIT/TSC accordingly.
++  This can work around some very buggy hardware where the PIT timer runs way too
++  fast.
++
+   report_lost_ticks
+   Report when timer interrupts are lost because some code turned off
+   interrupts for too long.
+diff -Naur 2.6.14/include/asm-x86_64/proto.h 2.6.14-pmtmr/include/asm-x86_64/proto.h
+--- 2.6.14/include/asm-x86_64/proto.h	2005-10-28 02:02:08.000000000 +0200
++++ 2.6.14-pmtmr/include/asm-x86_64/proto.h	2005-10-30 11:02:30.000000000 +0100
+@@ -38,6 +38,7 @@
+ extern void time_init_gtod(void);
+ extern int pmtimer_mark_offset(void);
+ extern unsigned int do_gettimeoffset_pm(void);
++extern int verify_pmtmr_rate(int);
+ extern u32 pmtmr_ioport;
+ extern unsigned long long monotonic_base;
+ extern int sysctl_vsyscall;
================================================================



More information about the pld-cvs-commit mailing list