bk://linux-dj.bkbits.net/cpufreq
davej@redhat.com|ChangeSet|20040419082344|03690 davej

# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2004/04/19 09:23:44+01:00 davej@redhat.com 
#   [CPUFREQ] Fix broken cast.
#   This breaks on x86-64 with the following warning.
#   
#   drivers/cpufreq/cpufreq_userspace.c: In function `cpufreq_procctl':
#   drivers/cpufreq/cpufreq_userspace.c:170: warning: cast from pointer to integer of different size
#   drivers/cpufreq/cpufreq_userspace.c: In function `cpufreq_sysctl':
#   drivers/cpufreq/cpufreq_userspace.c:208: warning: cast from pointer to integer of different size
# 
# drivers/cpufreq/cpufreq_userspace.c
#   2004/04/19 09:23:38+01:00 davej@redhat.com +2 -2
#   [CPUFREQ] Fix broken cast.
#   This breaks on x86-64 with the following warning.
#   
#   drivers/cpufreq/cpufreq_userspace.c: In function `cpufreq_procctl':
#   drivers/cpufreq/cpufreq_userspace.c:170: warning: cast from pointer to integer of different size
#   drivers/cpufreq/cpufreq_userspace.c: In function `cpufreq_sysctl':
#   drivers/cpufreq/cpufreq_userspace.c:208: warning: cast from pointer to integer of different size
# 
# ChangeSet
#   2004/04/19 09:20:19+01:00 davej@redhat.com 
#   [CPUFREQ] Fix up missing CONFIG_X86_POWERNOW_K8_ACPI 
#   We don't need this, we can infer from CONFIG_ACPI_PROCESSOR
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k8.h
#   2004/04/19 09:20:13+01:00 davej@redhat.com +1 -1
#   [CPUFREQ] Fix up missing CONFIG_X86_POWERNOW_K8_ACPI 
#   We don't need this, we can infer from CONFIG_ACPI_PROCESSOR
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k8.c
#   2004/04/19 09:20:13+01:00 davej@redhat.com +2 -2
#   [CPUFREQ] Fix up missing CONFIG_X86_POWERNOW_K8_ACPI 
#   We don't need this, we can infer from CONFIG_ACPI_PROCESSOR
# 
# ChangeSet
#   2004/04/19 08:08:52+01:00 davej@redhat.com 
#   [CPUFREQ] Fix debug build of powernow-k8
#   From Paul Devriendt
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k8.c
#   2004/04/19 08:08:46+01:00 davej@redhat.com +1 -1
#   [CPUFREQ] Fix debug build of powernow-k8
#   From Paul Devriendt
# 
# ChangeSet
#   2004/04/16 16:41:34+01:00 davej@redhat.com 
#   [CPUFREQ] Not all powernow-K7 BIOS's put the frequency at MAX at POST.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/16 16:41:26+01:00 davej@redhat.com +1 -1
#   [CPUFREQ] Not all powernow-K7 BIOS's put the frequency at MAX at POST.
# 
# ChangeSet
#   2004/04/14 22:26:47+01:00 davej@redhat.com 
#   [CPUFREQ] clear defaults before powernow-k7 acpi fallback
#   Decoding the legacy tables may have set these values.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 22:26:42+01:00 davej@redhat.com +3 -0
#   [CPUFREQ] clear defaults before powernow-k7 acpi fallback
#   Decoding the legacy tables may have set these values.
# 
# ChangeSet
#   2004/04/14 22:22:30+01:00 davej@redhat.com 
#   [CPUFREQ] powernow-k7 ACPI->PST values were a factor of 10 off.
#   As much as I like the idea of a 13GHz laptop, setting it to 1.3GHz is probably
#   for the best for the time being.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 22:22:22+01:00 davej@redhat.com +1 -1
#   [CPUFREQ] powernow-k7 ACPI->PST values were a factor of 10 off.
#   As much as I like the idea of a 13GHz laptop, setting it to 1.3GHz is probably
#   for the best for the time being.
# 
# ChangeSet
#   2004/04/14 22:20:31+01:00 davej@redhat.com 
#   [CPUFREQ] Make powernow-k7 acpi debug output a little less verbose.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 22:20:25+01:00 davej@redhat.com +1 -4
#   [CPUFREQ] Make powernow-k7 acpi debug output a little less verbose.
# 
# ChangeSet
#   2004/04/14 22:18:39+01:00 davej@redhat.com 
#   [CPUFREQ] Add a module parameter to force ACPI to be used.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 22:18:33+01:00 davej@redhat.com +12 -1
#   [CPUFREQ] Add a module parameter to force ACPI to be used.
# 
# ChangeSet
#   2004/04/14 18:21:08+01:00 davej@redhat.com 
#   [CPUFREQ] Remove bogus newline in powernow-k7 driver.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 18:21:02+01:00 davej@redhat.com +0 -2
#   [CPUFREQ] Remove bogus newline in powernow-k7 driver.
# 
# ChangeSet
#   2004/04/14 15:15:56+01:00 davej@redhat.com 
#   [CPUFREQ] powernow-k7 needs to init later.
#   Due to the possible dependancy on ACPI.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 15:15:50+01:00 davej@redhat.com +1 -1
#   [CPUFREQ] powernow-k7 needs to init later.
#   Due to the possible dependancy on ACPI.
# 
# ChangeSet
#   2004/04/14 15:14:35+01:00 davej@redhat.com 
#   [CPUFREQ] Drop unneeded part of last patch.
#   acpi fallback is handled already in the init function.
#   
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 15:14:29+01:00 davej@redhat.com +6 -7
#   [CPUFREQ] Drop unneeded part of last patch.
#   acpi fallback is handled already in the init function.
#   
# 
# ChangeSet
#   2004/04/14 13:26:15+01:00 davej@redhat.com 
#   [CPUFREQ] powernow-k7 ACPI integration.
#   More from Bruno Ducrot.
#   
#   Warning: it will only half work on the ACER Aspire, though: there is
#   no pstate in the DSDT corresponding to the max frequency...
#   I'm looking how to handle that correctly, probably at the init stage,
#   if the max frequency is not given, then add it to powernow_table.
#   
#   * Integrate acpi perflib from Dominik.
#   * Use acpi if the PST tables are known to be broken (ASUS Aspire match one PST,
#     but give brain damaged values), or use ACPI if no PST found.
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 13:26:09+01:00 davej@redhat.com +168 -12
#   [CPUFREQ] powernow-k7 ACPI integration.
#   More from Bruno Ducrot.
#   
#   Warning: it will only half work on the ACER Aspire, though: there is
#   no pstate in the DSDT corresponding to the max frequency...
#   I'm looking how to handle that correctly, probably at the init stage,
#   if the max frequency is not given, then add it to powernow_table.
#   
#   * Integrate acpi perflib from Dominik.
#   * Use acpi if the PST tables are known to be broken (ASUS Aspire match one PST,
#     but give brain damaged values), or use ACPI if no PST found.
# 
# ChangeSet
#   2004/04/14 12:22:15+01:00 davej@redhat.com 
#   [CPUFREQ] don't use speedstep-centrino on unsupported CPUs
#   
#   From: Dominik Brodowski <linux@dominikbrodowski.de>
#   
#   Don't use the ACPI data on CPUs we don't know nothing about.
# 
# arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
#   2004/04/14 12:20:07+01:00 davej@redhat.com +16 -15
#   [CPUFREQ] don't use speedstep-centrino on unsupported CPUs
#   
#   From: Dominik Brodowski <linux@dominikbrodowski.de>
#   
#   Don't use the ACPI data on CPUs we don't know nothing about.
# 
# ChangeSet
#   2004/04/14 11:40:42+01:00 davej@redhat.com 
#   [CPUFREQ] Correcting SGTC.  Timer is based upon FSB
#   From: Bruno Ducrot <ducrot@poupinou.org>
#   
#   I think this patch is needed especially if the FSB is around 166MHz or
#   200MHz, or else I believe we get instabilities on some K7's motherboard
#   powernow capable (it's called Cool'n Quiet IIRC).
#                                                                                            
#                                                                                            
#   * Deduce fsb from cpu_khz and the max multiplier.  It will be given as kHz now,
#     so that frequency associated to a multiplier will be computate more
#     accurately.  Also, we need it for SGTC (see below).
#   * Fix how cpuid is computed in powernow_decode_bios().
#   * Be more restrictive for PST.  It may be possible (on desktop shipped with
#     low power Athlon models) that FSB can be changed by dip switchs on
#     motherboard for example.
#   * Fix computation for SGTC.  It use the bus timer (and then the bus
#     frequency given by fsb).
#   
# 
# arch/i386/kernel/cpu/cpufreq/powernow-k7.c
#   2004/04/14 11:40:36+01:00 davej@redhat.com +59 -16
#   [CPUFREQ] Correcting SGTC.  Timer is based upon FSB
#   From: Bruno Ducrot <ducrot@poupinou.org>
#   
#   I think this patch is needed especially if the FSB is around 166MHz or
#   200MHz, or else I believe we get instabilities on some K7's motherboard
#   powernow capable (it's called Cool'n Quiet IIRC).
#                                                                                            
#                                                                                            
#   * Deduce fsb from cpu_khz and the max multiplier.  It will be given as kHz now,
#     so that frequency associated to a multiplier will be computate more
#     accurately.  Also, we need it for SGTC (see below).
#   * Fix how cpuid is computed in powernow_decode_bios().
#   * Be more restrictive for PST.  It may be possible (on desktop shipped with
#     low power Athlon models) that FSB can be changed by dip switchs on
#     motherboard for example.
#   * Fix computation for SGTC.  It use the bus timer (and then the bus
#     frequency given by fsb).
#   
# 
# ChangeSet
#   2004/04/13 10:02:09-07:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-cpufreq
# 
# arch/i386/kernel/cpu/cpufreq/Kconfig
#   2004/04/13 10:02:06-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
# ChangeSet
#   2004/04/12 20:52:00-07:00 akpm@bix.(none) 
#   Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-cpufreq
# 
# arch/i386/kernel/cpu/cpufreq/Kconfig
#   2004/04/12 20:51:56-07:00 akpm@bix.(none) +0 -0
#   Auto merged
# 
diff -Nru a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c
--- a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c	Wed Apr 21 00:30:54 2004
+++ b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c	Wed Apr 21 00:30:54 2004
@@ -17,6 +17,7 @@
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h> 
+#include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
@@ -27,6 +28,11 @@
 #include <asm/io.h>
 #include <asm/system.h>
 
+#ifdef CONFIG_ACPI_PROCESSOR
+#include <linux/acpi.h>
+#include <acpi/processor.h>
+#endif
+
 #include "powernow-k7.h"
 
 #define DEBUG
@@ -57,6 +63,17 @@
 	u8 numpstates;
 };
 
+#ifdef CONFIG_ACPI_PROCESSOR
+union powernow_acpi_control_t {
+	struct {
+		unsigned long fid:5,
+		vid:5,
+		sgtc:20,
+		res1:2;
+	} bits;
+	unsigned long val;
+};
+#endif
 
 /* divide by 1000 to get VID. */
 static int mobile_vid_table[32] = {
@@ -74,6 +91,12 @@
     150, 225, 160, 165, 170, 180, -1, -1,
 };
 
+/* This parameter is used in order to force ACPI instead of legacy method for
+ * configuration purpose.
+ */
+
+static int powernow_acpi_force;
+
 static struct cpufreq_frequency_table *powernow_table;
 
 static unsigned int can_scale_bus;
@@ -85,6 +108,14 @@
 static unsigned int latency;
 static char have_a0;
 
+static int check_fsb(unsigned int fsbspeed)
+{
+	int delta;
+	unsigned int f = fsb / 1000;
+
+	delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
+	return (delta < 5);
+}
 
 static int check_powernow(void)
 {
@@ -140,7 +171,8 @@
 
 static int get_ranges (unsigned char *pst)
 {
-	unsigned int j, speed;
+	unsigned int j;
+	unsigned int speed;
 	u8 fid, vid;
 
 	powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
@@ -151,12 +183,12 @@
 	for (j=0 ; j < number_scales; j++) {
 		fid = *pst++;
 
-		powernow_table[j].frequency = fsb * fid_codes[fid] * 100;
+		powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
 		powernow_table[j].index = fid; /* lower 8 bits */
 
-		speed = fsb * (fid_codes[fid]/10);
+		speed = powernow_table[j].frequency;
+
 		if ((fid_codes[fid] % 10)==5) {
-			speed += fsb/2;
 #if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
 			if (have_a0 == 1)
 				powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;
@@ -164,7 +196,7 @@
 		}
 
 		dprintk (KERN_INFO PFX "   FID: 0x%x (%d.%dx [%dMHz])\t", fid,
-			fid_codes[fid] / 10, fid_codes[fid] % 10, speed);
+			fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000);
 
 		if (speed < minimum_speed)
 			minimum_speed = speed;
@@ -176,8 +208,6 @@
 		dprintk ("VID: 0x%x (%d.%03dV)\n", vid,	mobile_vid_table[vid]/1000,
 			mobile_vid_table[vid]%1000);
 	}
-	dprintk ("\n");
-
 	powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
 	powernow_table[number_scales].index = 0;
 
@@ -234,7 +264,8 @@
 
 	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
 	cfid = fidvidstatus.bits.CFID;
-	freqs.old = fsb * fid_codes[cfid] * 100;
+	freqs.old = fsb * fid_codes[cfid] / 10;
+
 	freqs.new = powernow_table[index].frequency;
 
 	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
@@ -262,19 +293,136 @@
 }
 
 
+#ifdef CONFIG_ACPI_PROCESSOR
+
+struct acpi_processor_performance *acpi_processor_perf;
+
+static int powernow_acpi_init(void)
+{
+	int i;
+	int retval = 0;
+	union powernow_acpi_control_t pc;
+
+	if (acpi_processor_perf != NULL && powernow_table != NULL) {
+		retval = -EINVAL;
+		goto err0;
+	}
+
+	acpi_processor_perf = kmalloc(sizeof(struct acpi_processor_performance),
+				      GFP_KERNEL);
+
+	if (!acpi_processor_perf) {
+		retval = -ENOMEM;
+		goto err0;
+	}
+
+	memset(acpi_processor_perf, 0, sizeof(struct acpi_processor_performance));
+
+	if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
+		retval = -EIO;
+		goto err1;
+	}
+
+	if (acpi_processor_perf->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
+		retval = -ENODEV;
+		goto err2;
+	}
+
+	if (acpi_processor_perf->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
+		retval = -ENODEV;
+		goto err2;
+	}
+
+	number_scales = acpi_processor_perf->state_count;
+
+	if (number_scales < 2) {
+		retval = -ENODEV;
+		goto err2;
+	}
+
+	powernow_table = kmalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL);
+	if (!powernow_table) {
+		retval = -ENOMEM;
+		goto err2;
+	}
+
+	memset(powernow_table, 0, ((number_scales + 1) * sizeof(struct cpufreq_frequency_table)));
+
+	pc.val = (unsigned long) acpi_processor_perf->states[0].control;
+	for (i = 0; i < number_scales; i++) {
+		u8 fid, vid;
+		unsigned int speed;
+
+		pc.val = (unsigned long) acpi_processor_perf->states[i].control;
+		dprintk (KERN_INFO PFX "acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
+			 i,
+			 (u32) acpi_processor_perf->states[i].core_frequency,
+			 (u32) acpi_processor_perf->states[i].power,
+			 (u32) acpi_processor_perf->states[i].transition_latency,
+			 (u32) acpi_processor_perf->states[i].control,
+			 pc.bits.sgtc);
+
+		vid = pc.bits.vid;
+		fid = pc.bits.fid;
+
+		powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
+		powernow_table[i].index = fid; /* lower 8 bits */
+		powernow_table[i].index |= (vid << 8); /* upper 8 bits */
+
+		speed = powernow_table[i].frequency;
+
+		if ((fid_codes[fid] % 10)==5) {
+			if (have_a0 == 1)
+				powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
+		}
+
+		dprintk (KERN_INFO PFX "   FID: 0x%x (%d.%dx [%dMHz])\t", fid,
+			fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000);
+		dprintk ("VID: 0x%x (%d.%03dV)\n", vid,	mobile_vid_table[vid]/1000,
+			mobile_vid_table[vid]%1000);
+
+		if (latency < pc.bits.sgtc)
+			latency = pc.bits.sgtc;
+
+		if (speed < minimum_speed)
+			minimum_speed = speed;
+		if (speed > maximum_speed)
+			maximum_speed = speed;
+	}
+
+	powernow_table[i].frequency = CPUFREQ_TABLE_END;
+	powernow_table[i].index = 0;
+
+	return 0;
+
+err2:
+	acpi_processor_unregister_performance(acpi_processor_perf, 0);
+err1:
+	kfree(acpi_processor_perf);
+err0:
+	printk(KERN_WARNING PFX "ACPI perflib can not be used in this platform\n");
+	acpi_processor_perf = NULL;
+	return retval;
+}
+#else
+static int powernow_acpi_init(void)
+{
+	printk(KERN_INFO PFX "no support for ACPI processor found."
+	       "  Please recompile your kernel with ACPI processor\n");
+	return -EINVAL;
+}
+#endif
+
 static int powernow_decode_bios (int maxfid, int startvid)
 {
 	struct psb_s *psb;
 	struct pst_s *pst;
-	struct cpuinfo_x86 *c = cpu_data;
 	unsigned int i, j;
 	unsigned char *p;
 	unsigned int etuple;
 	unsigned int ret;
 
 	etuple = cpuid_eax(0x80000001);
-	etuple &= 0xf00;
-	etuple |= (c->x86_model<<4)|(c->x86_mask);
 
 	for (i=0xC0000; i < 0xffff0 ; i+=16) {
 
@@ -305,7 +453,6 @@
 			}
 			dprintk (KERN_INFO PFX "Settling Time: %d microseconds.\n", psb->settlingtime);
 			dprintk (KERN_INFO PFX "Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst);
-			latency *= 100;	/* SGTC needs to be in units of 10ns */
 
 			p += sizeof (struct psb_s);
 
@@ -315,7 +462,8 @@
 				pst = (struct pst_s *) p;
 				number_scales = pst->numpstates;
 
-				if ((etuple == pst->cpuid) && (maxfid==pst->maxfid) && (startvid==pst->startvid))
+				if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) &&
+				    (maxfid==pst->maxfid) && (startvid==pst->startvid))
 				{
 					dprintk (KERN_INFO PFX "PST:%d (@%p)\n", i, pst);
 					dprintk (KERN_INFO PFX " cpuid: 0x%x\t", pst->cpuid);
@@ -323,7 +471,6 @@
 					dprintk ("maxFID: 0x%x\t", pst->maxfid);
 					dprintk ("startvid: 0x%x\n", pst->startvid);
 
-					fsb = pst->fsbspeed;
 					ret = get_ranges ((char *) pst + sizeof (struct pst_s));
 					return ret;
 
@@ -335,7 +482,7 @@
 			}
 			printk (KERN_INFO PFX "No PST tables match this cpuid (0x%x)\n", etuple);
 			printk (KERN_INFO PFX "This is indicative of a broken BIOS.\n");
-			printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.shtml\n");
+
 			return -EINVAL;
 		}
 		p++;
@@ -365,6 +512,33 @@
 	return cpufreq_frequency_table_verify(policy, powernow_table);
 }
 
+/*
+ * We use the fact that the bus frequency is somehow
+ * a multiple of 100000/3 khz, then we compute sgtc according
+ * to this multiple.
+ * That way, we match more how AMD thinks all of that work.
+ * We will then get the same kind of behaviour already tested under
+ * the "well-known" other OS.
+ */
+static int __init fixup_sgtc(void)
+{
+	unsigned int sgtc;
+	unsigned int m;
+
+	m = fsb / 3333;
+	if ((m % 10) >= 5)
+		m += 5;
+
+	m /= 10;
+
+	sgtc = 100 * m * latency;
+	sgtc = sgtc / 3;
+	if (sgtc > 0xfffff) {
+		printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
+		sgtc = 0xfffff;
+	}
+	return sgtc;
+}
 
 static int __init powernow_cpu_init (struct cpufreq_policy *policy)
 {
@@ -376,18 +550,45 @@
 
 	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
 
-	result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
+	/* A K7 with powernow technology is set to max frequency by BIOS */
+	fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
+	if (!fsb) {
+		printk(KERN_WARNING PFX "can not determine bus frequency\n");
+		return -EINVAL;
+	}
+	dprintk(KERN_INFO PFX "FSB: %3d.%03d MHz\n", fsb/1000, fsb%1000);
+
+	if ((dmi_broken & BROKEN_CPUFREQ) || powernow_acpi_force) {
+		printk (KERN_INFO PFX "PSB/PST known to be broken.  Trying ACPI instead\n");
+		result = powernow_acpi_init();
+	} else {
+		result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
+		if (result) {
+			printk (KERN_INFO PFX "Trying ACPI perflib\n");
+			maximum_speed = 0;
+			minimum_speed = -1;
+			latency = 0;
+			result = powernow_acpi_init();
+			if (result) {
+				printk (KERN_INFO PFX "ACPI and legacy methods failed\n");
+				printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.shtml\n");
+			}
+		} else {
+			/* SGTC use the bus clock as timer */
+			latency = fixup_sgtc();
+			printk(KERN_INFO PFX "SGTC: %d\n", latency);
+		}
+	}
+
 	if (result)
 		return result;
 
 	printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
-				minimum_speed, maximum_speed);
+				minimum_speed/1000, maximum_speed/1000);
 
 	policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
 
-	/* 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->cpuinfo.transition_latency = 20 * latency / fsb;
 
 	policy->cur = maximum_speed;
 
@@ -418,10 +619,6 @@
 
 static int __init powernow_init (void)
 {
-	if (dmi_broken & BROKEN_CPUFREQ) {
-		printk (KERN_INFO PFX "Disabled at boot time by DMI,\n");
-		return -ENODEV;
-	}
 	if (check_powernow()==0)
 		return -ENODEV;
 	return cpufreq_register_driver(&powernow_driver);
@@ -430,15 +627,25 @@
 
 static void __exit powernow_exit (void)
 {
+#ifdef CONFIG_ACPI_PROCESSOR
+	if (acpi_processor_perf) {
+		acpi_processor_unregister_performance(acpi_processor_perf, 0);
+		kfree(acpi_processor_perf);
+	}
+#endif
 	cpufreq_unregister_driver(&powernow_driver);
 	if (powernow_table)
 		kfree(powernow_table);
 }
 
+module_param(powernow_acpi_force,  int, 0444);
+
+MODULE_PARM_DESC(powernow_acpi_force, "Force ACPI to be used");
+
 MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");
 MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
 MODULE_LICENSE ("GPL");
 
-module_init(powernow_init);
+late_initcall(powernow_init);
 module_exit(powernow_exit);
 
diff -Nru a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c
--- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c	Wed Apr 21 00:30:54 2004
+++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c	Wed Apr 21 00:30:54 2004
@@ -32,7 +32,7 @@
 #include <asm/io.h>
 #include <asm/delay.h>
 
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+#ifdef CONFIG_ACPI_PROCESSOR
 #include <linux/acpi.h>
 #include <acpi/processor.h>
 #endif
@@ -269,7 +269,7 @@
 
 	dprintk(KERN_DEBUG PFX
 		"ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",
-		smp_processor_id();
+		smp_processor_id(),
 		data->currfid, data->currvid, reqvid, data->rvo);
 
 	while (data->currvid > reqvid) {
@@ -666,7 +666,7 @@
 	return -ENODEV;
 }
 
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+#ifdef CONFIG_ACPI_PROCESSOR
 static void powernow_k8_acpi_pst_values(struct powernow_k8_data *data, unsigned int index)
 {
 	if (!data->acpi_data.state_count)
diff -Nru a/arch/i386/kernel/cpu/cpufreq/powernow-k8.h b/arch/i386/kernel/cpu/cpufreq/powernow-k8.h
--- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.h	Wed Apr 21 00:30:54 2004
+++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.h	Wed Apr 21 00:30:54 2004
@@ -29,7 +29,7 @@
 	 * frequency is in kHz */
 	struct cpufreq_frequency_table  *powernow_table;
 
-#ifdef CONFIG_X86_POWERNOW_K8_ACPI
+#ifdef CONFIG_ACPI_PROCESSOR
 	/* the acpi table needs to be kept. it's only available if ACPI was
 	 * used to determine valid frequency/vid/fid states */
 	struct acpi_processor_performance acpi_data;
diff -Nru a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c
--- a/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c	Wed Apr 21 00:30:54 2004
+++ b/arch/i386/kernel/cpu/cpufreq/speedstep-centrino.c	Wed Apr 21 00:30:54 2004
@@ -195,21 +195,6 @@
 	struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
 	struct cpu_model *model;
 
-	if (!cpu_has(cpu, X86_FEATURE_EST))
-		return -ENODEV;
-
-	/* Only Intel Pentium M stepping 5 for now - add new CPUs as
-	   they appear after making sure they use PERF_CTL in the same
-	   way. */
-	if (cpu->x86_vendor != X86_VENDOR_INTEL ||
-	    cpu->x86        != 6 ||
-	    cpu->x86_model  != 9 ||
-	    cpu->x86_mask   != 5) {
-		printk(KERN_INFO PFX "found unsupported CPU with Enhanced SpeedStep: "
-		       "send /proc/cpuinfo to " MAINTAINER "\n");
-		return -ENODEV;
-	}
-
 	for(model = models; model->model_name != NULL; model++)
 		if (strcmp(cpu->x86_model_id, model->model_name) == 0)
 			break;
@@ -361,12 +346,28 @@
 
 static int centrino_cpu_init(struct cpufreq_policy *policy)
 {
+	struct cpuinfo_x86 *cpu = &cpu_data[policy->cpu];
 	unsigned freq;
 	unsigned l, h;
 	int ret;
 
 	if (policy->cpu != 0)
 		return -ENODEV;
+
+	if (!cpu_has(cpu, X86_FEATURE_EST))
+		return -ENODEV;
+
+	/* Only Intel Pentium M stepping 5 for now - add new CPUs as
+	   they appear after making sure they use PERF_CTL in the same
+	   way. */
+	if (cpu->x86_vendor != X86_VENDOR_INTEL ||
+	    cpu->x86        != 6 ||
+	    cpu->x86_model  != 9 ||
+	    cpu->x86_mask   != 5) {
+		printk(KERN_INFO PFX "found unsupported CPU with Enhanced SpeedStep: "
+		       "send /proc/cpuinfo to " MAINTAINER "\n");
+		return -ENODEV;
+	}
 
 	if (centrino_cpu_init_acpi(policy)) {
 		if (centrino_cpu_init_table(policy)) {
diff -Nru a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
--- a/drivers/cpufreq/cpufreq_userspace.c	Wed Apr 21 00:30:54 2004
+++ b/drivers/cpufreq/cpufreq_userspace.c	Wed Apr 21 00:30:54 2004
@@ -167,7 +167,7 @@
 		void __user *buffer, size_t *lenp)
 {
 	char buf[16], *p;
-	int cpu = (int) ctl->extra1;
+	int cpu = (long) ctl->extra1;
 	int len, left = *lenp;
 
 	if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) {
@@ -205,7 +205,7 @@
 	       void __user *oldval, size_t __user *oldlenp,
 	       void __user *newval, size_t newlen, void **context)
 {
-	int cpu = (int) table->extra1;
+	int cpu = (long) table->extra1;
 
 	if (!cpu_online(cpu))
 		return -EINVAL;