From 314a29b2b8899a02cbdbd6932293a386c446564f Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 7 May 2004 17:45:54 +0100 Subject: [PATCH] [CPUFREQ] Handle P4 TSC scaling. Currently, the TSC cpufreq notifiers does almost nothing on P4s, as we assumed the TSC to be constant independent of _all_ frequency transitions. Extensive testing by Karol Kozimor has shown, though, that only _throttling_ does not affect the TSC, but _scaling_ does. So: - pass the CPUFREQ_CONST_LOOPS flags (to be exact, all flags) to cpufreq transition notifiers - skip TSC value changes if this flag is set - set this flag for P4 / P4-Ms only in p4-clockmod [On Pentium-M banias the TSC _is_ affected by p4-clock modulation --- arch/i386/kernel/cpu/cpufreq/p4-clockmod.c | 5 +++++ arch/i386/kernel/timers/timer_tsc.c | 20 ++++++++------------ drivers/cpufreq/cpufreq.c | 4 +++- include/linux/cpufreq.h | 1 + 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c index a9be690b46111..ec08f2ef12e89 100644 --- a/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c +++ b/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c @@ -51,6 +51,7 @@ enum { static int has_N44_O17_errata[NR_CPUS]; static unsigned int stock_freq; +static struct cpufreq_driver p4clockmod_driver; static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) { @@ -193,6 +194,10 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) return 0; } + /* on P-4s, the TSC runs with constant frequency independent wether + * throttling is active or not. */ + p4clockmod_driver.flags |= CPUFREQ_CONST_LOOPS; + if (speedstep_detect_processor() == SPEEDSTEP_PROCESSOR_P4M) { printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. " "The speedstep-ich or acpi cpufreq modules offer " diff --git a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c index 1034c6c1bc858..532ef1d2d74fc 100644 --- a/arch/i386/kernel/timers/timer_tsc.c +++ b/arch/i386/kernel/timers/timer_tsc.c @@ -353,14 +353,11 @@ static inline void cpufreq_delayed_get(void) } /* If the CPU frequency is scaled, TSC-based delays will need a different - * loops_per_jiffy value to function properly. An exception to this - * are modern Intel Pentium 4 processors, where the TSC runs at a constant - * speed independent of frequency scaling. + * loops_per_jiffy value to function properly. */ static unsigned int ref_freq = 0; static unsigned long loops_per_jiffy_ref = 0; -static unsigned int variable_tsc = 1; #ifndef CONFIG_SMP static unsigned long fast_gettimeoffset_ref = 0; @@ -386,13 +383,13 @@ time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || (val == CPUFREQ_RESUMECHANGE)) { - if (variable_tsc) + if (!freq->flags & CPUFREQ_CONST_LOOPS) cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new); #ifndef CONFIG_SMP if (cpu_khz) cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new); if (use_tsc) { - if (variable_tsc) { + if (!freq->flags & CPUFREQ_CONST_LOOPS) { fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq); set_cyc2ns_scale(cpu_khz/1000); } @@ -411,13 +408,12 @@ static struct notifier_block time_cpufreq_notifier_block = { static int __init cpufreq_tsc(void) { + int ret; INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL); - cpufreq_init = 1; - /* P4 and above CPU TSC freq doesn't change when CPU frequency changes*/ - if ((boot_cpu_data.x86 >= 15) && (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)) - variable_tsc = 0; - - return cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + ret = cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); + if (!ret) + cpufreq_init = 1; + return ret; } core_initcall(cpufreq_tsc); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index c345cb233cf1e..912ce70348da3 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1018,7 +1018,7 @@ static unsigned int l_p_j_ref_freq; static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { - if (cpufreq_driver->flags & CPUFREQ_CONST_LOOPS) + if (ci->flags & CPUFREQ_CONST_LOOPS) return; if (!l_p_j_ref_freq) { @@ -1045,6 +1045,8 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) { BUG_ON(irqs_disabled()); + freqs->flags = cpufreq_driver->flags; + down_read(&cpufreq_notifier_rwsem); switch (state) { case CPUFREQ_PRECHANGE: diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 36f58874788f3..b4204dc7e304f 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -106,6 +106,7 @@ struct cpufreq_freqs { unsigned int cpu; /* cpu nr */ unsigned int old; unsigned int new; + u8 flags; /* flags of cpufreq_driver, see below. */ }; -- 2.53.0