Skip to contentSkip to conversation

cpufreq updates for cpufreq-bk

PATCH New Dominik Brodowski 1 participant 1 message Dec 19, 2003

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 cpufreq updates for cpufreq-bk

The following patches, all tested on two test systems (speedstep-smi and p4-clockmod), are not yet merged into cpufreq-bk. Dave, could you merge them, please? And push them -- along the other pieces in bk already -- to Andrew and Linus for 2.6.1, please?

cpufreq-2.6.0-tsc
- [i386-timing] on P4s no TSC adjustment is necessary

cpufreq-2.6.0-speedstep-smi-disable_debug_output
- [speedstep-smi] Disable debug output

cpufreq-2.6.0-speedstep-smi-spelling_fix
- [speedstep-smi] Spelling fix

cpufreq-2.6.0-speedstep-smi-bugfix-1422
- [speedstep-smi] Disable smi_detect_freqs() call on systems which do not support it [BUG #1422]

cpufreq-2.6.0-speedstep-lib-533mhz_fsb
- [speedstep-lib] Support for 533 MHz FSB

cpufreq-2.6.0-p4_clockmod-detect_speed
- [p4-clockmod] Detect CPU speed without relying on cpu_khz

cpufreq-2.6.0-clean_headers
- [core] remove unneeded #ifdefs in include/linux/cpufreq.h

cpufreq-2.6.0-latency
- [drivers] latency in nanoseconds -- sometimes nanoseconds are used, sometimes microseconds, sometimes even something else.

cpufreq-2.6.0-speedstep-smi-reduce_failures
- [speedstep-smi] Do something about "cpufreq: change failed"

Dominik

There's no need for some #ifdefs in cpufreq.h

 include/linux/cpufreq.h |   10 ++++------
 1 files changed, 4 insertions(+), 6 deletions(-)
include/linux/cpufreq.h +4 -6
diff -ruN linux-original/include/linux/cpufreq.h linux/include/linux/cpufreq.h
@@ -231,17 +231,18 @@
 /* the proc_intf.c needs this */
 int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpufreq_governor **governor);
 
-#if defined(CONFIG_CPU_FREQ_GOV_USERSPACE) || defined(CONFIG_CPU_FREQ_GOV_USERSPACE_MODULE)
+
 /*********************************************************************
  *                      CPUFREQ USERSPACE GOVERNOR                   *
  *********************************************************************/
 int cpufreq_gov_userspace_init(void);
 
+#ifdef CONFIG_CPU_FREQ_24_API
+
 int cpufreq_setmax(unsigned int cpu);
 int cpufreq_set(unsigned int kHz, unsigned int cpu);
 unsigned int cpufreq_get(unsigned int cpu);
 
-#ifdef CONFIG_CPU_FREQ_24_API
 
 /* /proc/sys/cpu */
 enum {
@@ -289,8 +290,6 @@
 
 #endif /* CONFIG_CPU_FREQ_24_API */
 
-#endif /* CONFIG_CPU_FREQ_GOV_USERSPACE */
-
 
 /*********************************************************************
  *                       CPUFREQ DEFAULT GOVERNOR                    *
@@ -305,6 +304,7 @@
 #define CPUFREQ_DEFAULT_GOVERNOR	&cpufreq_gov_userspace
 #endif
 
+
 /*********************************************************************
  *                     FREQUENCY TABLE HELPERS                       *
  *********************************************************************/
@@ -318,7 +318,6 @@
 				    * order */
 };
 
-#if defined(CONFIG_CPU_FREQ_TABLE) || defined(CONFIG_CPU_FREQ_TABLE_MODULE)
 int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
 				    struct cpufreq_frequency_table *table);
 
@@ -340,5 +339,4 @@
 void cpufreq_frequency_table_put_attr(unsigned int cpu);
 
 
-#endif /* CONFIG_CPU_FREQ_TABLE */
 #endif /* _LINUX_CPUFREQ_H */

Even though the core stated that cpuinfo.transition_latency needs to be in 10^(-9) s, hardly any driver set it to nanoseconds but to microseconds. So, fix up the drivers.

 arch/arm/mach-integrator/cpu.c             |    2 +-
 arch/i386/kernel/cpu/cpufreq/acpi.c        |    4 ++--
 arch/i386/kernel/cpu/cpufreq/p4-clockmod.c |    2 +-
 arch/i386/kernel/cpu/cpufreq/powernow-k7.c |    6 +++++-
 arch/i386/kernel/cpu/cpufreq/powernow-k8.c |    7 ++++---
 include/linux/cpufreq.h                    |    2 +-
 6 files changed, 14 insertions(+), 9 deletions(-)
arch/arm/mach-integrator/cpu.c +1 -1
diff -ruN linux-original/arch/arm/mach-integrator/cpu.c linux/arch/arm/mach-integrator/cpu.c
@@ -172,7 +172,7 @@
 	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 	policy->cpuinfo.max_freq = 160000;
 	policy->cpuinfo.min_freq = 12000;
-	policy->cpuinfo.transition_latency = 1000; /* 1 ms, assumed */
+	policy->cpuinfo.transition_latency = 1000000; /* 1 ms, assumed */
 	policy->cur = policy->min = policy->max =
 		icst525_khz(&cclk_params, vco); /* current freq */
 
arch/i386/kernel/cpu/cpufreq/acpi.c +2 -2
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/acpi.c linux/arch/i386/kernel/cpu/cpufreq/acpi.c
@@ -578,8 +578,8 @@
 	/* detect transition latency */
 	policy->cpuinfo.transition_latency = 0;
 	for (i=0;i<perf->state_count;i++) {
-		if (perf->states[i].transition_latency > policy->cpuinfo.transition_latency)
-			policy->cpuinfo.transition_latency = perf->states[i].transition_latency;
+		if ((perf->states[i].transition_latency * 1000) > policy->cpuinfo.transition_latency)
+			policy->cpuinfo.transition_latency = perf->states[i].transition_latency * 1000;
 	}
 	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 	policy->cur = perf->states[pr->limit.state.px].core_frequency * 1000;
arch/i386/kernel/cpu/cpufreq/p4-clockmod.c +1 -1
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c linux/arch/i386/kernel/cpu/cpufreq/p4-clockmod.c
@@ -255,7 +255,7 @@
 	
 	/* cpuinfo and default policy values */
 	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-	policy->cpuinfo.transition_latency = 1000;
+	policy->cpuinfo.transition_latency = 1000000; /* assumed */
 	policy->cur = stock_freq;
 
 	return cpufreq_frequency_table_cpuinfo(policy, &p4clockmod_table[0]);
arch/i386/kernel/cpu/cpufreq/powernow-k7.c +5 -1
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k7.c linux/arch/i386/kernel/cpu/cpufreq/powernow-k7.c
@@ -386,7 +386,11 @@
 				minimum_speed, maximum_speed);
 
 	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-	policy->cpuinfo.transition_latency = latency;
+
+	/* latency is in 10 ns (look for SGTC above) for each VID
+	 * and FID transition, so multiply that value with 20 */
+	policy->cpuinfo.transition_latency = latency * 20;
+
 	policy->cur = maximum_speed;
 
 	return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
arch/i386/kernel/cpu/cpufreq/powernow-k8.c +4 -3
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/powernow-k8.c linux/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
@@ -957,9 +957,10 @@
 
 	pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
 
-	/* Take a crude guess here. */
-	pol->cpuinfo.transition_latency = ((rvo + 8) * vstable * VST_UNITS_20US)
-	    + (3 * (1 << irt) * 10);
+	/* Take a crude guess here. 
+	 * That guess was in microseconds, so multply with 1000 */
+	pol->cpuinfo.transition_latency = (((rvo + 8) * vstable * VST_UNITS_20US)
+	    + (3 * (1 << irt) * 10)) * 1000;
 
 	if (query_current_values_with_pending_wait())
 		return -EIO;
include/linux/cpufreq.h +1 -1
diff -ruN linux-original/include/linux/cpufreq.h linux/include/linux/cpufreq.h
@@ -57,7 +57,7 @@
 struct cpufreq_cpuinfo {
 	unsigned int		max_freq;
 	unsigned int		min_freq;
-	unsigned int		transition_latency; /* in 10^(-9) s */
+	unsigned int		transition_latency; /* in 10^(-9) s = nanoseconds */
 };
 
 struct cpufreq_real_policy {

Other cpufreq drivers don't rely on cpu_khz, but use the "decoded"/theoretical CPU frequency as base for all calculations.

 arch/i386/kernel/cpu/cpufreq/p4-clockmod.c |   66 +++++++++++++++++++++++------
 1 files changed, 53 insertions(+), 13 deletions(-)
arch/i386/kernel/cpu/cpufreq/p4-clockmod.c +53 -13

90 lines not on this page · raw message

I don't know whether there's a P4-M with 533 or 800 MHz FSB out there yet, but let's prepare the speedstep-lib for these FSBs anyways.

 arch/i386/kernel/cpu/cpufreq/speedstep-lib.c |   37 ++++++++++++++++++++++++---
 arch/i386/kernel/cpu/cpufreq/speedstep-lib.h |    2 -
 2 files changed, 35 insertions(+), 4 deletions(-)
arch/i386/kernel/cpu/cpufreq/speedstep-lib.c +34 -3
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c linux/arch/i386/kernel/cpu/cpufreq/speedstep-lib.c
@@ -106,14 +106,45 @@
 
 static unsigned int pentium4_get_frequency(void)
 {
-	u32 msr_lo, msr_hi;
+	struct cpuinfo_x86 *c = &boot_cpu_data;
+	u32 msr_lo, msr_hi, mult;
+	unsigned int fsb = 0;
 
 	rdmsr(0x2c, msr_lo, msr_hi);
 
 	dprintk(KERN_DEBUG "speedstep-lib: P4 - MSR_EBC_FREQUENCY_ID: 0x%x 0x%x\n", msr_lo, msr_hi);
 
-	msr_lo >>= 24;
-	return (msr_lo * 100000);
+	/* decode the FSB: see IA-32 Intel (C) Architecture Software 
+	 * Developer's Manual, Volume 3: System Prgramming Guide,
+	 * revision #12 in Table B-1: MSRs in the Pentium 4 and
+	 * Intel Xeon Processors, on page B-4 and B-5.
+	 */
+	if (c->x86_model < 2)
+		fsb = 100 * 1000;
+	else {
+		u8 fsb_code = (msr_lo >> 16) & 0x7;
+		switch (fsb_code) {
+		case 0:
+			fsb = 100 * 1000;
+			break;
+		case 1:
+			fsb = 13333 * 10;
+			break;
+		case 2:
+			fsb = 200 * 1000;
+			break;
+		}
+	}
+
+	if (!fsb)
+		printk(KERN_DEBUG "speedstep-lib: couldn't detect FSB speed. Please send an e-mail to <[email protected]>\n");
+
+	/* Multiplier. */
+	mult = msr_lo >> 24;
+
+	dprintk(KERN_DEBUG "speedstep-lib: P4 - FSB %u kHz; Multiplier %u\n", fsb, mult);
+
+	return (fsb * mult);
 }
 
  
arch/i386/kernel/cpu/cpufreq/speedstep-lib.h +1 -1
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h linux/arch/i386/kernel/cpu/cpufreq/speedstep-lib.h
@@ -15,7 +15,7 @@
 #define SPEEDSTEP_PROCESSOR_PIII_C_EARLY	0x00000001  /* Coppermine core */
 #define SPEEDSTEP_PROCESSOR_PIII_C		0x00000002  /* Coppermine core */
 #define SPEEDSTEP_PROCESSOR_PIII_T 		0x00000003  /* Tualatin core */
-#define SPEEDSTEP_PROCESSOR_P4M			0x00000004  /* P4-M with 100 MHz FSB */
+#define SPEEDSTEP_PROCESSOR_P4M			0x00000004  /* P4-M  */
 
 /* speedstep states -- only two of them */
 

The SMI call in speedstep_smi_get_freqs causes hangs on certain notebooks. A failure of the function itself is non-critical, as a workaround exists.

 arch/i386/kernel/cpu/cpufreq/speedstep-smi.c |    6 ++++++
 1 files changed, 6 insertions(+)
arch/i386/kernel/cpu/cpufreq/speedstep-smi.c +6 -0
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c linux/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c
@@ -85,6 +85,9 @@
 
 /**
  * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
+ * Only available on later SpeedStep-enabled systems, returns false results or
+ * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing
+ * shows that the latter occurs if !(ist_info.event & 0xFFFF).
  *
  */
 static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
@@ -93,6 +96,9 @@
 	u32 state=0;
 	u32 function = GET_SPEEDSTEP_FREQS;
 
+	if (!(ist_info.event & 0xFFFF))
+		return -ENODEV;
+
 	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
 
 	__asm__ __volatile__("movl $0, %%edi\n"

speedstep-smi is getting more and more stable, so disable verbose debug output.

 arch/i386/kernel/cpu/cpufreq/speedstep-smi.c |    2 +-
 1 files changed, 1 insertion(+), 1 deletion(-)
arch/i386/kernel/cpu/cpufreq/speedstep-smi.c +1 -1
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c linux/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c
@@ -54,7 +54,7 @@
 /* DEBUG
  *   Define it if you want verbose debug output, e.g. for bug reporting
  */
-#define SPEEDSTEP_DEBUG
+//#define SPEEDSTEP_DEBUG
 
 #ifdef SPEEDSTEP_DEBUG
 #define dprintk(msg...) printk(msg)

Transitions initiated by speedstep-smi currently fail often if system load is high. Bruno Ducrot and I have found out that the failures disappear if DMA is disabled. As disabling (U)DMA isn't something nice, here's a workaround which (almost) always works: retrying the SMI call up to five times, and waiting some time before doing so. Please note that this will completely mess up real-time-critical tasks or dynamic scaling. But speedstep-smi (1st generation of speedstep) wasn't meant to be used for that anyway.

 arch/i386/kernel/cpu/cpufreq/speedstep-smi.c |   32 +++++++++++++++++++--------
 1 files changed, 23 insertions(+), 9 deletions(-)
arch/i386/kernel/cpu/cpufreq/speedstep-smi.c +23 -9
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c linux/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c
@@ -19,6 +19,7 @@
 #include <linux/cpufreq.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
 #include <asm/ist.h>
 
 #include "speedstep-lib.h"
@@ -51,6 +52,10 @@
 #define SET_SPEEDSTEP_STATE 2
 #define GET_SPEEDSTEP_FREQS 4
 
+/* how often shall the SMI call be tried if it failed, e.g. because
+ * of DMA activity going on? */
+#define SMI_TRIES 5
+
 /* DEBUG
  *   Define it if you want verbose debug output, e.g. for bug reporting
  */
@@ -140,10 +145,11 @@
  */
 static void speedstep_set_state (unsigned int state, unsigned int notify)
 {
-	unsigned int old_state, result, command, new_state;
+	unsigned int old_state, result = 0, command, new_state;
 	unsigned long flags;
 	struct cpufreq_freqs freqs;
 	unsigned int function=SET_SPEEDSTEP_STATE;
+	unsigned int retry = 0;
 
 	if (state > 0x1)
 		return;
@@ -163,20 +169,28 @@
 	local_irq_save(flags);
 
 	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);
-	__asm__ __volatile__(
-		"movl $0, %%edi\n"
-		"out %%al, (%%dx)\n"
-		: "=b" (new_state), "=D" (result)
-		: "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
-	);
+
+	do {
+		if (retry) {
+			dprintk(KERN_INFO "cpufreq: retry %u, previous result %u\n", retry, result);
+			mdelay(retry * 50);
+		}
+		retry++;
+		__asm__ __volatile__(
+			"movl $0, %%edi\n"
+			"out %%al, (%%dx)\n"
+			: "=b" (new_state), "=D" (result)
+			: "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
+			);
+	} while ((new_state != state) && (retry <= SMI_TRIES));
 
 	/* enable IRQs */
 	local_irq_restore(flags);
 
 	if (new_state == state) {
-		dprintk(KERN_INFO "cpufreq: change to %u MHz succeeded\n", (freqs.new / 1000));
+		dprintk(KERN_INFO "cpufreq: change to %u MHz succeeded after %u tries with result %u\n", (freqs.new / 1000), retry, result);
 	} else {
-		printk(KERN_ERR "cpufreq: change failed\n");
+		printk(KERN_ERR "cpufreq: change failed with new_state %u and result %u\n", new_state, result);
 	}
 
 	if (notify)

The reporter of bug 1422 noted this spelling fix -- thanks!

 arch/i386/kernel/cpu/cpufreq/speedstep-smi.c |    2 +-
 1 files changed, 1 insertion(+), 1 deletion(-)
arch/i386/kernel/cpu/cpufreq/speedstep-smi.c +1 -1
diff -ruN linux-original/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c linux/arch/i386/kernel/cpu/cpufreq/speedstep-smi.c
@@ -168,7 +168,7 @@
 	local_irq_restore(flags);
 
 	if (new_state == state) {
-		dprintk(KERN_INFO "cpufreq: change to %u MHz succeded\n", (freqs.new / 1000));
+		dprintk(KERN_INFO "cpufreq: change to %u MHz succeeded\n", (freqs.new / 1000));
 	} else {
 		printk(KERN_ERR "cpufreq: change failed\n");
 	}
Quoted 3 lines · Shaohua

TSC frequency doesn't change under P4 and above when CPU frequency changes, which caused some problems. Attached patch will handle this problem. How about it?

A few cleanups were added to this patch:
- cpufreq_register_notifier() shouldn't be called during time_init() because it shouldn't be called while we're in atomic context
- the p4 detection code should only be used when cpufreq is used
- also cyc2us and loops_per_jiffy are affected (I think)
- the patch had an invalid header

 timer_tsc.c |   35 +++++++++++++++++++++++++++--------
 1 files changed, 27 insertions(+), 8 deletions(-)
arch/i386/kernel/timers/timer_tsc.c +27 -8
diff -ruN linux-original/arch/i386/kernel/timers/timer_tsc.c linux/arch/i386/kernel/timers/timer_tsc.c
@@ -306,9 +306,17 @@
 }
 #endif
 
+
 #ifdef CONFIG_CPU_FREQ
+/* 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. 
+ */
+
 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;
@@ -333,12 +341,15 @@
 
 	if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) ||
 	    (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) {
-		cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
+		if (variable_tsc)
+			cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
 #ifndef CONFIG_SMP
 		if (use_tsc) {
-			fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
 			cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
-			set_cyc2ns_scale(cpu_khz/1000);
+			if (variable_tsc) {
+				fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
+				set_cyc2ns_scale(cpu_khz/1000);
+			}
 		}
 #endif
 	}
@@ -350,7 +361,19 @@
 static struct notifier_block time_cpufreq_notifier_block = {
 	.notifier_call	= time_cpufreq_notifier
 };
-#endif
+
+
+static int __init cpufreq_tsc(void)
+{
+	/* 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);
+}
+core_initcall(cpufreq_tsc);
+
+#endif 
 
 
 static int __init init_tsc(char* override)
@@ -392,10 +415,6 @@
  	 *	some CPU's have a TSC. Thats never worked and nobody has
  	 *	moaned if you have the only one in the world - you fix it!
  	 */
- 
-#ifdef CONFIG_CPU_FREQ
-	cpufreq_register_notifier(&time_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
-#endif
 
 	count2 = LATCH; /* initialize counter for mark_offset_tsc() */
 

_______________________________________________
Cpufreq mailing list
[email protected]
http://www.linux.org.uk/mailman/listinfo/cpufreq