From: john stultz Here are my fixes to Dominik's patch. It cleans up some of the math, adds lost-tick compensation and monotonic_clock, fixes up a bit of the ACPI table mapping, and moves the config option under the ACPI menu. 25-akpm/Documentation/kernel-parameters.txt | 2 25-akpm/arch/i386/Kconfig | 18 ----- 25-akpm/arch/i386/kernel/acpi/boot.c | 10 ++- 25-akpm/arch/i386/kernel/timers/timer_pm.c | 93 ++++++++++++++++++++++------ 25-akpm/drivers/acpi/Kconfig | 19 +++++ 5 files changed, 103 insertions(+), 39 deletions(-) diff -puN arch/i386/Kconfig~acpi-pm-timer-fixes arch/i386/Kconfig --- 25/arch/i386/Kconfig~acpi-pm-timer-fixes Fri Nov 7 13:25:03 2003 +++ 25-akpm/arch/i386/Kconfig Fri Nov 7 13:29:07 2003 @@ -411,24 +411,6 @@ config HPET_TIMER config HPET_EMULATE_RTC def_bool HPET_TIMER && RTC=y -config X86_PM_TIMER - bool "Power Management Timer Support" - depends on (!X86_VISWS && !X86_VISWS) && EXPERIMENTAL - default n - help - The Power Management Timer is available on all ACPI-capable, - in most cases even if ACPI is unusable or blacklisted. - - This timing source is not affected by powermanagement features - like aggressive processor idling, throttling, frequency and/or - voltage scaling, unlike the commonly used Time Stamp Counter - (TSC) timing source. - - So, if you see messages like 'Losing too many ticks!' in the - kernel logs, and/or you are using a this on a notebook which - does not yet have an HPET (see above), you should say "Y" - here. Otherwise, say "N". - config SMP bool "Symmetric multi-processing support" ---help--- diff -puN arch/i386/kernel/acpi/boot.c~acpi-pm-timer-fixes arch/i386/kernel/acpi/boot.c --- 25/arch/i386/kernel/acpi/boot.c~acpi-pm-timer-fixes Fri Nov 7 13:25:03 2003 +++ 25-akpm/arch/i386/kernel/acpi/boot.c Fri Nov 7 13:25:10 2003 @@ -320,15 +320,19 @@ static int __init acpi_parse_hpet(unsign } #endif -/* detect the location of the ACPI PM Timer +/* detect the location of the ACPI PM Timer */ #ifdef CONFIG_X86_PM_TIMER extern u32 pmtmr_ioport; static int __init acpi_parse_fadt(unsigned long phys, unsigned long size) { - struct fadt_descriptor_rev2 *fadt; + struct fadt_descriptor_rev2 *fadt =0; - fadt = __va(phys); + fadt = (struct fadt_descriptor_rev2*) __acpi_map_table(phys,size); + if(!fadt) { + printk(KERN_WARNING PREFIX "Unable to map FADT\n"); + return 0; + } if (fadt->revision >= FADT2_REVISION_ID) { /* FADT rev. 2 */ diff -puN arch/i386/kernel/timers/timer_pm.c~acpi-pm-timer-fixes arch/i386/kernel/timers/timer_pm.c --- 25/arch/i386/kernel/timers/timer_pm.c~acpi-pm-timer-fixes Fri Nov 7 13:25:03 2003 +++ 25-akpm/arch/i386/kernel/timers/timer_pm.c Fri Nov 7 13:27:35 2003 @@ -30,7 +30,12 @@ u32 pmtmr_ioport = 0; /* value of the Power timer at last timer interrupt */ static u32 offset_tick; +static u32 offset_delay; +static unsigned long long monotonic_base; +static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED; + +#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */ static int init_pmtmr(char* override) { @@ -62,24 +67,89 @@ static int init_pmtmr(char* override) return -ENODEV; } +static inline u32 cyc2us(u32 cycles) +{ + /* The Power Management Timer ticks at 3.579545 ticks per microsecond. + * 1 / PM_TIMER_FREQUENCY == 0.27936511 =~ 286/1024 [error: 0.024%] + * + * Even with HZ = 100, delta is at maximum 35796 ticks, so it can + * easily be multiplied with 286 (=0x11E) without having to fear + * u32 overflows. + */ + cycles *= 286; + return (cycles >> 10); +} /* * this gets called during each timer interrupt */ static void mark_offset_pmtmr(void) { + u32 lost, delta, last_offset; + static int first_run = 1; + last_offset = offset_tick; + + write_seqlock(&monotonic_lock); + offset_tick = inl(pmtmr_ioport); - offset_tick &= 0xFFFFFF; /* limit it to 24 bits */ + offset_tick &= ACPI_PM_MASK; /* limit it to 24 bits */ + + /* calculate tick interval */ + delta = (offset_tick - last_offset) & ACPI_PM_MASK; + + /* convert to usecs */ + delta = offset_delay + cyc2us(delta); + + /* convert to ticks */ + lost = delta/(USEC_PER_SEC/HZ); + offset_delay = delta%(USEC_PER_SEC/HZ); + + /* update the monotonic base value */ + monotonic_base += delta; + write_sequnlock(&monotonic_lock); + + + /* compensate for lost ticks */ + if (lost >= 2) + jiffies += lost - 1; + + /* don't calculate delay for first run, + or if we've got less then a tick */ + if (first_run || (lost < 1)) { + first_run = 0; + offset_delay = 0; + } + return; } static unsigned long long monotonic_clock_pmtmr(void) { - return 0; -} + u32 last_offset, this_offset; + unsigned long long base, ret; + unsigned seq; + + + /* atomically read monotonic base & last_offset */ + do { + seq = read_seqbegin(&monotonic_lock); + last_offset = offset_tick; + base = monotonic_base; + } while (read_seqretry(&monotonic_lock, seq)); + + + /* Read the cyclone counter */ + + this_offset = inl(pmtmr_ioport) & ACPI_PM_MASK; + /* convert to nanoseconds */ + ret = base + ((this_offset - last_offset) & ACPI_PM_MASK); + ret = cyc2us(ret)*1000; + return ret; +} + /* * copied from delay_pit */ @@ -106,21 +176,10 @@ static unsigned long get_offset_pmtmr(vo offset = offset_tick; now = inl(pmtmr_ioport); - now &= 0xFFFFFF; - if (likely(offset < now)) - delta = now - offset; - else if (offset > now) - delta = (0xFFFFFF - offset) + now; + now &= ACPI_PM_MASK; + delta = (now - offset)&ACPI_PM_MASK; - /* The Power Management Timer ticks at 3.579545 ticks per microsecond. - * 1 / PM_TIMER_FREQUENCY == 0.27936511 =~ 286/1024 [error: 0.024%] - * - * Even with HZ = 100, delta is at maximum 35796 ticks, so it can - * easily be multiplied with 286 (=0x11E) without having to fear - * u32 overflows. - */ - delta *= 286; - return (unsigned long) (delta >> 10); + return (unsigned long) offset_delay + cyc2us(delta); } diff -puN Documentation/kernel-parameters.txt~acpi-pm-timer-fixes Documentation/kernel-parameters.txt --- 25/Documentation/kernel-parameters.txt~acpi-pm-timer-fixes Fri Nov 7 13:25:03 2003 +++ 25-akpm/Documentation/kernel-parameters.txt Fri Nov 7 13:25:03 2003 @@ -214,7 +214,7 @@ running once the system is up. Forces specified timesource (if avaliable) to be used when calculating gettimeofday(). If specicified timesource is not avalible, it defaults to PIT. - Format: { pit | tsc | cyclone | ... } + Format: { pit | tsc | cyclone | pmtmr } hpet= [IA-32,HPET] option to disable HPET and use PIT. Format: disable diff -puN drivers/acpi/Kconfig~acpi-pm-timer-fixes drivers/acpi/Kconfig --- 25/drivers/acpi/Kconfig~acpi-pm-timer-fixes Fri Nov 7 13:25:03 2003 +++ 25-akpm/drivers/acpi/Kconfig Fri Nov 7 13:25:10 2003 @@ -263,5 +263,24 @@ config ACPI_RELAXED_AML particular, many Toshiba laptops require this for correct operation of the AC module. +config X86_PM_TIMER + bool "Power Management Timer Support" + depends on X86 && ACPI + depends on ACPI_BOOT && EXPERIMENTAL + default n + help + The Power Management Timer is available on all ACPI-capable, + in most cases even if ACPI is unusable or blacklisted. + + This timing source is not affected by powermanagement features + like aggressive processor idling, throttling, frequency and/or + voltage scaling, unlike the commonly used Time Stamp Counter + (TSC) timing source. + + So, if you see messages like 'Losing too many ticks!' in the + kernel logs, and/or you are using a this on a notebook which + does not yet have an HPET (see above), you should say "Y" + here. + endmenu _