Commit 314a29b2 authored by Dave Jones's avatar Dave Jones Committed by Dave Jones

[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
parent 460d0bf0
...@@ -51,6 +51,7 @@ enum { ...@@ -51,6 +51,7 @@ enum {
static int has_N44_O17_errata[NR_CPUS]; static int has_N44_O17_errata[NR_CPUS];
static unsigned int stock_freq; static unsigned int stock_freq;
static struct cpufreq_driver p4clockmod_driver;
static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) 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) ...@@ -193,6 +194,10 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c)
return 0; 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) { if (speedstep_detect_processor() == SPEEDSTEP_PROCESSOR_P4M) {
printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. " printk(KERN_WARNING PFX "Warning: Pentium 4-M detected. "
"The speedstep-ich or acpi cpufreq modules offer " "The speedstep-ich or acpi cpufreq modules offer "
......
...@@ -353,14 +353,11 @@ static inline void cpufreq_delayed_get(void) ...@@ -353,14 +353,11 @@ static inline void cpufreq_delayed_get(void)
} }
/* If the CPU frequency is scaled, TSC-based delays will need a different /* If the CPU frequency is scaled, TSC-based delays will need a different
* loops_per_jiffy value to function properly. An exception to this * loops_per_jiffy value to function properly.
* are modern Intel Pentium 4 processors, where the TSC runs at a constant
* speed independent of frequency scaling.
*/ */
static unsigned int ref_freq = 0; static unsigned int ref_freq = 0;
static unsigned long loops_per_jiffy_ref = 0; static unsigned long loops_per_jiffy_ref = 0;
static unsigned int variable_tsc = 1;
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
static unsigned long fast_gettimeoffset_ref = 0; static unsigned long fast_gettimeoffset_ref = 0;
...@@ -386,13 +383,13 @@ time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, ...@@ -386,13 +383,13 @@ time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
(val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
(val == CPUFREQ_RESUMECHANGE)) { (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); cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
#ifndef CONFIG_SMP #ifndef CONFIG_SMP
if (cpu_khz) if (cpu_khz)
cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new); cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
if (use_tsc) { if (use_tsc) {
if (variable_tsc) { if (!freq->flags & CPUFREQ_CONST_LOOPS) {
fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq); fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
set_cyc2ns_scale(cpu_khz/1000); set_cyc2ns_scale(cpu_khz/1000);
} }
...@@ -411,13 +408,12 @@ static struct notifier_block time_cpufreq_notifier_block = { ...@@ -411,13 +408,12 @@ static struct notifier_block time_cpufreq_notifier_block = {
static int __init cpufreq_tsc(void) static int __init cpufreq_tsc(void)
{ {
int ret;
INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL); INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL);
ret = cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
if (!ret)
cpufreq_init = 1; cpufreq_init = 1;
/* P4 and above CPU TSC freq doesn't change when CPU frequency changes*/ return ret;
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);
} }
core_initcall(cpufreq_tsc); core_initcall(cpufreq_tsc);
......
...@@ -1018,7 +1018,7 @@ static unsigned int l_p_j_ref_freq; ...@@ -1018,7 +1018,7 @@ static unsigned int l_p_j_ref_freq;
static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) 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; return;
if (!l_p_j_ref_freq) { if (!l_p_j_ref_freq) {
...@@ -1045,6 +1045,8 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state) ...@@ -1045,6 +1045,8 @@ void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
{ {
BUG_ON(irqs_disabled()); BUG_ON(irqs_disabled());
freqs->flags = cpufreq_driver->flags;
down_read(&cpufreq_notifier_rwsem); down_read(&cpufreq_notifier_rwsem);
switch (state) { switch (state) {
case CPUFREQ_PRECHANGE: case CPUFREQ_PRECHANGE:
......
...@@ -106,6 +106,7 @@ struct cpufreq_freqs { ...@@ -106,6 +106,7 @@ struct cpufreq_freqs {
unsigned int cpu; /* cpu nr */ unsigned int cpu; /* cpu nr */
unsigned int old; unsigned int old;
unsigned int new; unsigned int new;
u8 flags; /* flags of cpufreq_driver, see below. */
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment