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