public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* powernow-k8-acpi driver
@ 2004-03-03 21:54 Pavel Machek
  2004-03-03 22:27 ` Dave Jones
  0 siblings, 1 reply; 21+ messages in thread
From: Pavel Machek @ 2004-03-03 21:54 UTC (permalink / raw)
  To: Cpufreq mailing list, kernel list, davej

Hi!

Lots of machines have broken PST tables, so current in-kernel driver
refuses to works on them. Vendors do get ACPI tables right because
apparently Windows use them ;-). So this driver tends to work.

Comments? Could we get this into mainline?

								Pavel

--- tmp/linux/arch/i386/kernel/cpu/cpufreq/Kconfig	2004-02-05 01:53:54.000000000 +0100
+++ linux/arch/i386/kernel/cpu/cpufreq/Kconfig	2004-03-03 12:39:29.000000000 +0100
@@ -93,6 +93,19 @@
 	depends on CPU_FREQ && EXPERIMENTAL
 	help
 	  This adds the CPUFreq driver for mobile AMD Opteron/Athlon64 processors.
+	  It relies on old "PST" tables. Unfortunately, many BIOSes get this table
+	  wrong. 
+
+	  For details, take a look at linux/Documentation/cpu-freq. 
+
+	  If in doubt, say N.
+
+config X86_POWERNOW_K8_ACPI
+	tristate "AMD Opteron/Athlon64 PowerNow! using ACPI"
+	depends on CPU_FREQ && EXPERIMENTAL
+	help
+	  This adds the CPUFreq driver for mobile AMD Opteron/Athlon64 processors.
+	  It relies on ACPI.
 
 	  For details, take a look at linux/Documentation/cpu-freq. 
 
--- tmp/linux/arch/i386/kernel/cpu/cpufreq/Makefile	2003-10-09 00:13:13.000000000 +0200
+++ linux/arch/i386/kernel/cpu/cpufreq/Makefile	2004-03-03 12:39:43.000000000 +0100
@@ -1,6 +1,7 @@
 obj-$(CONFIG_X86_POWERNOW_K6)	+= powernow-k6.o
 obj-$(CONFIG_X86_POWERNOW_K7)	+= powernow-k7.o
 obj-$(CONFIG_X86_POWERNOW_K8)	+= powernow-k8.o
+obj-$(CONFIG_X86_POWERNOW_K8_ACPI)	+= powernow-k8-acpi.o
 obj-$(CONFIG_X86_LONGHAUL)	+= longhaul.o
 obj-$(CONFIG_X86_P4_CLOCKMOD)	+= p4-clockmod.o
 obj-$(CONFIG_ELAN_CPUFREQ)	+= elanfreq.o
--- tmp/linux/arch/i386/kernel/cpu/cpufreq/powernow-k8-acpi.c	2004-03-03 12:30:35.000000000 +0100
+++ linux/arch/i386/kernel/cpu/cpufreq/powernow-k8-acpi.c	2004-03-03 12:38:17.000000000 +0100
@@ -0,0 +1,1184 @@
+/*
+ *   (c) 2003, 2004 Advanced Micro Devices, Inc.
+ *  Your use of this code is subject to the terms and conditions of the
+ *  GNU general public license version 2. See "../../../../../COPYING" or
+ *  http://www.gnu.org/licenses/gpl.html
+ *
+ *  This is the ACPI version of the cpu frequency driver. There is a
+ *  depreciated (and less functional) version of this driver that does not
+ *  use ACPI, and also does not support SMP.
+ *
+ *  Support : paul.devriendt@amd.com
+ *
+ *  Based on the powernow-k7.c module written by Dave Jones.
+ *  (c) 2003 Dave Jones <davej@codemonkey.ork.uk> on behalf of SuSE Labs
+ *  Licensed under the terms of the GNU GPL License version 2.
+ *  Based upon datasheets & sample CPUs kindly provided by AMD.
+ *
+ *  Valuable input gratefully received from Dave Jones, Pavel Machek, Dominik
+ *  Brodowski, and others.
+ *
+ *  Processor information obtained from Chapter 9 (Power and Thermal Management)
+ *  of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD
+ *  Opteron Processors", available for download from www.amd.com
+ */
+
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/acpi.h>
+
+#include <asm/msr.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#undef DEBUG
+#define VERSION "Version 1.20.02, Mar 1, 2004"
+#include "powernow-k8-acpi.h"
+
+static u8 **procs;                  /* per processor data structure     */
+static u32 rstps;                   /* pstates allowed restrictions     */
+static u32 seenrst;                 /* remember old bat restrictions    */
+static volatile int pollflg;        /* remember the state of the poller */
+static int acpierr;                 /* retain acpi error across walker  */
+static acpi_handle ppch;	    /* handle of the ppc object         */
+static acpi_handle psrh;            /* handle of the acpi power object  */
+static DECLARE_MUTEX(fidvid_sem);   /* serialize freq changes           */
+static DECLARE_MUTEX(poll_sem);     /* serialize poller state changes   */
+static struct timer_list ac_timer;  /* timer for the poller             */
+
+static struct cpufreq_driver cpufreq_amd64_driver = {
+	.verify = powernowk8_verify,
+	.target = powernowk8_target,
+	.init = powernowk8_cpu_init,
+	.name = "powernow-k8",
+	.owner = THIS_MODULE,
+};
+
+static inline u32 freq_from_fid(u8 fid)
+{
+	return 800 + (fid * 100);
+}
+
+static inline u32 kfreq_from_fid(u8 fid)
+{
+	return KHZ * freq_from_fid(fid);
+}
+
+static inline u32 fid_from_freq(u32 freq)
+{
+	return (freq - 800) / 100;
+}
+
+static inline u32 convert_fid_to_vfid(u8 fid)
+{
+	if (fid < HI_FID_TABLE_BOTTOM) {
+		return 8 + (2 * fid);
+	} else {
+		return fid;
+	}
+}
+
+static inline int pending_bit_stuck(void)
+{
+	u32 lo;
+	u32 hi;
+
+	rdmsr(MSR_FIDVID_STAT, lo, hi);
+	return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;
+}
+
+static int query_current_values_with_pending_wait(u8 *perproc)
+{
+	u32 lo = MSR_S_LO_CHANGE_PENDING;
+	u32 hi;
+	u32 i = 0;
+
+	while (lo & MSR_S_LO_CHANGE_PENDING) {
+		if (i++ > 0x1000000) {
+			printk(EFX "change pending stuck\n");
+			return 1;
+		}
+		rdmsr(MSR_FIDVID_STAT, lo, hi);
+	}
+	perproc[PP_OFF_CVID] = hi & MSR_S_HI_CURRENT_VID;
+	perproc[PP_OFF_CFID] = lo & MSR_S_LO_CURRENT_FID;
+	return 0;
+}
+
+/* need to init the control msr to a safe value (for each cpu) */
+static void fidvid_msr_init(void)
+{
+	u32 lo;
+	u32 hi;
+	u8 fid;
+	u8 vid;
+
+	rdmsr(MSR_FIDVID_STAT, lo, hi);
+	vid = hi & MSR_S_HI_CURRENT_VID;
+	fid = lo & MSR_S_LO_CURRENT_FID;
+	lo = fid | (vid << MSR_C_LO_VID_SHIFT);
+	hi = MSR_C_HI_STP_GNT_BENIGN;
+	dprintk(DFX "cpu%d, init lo %x, hi %x\n", smp_processor_id(), lo, hi);
+	wrmsr(MSR_FIDVID_CTL, lo, hi);
+}
+
+static inline void count_off_irt(u8 irt)
+{
+	udelay((1 << irt) * 10);
+}
+
+static inline void count_off_vst(u8 vstable)
+{
+	udelay(vstable * VST_UNITS_20US);
+}
+
+static int write_new_fid(u8 *perproc, u32 idx, u8 fid)
+{
+	u32 lo;
+	u32 hi;
+	struct pstate *pst;
+	u8 savevid = perproc[PP_OFF_CVID];
+
+	if (idx >= perproc[PP_OFF_NUMPS]) {
+		printk(EFX "idx overflow fid write\n");
+		return 1;
+	}
+	pst = (struct pstate *)(perproc + PP_OFF_BYTES);
+	pst += idx;
+
+	if ((fid & INVALID_FID_MASK) || (savevid & INVALID_VID_MASK)) {
+		printk(EFX "overflow on fid write\n");
+		return 1;
+	}
+	lo = fid | (savevid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT;
+	hi = pst->plllock * PLL_LOCK_CONVERSION;
+	dprintk(DFX "cpu%d, writing fid %x, lo %x, hi %x\n",
+		smp_processor_id(), fid, lo, hi);
+	wrmsr(MSR_FIDVID_CTL, lo, hi);
+	if (query_current_values_with_pending_wait(perproc))
+		return 1;
+	count_off_irt(pst->irt);
+
+	if (savevid != perproc[PP_OFF_CVID]) {
+		printk(EFX "vid change on fid trans, old %x, new %x\n",
+		       savevid, perproc[PP_OFF_CVID]);
+		return 1;
+	}
+	if (perproc[PP_OFF_CFID] != fid) {
+		printk(EFX "fid trans failed, targ %x, new %x\n",
+		       fid, perproc[PP_OFF_CFID]);
+		return 1;
+	}
+	return 0;
+}
+
+static int write_new_vid(u8 *perproc, u8 vid)
+{
+	u32 lo;
+	u8 savefid = perproc[PP_OFF_CFID];
+
+	if ((savefid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
+		printk(EFX "overflow on vid write\n");
+		return 1;
+	}
+
+	lo = perproc[PP_OFF_CFID] | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT;
+	dprintk(DFX "cpu%d, writing vid %x, lo %x, hi %x\n",
+		smp_processor_id(), vid, lo, STOP_GRANT_5NS);
+	wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
+	if (query_current_values_with_pending_wait(perproc))
+		return 1;
+
+	if (savefid != perproc[PP_OFF_CFID]) {
+		printk(EFX "fid change on vid trans, old %x new %x\n",
+		       savefid, perproc[PP_OFF_CFID]);
+		return 1;
+	}
+	if (vid != perproc[PP_OFF_CVID]) {
+		printk(EFX "vid trans failed, vid %x, cvid %x\n",
+		       vid, perproc[PP_OFF_CFID]);
+		return 1;
+	}
+	return 0;
+}
+
+static int decrease_vid_code_by_step(u8 *perproc, u32 idx, u8 reqvid, u8 step)
+{
+	struct pstate *pst;
+
+	if (idx >= perproc[PP_OFF_NUMPS]) {
+		printk(EFX "idx overflow vid step\n");
+		return 1;
+	}
+	pst = (struct pstate *) (perproc + PP_OFF_BYTES);
+	pst += idx;
+
+	if (step == 0)  /* BIOS error if this is the case, but continue */
+		step = 1;
+
+	if ((perproc[PP_OFF_CVID] - reqvid) > step)
+		reqvid = perproc[PP_OFF_CVID] - step;
+	if (write_new_vid(perproc, reqvid))
+		return 1;
+	count_off_vst(pst->vstable);
+	return 0;
+}
+
+static inline int core_voltage_pre_transition(u8 *perproc, u32 idx, u8 rvid)
+{
+	struct pstate *pst;
+	u8 rvosteps;
+	u8 savefid = perproc[PP_OFF_CFID];
+
+	pst = (struct pstate *) (perproc + PP_OFF_BYTES);
+	pst += idx;
+	rvosteps = pst->rvo;
+	dprintk(DFX "ph1 start%d, cfid 0x%x, cvid 0x%x, rvid 0x%x, rvo %x\n",
+		smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID], rvid, pst->rvo);
+
+	while (perproc[PP_OFF_CVID] > rvid) {
+		dprintk(DFX "ph1 curr %x, req vid %x\n",
+			    perproc[PP_OFF_CVID], rvid);
+		if (decrease_vid_code_by_step(perproc, idx, rvid, pst->vidmvs))
+			return 1;
+	}
+
+	while (rvosteps) {
+		if (perproc[PP_OFF_CVID] == 0) {
+			rvosteps = 0;
+		} else {
+			dprintk(DFX "ph1 changing vid for rvo, req 0x%x\n",
+				perproc[PP_OFF_CVID] - 1);
+			if (decrease_vid_code_by_step(perproc, idx,
+						perproc[PP_OFF_CVID] - 1, 1))
+				return 1;
+			rvosteps--;
+		}
+	}
+	if (query_current_values_with_pending_wait(perproc))
+		return 1;
+
+	if (savefid != perproc[PP_OFF_CFID]) {
+		printk(EFX "ph1 err, cfid changed %x\n", perproc[PP_OFF_CFID]);
+		return 1;
+	}
+	dprintk(DFX "ph1 done%d, cfid 0x%x, cvid 0x%x\n",
+		smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID]);
+	return 0;
+}
+
+static inline int core_frequency_transition(u8 * perproc, u32 idx, u8 reqfid)
+{
+	u8 vcoreqfid;
+	u8 vcocurrfid;
+	u8 vcofiddiff;
+	u8 savevid = perproc[PP_OFF_CVID];
+
+	if ((reqfid < HI_FID_TABLE_BOTTOM)
+	    && (perproc[PP_OFF_CFID] < HI_FID_TABLE_BOTTOM)) {
+		printk(EFX "ph2 illegal lo-lo transition %x %x\n",
+		       reqfid, perproc[PP_OFF_CFID]);
+		return 1;
+	}
+
+	if (perproc[PP_OFF_CFID] == reqfid) {
+		printk(EFX "ph2 null fid transition %x\n", reqfid );
+		return 0;
+	}
+
+	dprintk(DFX "ph2 start%d, cfid %x, cvid %x, rfid %x\n",
+		smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID], reqfid);
+
+	vcoreqfid = convert_fid_to_vfid(reqfid);
+	vcocurrfid = convert_fid_to_vfid(perproc[PP_OFF_CFID]);
+	vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+						: vcoreqfid - vcocurrfid;
+
+	while (vcofiddiff > FSTEP) {
+		if (reqfid > perproc[PP_OFF_CFID]) {
+			if (perproc[PP_OFF_CFID] > LO_FID_TABLE_TOP) {
+				if (write_new_fid(perproc, idx,
+						  perproc[PP_OFF_CFID] + FSTEP))
+					return 1;
+			} else {
+				if (write_new_fid(perproc, idx, FSTEP +
+				     convert_fid_to_vfid(perproc[PP_OFF_CFID])))
+					return 1;
+			}
+		} else {
+			if (write_new_fid(perproc, idx,
+					  perproc[PP_OFF_CFID]-FSTEP))
+				return 1;
+		}
+
+		vcocurrfid = convert_fid_to_vfid(perproc[PP_OFF_CFID]);
+		vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid
+						    : vcoreqfid - vcocurrfid;
+	}
+	if (write_new_fid(perproc, idx, reqfid))
+		return 1;
+	if (query_current_values_with_pending_wait(perproc))
+		return 1;
+
+	if (perproc[PP_OFF_CFID] != reqfid) {
+		printk(EFX "ph2 mismatch, failed transn, curr %x, req %x\n",
+		       perproc[PP_OFF_CFID], reqfid);
+		return 1;
+	}
+
+	if (savevid != perproc[PP_OFF_CVID]) {
+		printk(EFX "ph2 vid changed, save %x, curr %x\n", savevid,
+		       perproc[PP_OFF_CVID]);
+		return 1;
+	}
+
+	dprintk(DFX "ph2 complete%d, currfid 0x%x, currvid 0x%x\n",
+		smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID]);
+	return 0;
+}
+
+static inline int core_voltage_post_transition(u8 * perproc, u32 idx, u8 reqvid)
+{
+	u8 savefid = perproc[PP_OFF_CFID];
+	u8 savereqvid = reqvid;
+
+	dprintk(DFX "ph3 starting%d, cfid 0x%x, cvid 0x%x\n",
+		smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID]);
+
+	if (reqvid != perproc[PP_OFF_CVID]) {
+		if (write_new_vid(perproc, reqvid))
+			return 1;
+
+		if (savefid != perproc[PP_OFF_CFID]) {
+			printk(EFX "ph3: bad fid change, save %x, curr %x\n",
+			       savefid, perproc[PP_OFF_CFID]);
+			return 1;
+		}
+
+		if (perproc[PP_OFF_CVID] != reqvid) {
+			printk(EFX "ph3: failed vid trans\n, req %x, curr %x",
+			       reqvid, perproc[PP_OFF_CVID]);
+			return 1;
+		}
+	}
+	if (query_current_values_with_pending_wait(perproc))
+		return 1;
+
+	if (savereqvid != perproc[PP_OFF_CVID]) {
+		dprintk(EFX "ph3 failed, currvid 0x%x\n", perproc[PP_OFF_CVID]);
+		return 1;
+	}
+
+	if (savefid != perproc[PP_OFF_CFID]) {
+		dprintk(EFX "ph3 failed, currfid changed 0x%x\n",
+			perproc[PP_OFF_CFID]);
+		return 1;
+	}
+
+	dprintk(DFX "ph3 done%d, cfid 0x%x, cvid 0x%x\n",
+		smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID]);
+	return 0;
+}
+
+static inline int transition_fid_vid(u8 *perproc, u32 idx, u8 rfid, u8 rvid)
+{
+	if (core_voltage_pre_transition(perproc, idx, rvid))
+		return 1;
+	if (core_frequency_transition(perproc, idx, rfid))
+		return 1;
+	if (core_voltage_post_transition(perproc, idx, rvid))
+		return 1;
+	if (query_current_values_with_pending_wait(perproc))
+		return 1;
+	if ((rfid != perproc[PP_OFF_CFID]) || (rvid != perproc[PP_OFF_CVID])) {
+		printk(EFX "failed%d: req %x %x, curr %x %x\n",
+		       smp_processor_id(), rfid, rvid,
+		       perproc[PP_OFF_CFID], perproc[PP_OFF_CVID]);
+		return 1;
+	}
+	dprintk(IFX "transitioned%d: new fid 0x%x, vid 0x%x\n",
+		smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID]);
+	return 0;
+}
+
+static inline int check_supported_cpu(void)
+{
+	struct cpuinfo_x86 *c = cpu_data;
+	u32 eax, ebx, ecx, edx;
+
+	if (c->x86_vendor != X86_VENDOR_AMD)
+		return 0;
+
+	eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
+	if (!(((eax & CPUID_XFAM_MOD) == ATHLON64_XFAM_MOD) ||
+	      ((eax & CPUID_XFAM_MOD) == OPTERON_XFAM_MOD))) {
+		dprintk(DFX "AMD Athlon 64 / Opteron processor required\n");
+		return 0;
+	}
+
+	eax = cpuid_eax(CPUID_GET_MAX_CAPABILITIES);
+	if (eax < CPUID_FREQ_VOLT_CAPABILITIES) {
+		printk(IFX "No freq change capabilities\n");
+		return 0;
+	}
+
+	cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
+	if ((edx & P_STATE_TRANSITION_CAPABLE) != P_STATE_TRANSITION_CAPABLE) {
+		printk(IFX "P-state transitions not supported\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+/* evaluating this object tells us whether we are using mains or battery */
+static inline int process_psr(acpi_handle objh)
+{
+	if (num_online_cpus() == 1) /* ignore BIOS claiming battery MP boxes */
+		psrh = objh;
+	return 0;
+}
+
+static inline void
+extract_pss_package(struct pstate *pst, struct proc_pss *proc)
+{
+	pst->freq = proc->freq;
+	pst->fid = proc->cntl & FID_MASK;
+	pst->vid = (proc->cntl >> VID_SHIFT) & VID_MASK;
+	pst->irt = (proc->cntl >> IRT_SHIFT) & IRT_MASK;
+	pst->rvo = (proc->cntl >> RVO_SHIFT) & RVO_MASK;
+	pst->plllock = (proc->cntl >> PLL_L_SHIFT) & PLL_L_MASK;
+	pst->vidmvs = 1 << ((proc->cntl >> MVS_SHIFT) & MVS_MASK);
+	pst->vstable = (proc->cntl >> VST_SHIFT) & VST_MASK;
+}
+
+/* per cpu perf states */
+static int process_pss(acpi_handle objh, unsigned cpunumb)
+{
+	struct proc_pss proc;
+	u8 *perproc;
+	struct pstate *pst;
+	u32 pstc;
+	acpi_status rc;
+	char pss_arr[1000];  /* big buffer on the stack rather than dyn alloc */
+	struct acpi_buffer buf = { sizeof(pss_arr), pss_arr };
+	unsigned int i;
+	union acpi_object *obj;
+	union acpi_object *data;
+	struct acpi_buffer format = { sizeof(PSS_FMT_STR), PSS_FMT_STR };
+	struct acpi_buffer state;
+
+	dprintk(DFX "processing _PSS for cpu%d\n", cpunumb);
+
+	if (procs[cpunumb]) {
+		printk(EFX "duplicate cpu data in acpi _pss\n");
+		return -ENODEV;
+	}
+
+	memset(pss_arr, 0, sizeof(pss_arr));
+	rc = acpi_evaluate_object(objh, NULL, NULL, &buf);
+	if (ACPI_FAILURE(rc)) {
+		printk(EFX "evaluate pss failed: %x\n", rc);
+		return -ENODEV;
+	}
+
+	obj = (union acpi_object *) &pss_arr[0];
+	if (obj->package.type != ACPI_TYPE_PACKAGE) {
+		printk(EFX "pss is not a package\n");
+		return -ENODEV;
+	}
+	pstc = obj->package.count;
+	if (pstc < 2) {
+		printk(EFX "insufficient pstates (%d)\n", pstc);
+		return -ENODEV;
+	}
+
+	i = (PP_OFF_BYTES * sizeof(u8)) + (sizeof(struct pstate) * pstc);
+	perproc = kmalloc(i, GFP_KERNEL);
+	if (!perproc) {
+		printk(EFX "perproc memory alloc failure\n");
+		return -ENOMEM;
+	}
+	memset(perproc, 0, i);
+	pst = (struct pstate *) (perproc + PP_OFF_BYTES);
+	perproc[PP_OFF_NUMPS] = pstc;
+
+	data = obj->package.elements;
+	for (i = 0; i < pstc; i++) {
+		if (data[i].package.type != ACPI_TYPE_PACKAGE) {
+			printk(EFX "%d: type %d\n", i, data[i].package.type);
+			kfree(perproc);
+			return -ENODEV;
+		}
+		state.length = sizeof(struct proc_pss);
+		state.pointer = &proc;
+		rc = acpi_extract_package(&obj->package.elements[i],
+					  &format, &state);
+		if (rc) {
+			printk(EFX "extract err %x\n", rc);
+			kfree(perproc);
+			return -ENODEV;
+		}
+		extract_pss_package(pst + i, &proc);
+	}
+
+	procs[cpunumb] = perproc;
+	return 0;
+}
+
+static u32 query_ac(void)
+{
+	acpi_status rc;
+	unsigned long state;
+
+	if (psrh) {
+		rc = acpi_evaluate_integer(psrh, NULL, NULL, &state);
+		if (ACPI_SUCCESS(rc)) {
+			if (state == 1)
+				return POW_AC;
+			else if (state == 0)
+				return POW_BAT;
+			else
+				printk(EFX "psr state %lx\n", state);
+		}
+		else {
+			printk(EFX "error %x evaluating psr\n", rc );
+		}
+	}
+	return POW_UNK;
+}
+
+/* gives us the (optional) battery/thermal restrictions */
+static int process_ppc(acpi_handle objh)
+{
+	acpi_status rc;
+	unsigned long state;
+
+	if (objh) {
+		ppch = objh;
+	} else {
+		if (ppch) {
+			objh = ppch;
+		} else {
+			rstps = 0;
+			return 0;   
+		}
+	}
+
+	if (num_online_cpus() > 1) {
+		/* For future thermal support (next release?), rstps needs   */
+		/* to be per processor, and handled for the SMP case. Later. */
+		dprintk(EFX "ignoring attempt to restrict pstates for SMP\n");
+	}
+	else {
+		rc = acpi_evaluate_integer(objh, NULL, NULL, &state);
+		if (ACPI_SUCCESS(rc)) {
+			rstps = state & 0x0f;
+			//dprintk(DFX "pstate restrictions %x\n", rstps);
+			if (!seenrst)
+				seenrst = rstps;
+		}
+		else {
+			rstps = 0;
+			printk(EFX "error %x processing ppc\n", rc);
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+static int powernow_find_objects(acpi_handle objh, char *nspace)
+{
+	acpi_status rc;
+	char name[80] = { '?', '\0' };
+	struct acpi_buffer buf = { sizeof(name), name };
+	unsigned cpuobj;
+	unsigned len = strlen(nspace);
+	unsigned dotoff = len + 1;
+	unsigned objoff = len + 2;
+
+	rc = acpi_get_name(objh, ACPI_FULL_PATHNAME, &buf);
+	if (ACPI_SUCCESS(rc)) {
+		if (!psrh) {
+			if (!strcmp(name + strlen(name) - 4, "_PSR")) {
+				dprintk(IFX "_psr found in %s\n", name);
+				return process_psr(objh);
+			}
+		}
+
+		if ((!(strncmp(name, nspace, len))) && (name[dotoff] == '.')) {
+			dprintk(IFX "searching %s\n", nspace);
+			cpuobj = name[len] - '0';
+			dprintk(IFX "cpu%u, %s\n", cpuobj, name);
+			if (cpuobj >= num_online_cpus()) {
+				printk(EFX "cpu count mismatch: %d, %d\n",
+                                       cpuobj, num_online_cpus());
+				acpierr = -ENODEV;
+				return 0;
+			}
+
+			if (!(strcmp(name + objoff, "_PSS"))) {
+				dprintk(IFX "_pss found in %s\n", nspace);
+				acpierr = process_pss(objh, cpuobj);
+				return 0;
+			} else if (!(strcmp(name + objoff, "_PPC"))) {
+				dprintk(IFX "_ppc found in %s\n", nspace);
+				acpierr = process_ppc(objh);
+				return 0;
+			}
+		}
+	}
+	return 1;
+}
+
+static acpi_status
+powernow_walker(acpi_handle objh, u32 nestl, void *ctx, void **wrc)
+{
+	int notfound = powernow_find_objects(objh, "\\_SB_.CPU");
+	if (notfound)
+		powernow_find_objects(objh, "\\_PR_.CPU");
+	return AE_OK;
+}
+
+static inline int find_acpi_table(void)
+{
+	acpi_status rc;
+	acpi_status wrc;
+	void *pwrc = &wrc;
+	unsigned i;
+	unsigned j;
+	struct pstate *pst;
+
+	rc = acpi_subsystem_status();
+	if (ACPI_FAILURE(rc)) {
+		printk(EFX "acpi subsys rc: %x\n", rc);
+		return -ENODEV;
+	}
+	rc = acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, 6,
+				 powernow_walker, 0, &pwrc);
+	if (rc)
+		return rc;
+	if (acpierr)
+		return acpierr;
+
+	for (i = 0; i < num_online_cpus(); i++) {
+		if (procs[i]) {
+			pst = (struct pstate *) (procs[i] + PP_OFF_BYTES);
+			for (j = 0; j < procs[i][PP_OFF_NUMPS]; j++)
+				dprintk(IFX
+			            "cpu%d: freq %d: fid %x, vid %x, irt %x, "
+				    "rvo %x, plllock %x, vidmvs %x, vstbl %x\n",
+				    i, pst[j].freq, pst[j].fid, pst[j].vid,
+				    pst[j].irt, pst[j].rvo, pst[j].plllock,
+				    pst[j].vidmvs, pst[j].vstable);
+		} else {
+			printk(EFX "Missing pstates for cpu%d\n", i);
+			return -ENODEV;
+		}
+	}
+
+	i = query_ac();
+	dprintk(IFX "mains power %s\n", POW_AC == i ? "online"
+		       : POW_BAT == i ? "offline" : "unknown");
+
+	return 0;
+}
+
+/* destroy the global table of per processor data */
+static void cleanup_procs(void)  
+{
+	unsigned i;
+	if (procs)
+		for (i = 0; i < num_online_cpus(); i++)
+			kfree(procs[i]);
+	kfree(procs);
+}
+
+static u8 find_closest_fid(u16 freq)
+{
+	freq += MIN_FREQ_RESOLUTION - 1;
+	freq = (freq / MIN_FREQ_RESOLUTION) * MIN_FREQ_RESOLUTION;
+	if (freq < MIN_FREQ)
+		freq = MIN_FREQ;
+	else if (freq > MAX_FREQ)
+		freq = MAX_FREQ;
+	return fid_from_freq(freq);
+}
+
+static int find_match(u8 *perproc, u16 *ptargfreq, u16 *pmin, u16 *pmax,
+			u8 *pfid, u8 *pvid, u32 *idx)
+{
+	u32 availpstates = perproc[PP_OFF_NUMPS];
+	u8 targfid = find_closest_fid(*ptargfreq);
+	u8 minfid = find_closest_fid(*pmin);
+	u8 maxfid = find_closest_fid(*pmax);
+	u32 maxidx = 0;
+	u32 minidx = availpstates - 1;
+	u32 targidx = 0xffffffff;
+	int i;
+	struct pstate *pst = (struct pstate *) (perproc + PP_OFF_BYTES);
+
+	dprintk(DFX "find match: freq %d MHz (%x), min %d (%x), max %d (%x)\n",
+		*ptargfreq, targfid, *pmin, minfid, *pmax, maxfid);
+
+	/* restrict to permitted pstates (battery/thermal) */
+        process_ppc(0);
+	if (rstps > availpstates)
+		rstps = 0;
+	if (rstps && (POW_BAT == query_ac())) { /* not restricting for thermal */
+		maxidx = availpstates - rstps;
+		dprintk(DFX "bat: idx restr %d-%d\n", maxidx, minidx);
+	}
+
+	/* Restrict values to the frequency choices in the pst */
+	if (minfid < pst[minidx].fid)
+		minfid = pst[minidx].fid;
+	if (maxfid > pst[maxidx].fid)
+		maxfid = pst[maxidx].fid;
+
+	/* Find appropriate pst index for the max fid */
+	for (i = 0; i < (int) availpstates; i++) {
+		if (maxfid <= pst[i].fid)
+			maxidx = i;
+	}
+
+	/* Find appropriate pst index for the min fid */
+	for (i = availpstates - 1; i >= 0; i--) {
+		if (minfid >= pst[i].fid)
+			minidx = i;
+	}
+
+	if (minidx < maxidx)
+		minidx = maxidx;
+
+	dprintk(DFX "minidx %d, maxidx %d\n", minidx, maxidx);
+
+	/* Frequency ids are now constrained by limits matching PST entries */
+	minfid = pst[minidx].fid;
+	maxfid = pst[maxidx].fid;
+
+	/* Limit the target frequency to these limits */
+	if (targfid < minfid)
+		targfid = minfid;
+	else if (targfid > maxfid)
+		targfid = maxfid;
+
+	/* Find the best target index into the PST, contrained by the range */
+	for (i = minidx; i >= (int) maxidx; i--) {
+		if (targfid >= pst[i].fid)
+			targidx = i;
+	}
+
+	if (targidx == 0xffffffff) {
+		printk(EFX "could not find target\n");
+		return 1;
+	}
+	*pmin = freq_from_fid(minfid);
+	*pmax = freq_from_fid(maxfid);
+	*ptargfreq = freq_from_fid(pst[targidx].fid);
+	*pfid = pst[targidx].fid;
+	*pvid = pst[targidx].vid;
+	*idx = targidx;
+	return 0;
+}
+
+static inline int
+transition_frequency(u8 *perproc, u16 *preq, u16 *pmin, u16 *pmax)
+{
+	u32 idx;
+	int res;
+	struct cpufreq_freqs freqs;
+	u8 fid;
+	u8 vid;
+
+	if (find_match(perproc, preq, pmin, pmax, &fid, &vid, &idx))
+		return 1;
+	dprintk(DFX "matched idx %x: fid 0x%x vid 0x%x\n", idx, fid, vid);
+
+	if (query_current_values_with_pending_wait(perproc))
+		return 1;
+	if ((perproc[PP_OFF_CVID] == vid) && (perproc[PP_OFF_CFID] == fid)) {
+		dprintk(DFX "targ matches curr (fid %x, vid %x)\n", fid, vid);
+		return 0;
+	}
+
+	if ((fid < HI_FID_TABLE_BOTTOM)
+	    && (perproc[PP_OFF_CFID] < HI_FID_TABLE_BOTTOM)) {
+		printk(EFX "ignoring change in lo freq table: %x to %x\n",
+		       perproc[PP_OFF_CFID], fid);
+		return 1;
+	}
+
+	dprintk(DFX "cpu%d to fid %x vid %x\n", smp_processor_id(), fid, vid);
+
+	freqs.cpu = smp_processor_id();
+	freqs.old = freq_from_fid(perproc[PP_OFF_CFID]);
+	freqs.new = freq_from_fid(fid);
+	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+	down(&fidvid_sem);
+	res = transition_fid_vid(perproc, idx, fid, vid);
+	up(&fidvid_sem);
+
+	freqs.new = freq_from_fid(perproc[PP_OFF_CFID]);
+	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return res;
+}
+
+static int need_poller(void)   /* if running at a freq only allowed for a/c */
+{
+	u8 *perproc = procs[0];
+	struct pstate *pst = (struct pstate *)(perproc + PP_OFF_BYTES);
+        u32 maxidx;
+
+        if (num_online_cpus() > 1)
+		return 0;
+
+        process_ppc(0);
+	if (rstps > perproc[PP_OFF_NUMPS])
+		return 0;
+	maxidx = perproc[PP_OFF_NUMPS] - rstps;
+	pst += maxidx;
+	if (rstps && (perproc[PP_OFF_CFID] > pst->fid ))
+		return 1;
+	return 0;
+}
+
+/* transiion if needed, restart if needed */
+static void ac_poller(unsigned long x)  
+{
+	int pow;
+	u8 *perproc = procs[0];
+	struct pstate *pst = (struct pstate *)(perproc + PP_OFF_BYTES);
+        u32 maxidx = perproc[PP_OFF_NUMPS] - rstps;
+	u16 rf = pst[maxidx].freq;
+	u16 minfreq = pst[perproc[PP_OFF_NUMPS]-1].freq;
+	u16 maxfreq = pst[maxidx].freq;
+
+	down(&poll_sem);
+	if (pollflg == POLLER_UNLOAD) {
+		pollflg == POLLER_DEAD;
+		up(&poll_sem);
+		return;
+	}
+        process_ppc(0);
+	if (rstps > perproc[PP_OFF_NUMPS]) {
+		pollflg = POLLER_NOT_RUNNING;
+		up(&poll_sem);
+		return;
+	}
+	if (pollflg != POLLER_RUNNING)
+		panic("k8-pn pollflg %x\n", pollflg);
+	up(&poll_sem);
+
+	pow = query_ac();
+	if (POW_AC == pow) {                 /* only poll if cpu is at high */
+		if (need_poller()) {         /* speed and on mains power    */
+			start_ac_poller(1);
+			return;
+		}
+	}
+	else if (POW_BAT == pow) {
+		if (need_poller()) {
+			dprintk(DFX "battery emergency transition\n" );
+                        transition_frequency(perproc, &rf, &minfreq, &maxfreq);
+		}
+	}
+	down(&poll_sem);
+	pollflg = POLLER_NOT_RUNNING;
+	up(&poll_sem);
+}
+
+static void start_ac_poller(int frompoller)
+{
+	down(&poll_sem);
+	if ( (frompoller) || (pollflg == POLLER_NOT_RUNNING) ) {
+		init_timer(&ac_timer);
+		ac_timer.function = ac_poller;
+		ac_timer.data = 0;
+		ac_timer.expires = jiffies + HZ;
+		add_timer( &ac_timer );
+		pollflg = POLLER_RUNNING;
+		//dprintk(DFX "timer added\n");
+	}
+	up(&poll_sem);
+}
+
+static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq,
+				unsigned relation)
+{
+	cpumask_t oldmask = CPU_MASK_ALL;
+	unsigned thiscpu;
+	int rc = 0;
+	u16 reqfreq = (u16)(targfreq / KHZ);
+	u16 minfreq = (u16)(pol->min / KHZ);
+	u16 maxfreq = (u16)(pol->max / KHZ);
+	u8 *perproc;
+	u8 checkfid;
+	u8 checkvid;
+
+	dprintk(IFX "proc mask %lx, current %d\n", current->cpus_allowed,
+		smp_processor_id());
+	dprintk(DFX "targ%d: %d kHz, min %d, max %d, relation %d\n",
+		pol->cpu, targfreq, pol->min, pol->max, relation);
+
+	if (pol->cpu > num_online_cpus()) {
+		printk(EFX "targ out of range\n");
+		return -ENODEV;
+	}
+	if (procs == NULL) {
+		printk(EFX "targ: procs 0\n");
+		return -ENODEV;
+	}
+	perproc = procs[pol->cpu];
+	if (perproc == NULL) {
+		printk(EFX "targ: perproc 0 for cpu%d\n", pol->cpu);
+		return -ENODEV;
+	}
+
+        thiscpu = smp_processor_id();
+	if (num_online_cpus()>1) {
+		oldmask = current->cpus_allowed;
+		set_cpus_allowed(current, cpumask_of_cpu(pol->cpu));
+		schedule();
+	}
+
+	/* from this point, do not exit without restoring preempt and cpu */
+	preempt_disable();
+
+	dprintk(DFX "targ cpu %d, curr cpu %d (mask %lx)\n", pol->cpu,
+		    smp_processor_id(),	current->cpus_allowed);
+
+	checkfid = perproc[PP_OFF_CFID];
+	checkvid = perproc[PP_OFF_CVID];
+	if (query_current_values_with_pending_wait(perproc)) {
+		printk(EFX "drv targ fail: change pending bit set\n");
+		rc = -EIO;
+		goto targ_exit;
+	}
+	dprintk(DFX "targ%d: curr fid %x, vid %x\n", smp_processor_id(),
+		perproc[PP_OFF_CFID], perproc[PP_OFF_CVID]);
+	if ((checkvid != perproc[PP_OFF_CVID])
+	    || (checkfid != perproc[PP_OFF_CFID])) {
+		printk(EFX "error - out of sync, fid %x %x, vid %x %x\n",
+		       checkfid, perproc[PP_OFF_CFID], checkvid,
+		       perproc[PP_OFF_CVID]);
+	}
+
+	if (transition_frequency(perproc, &reqfreq, &minfreq, &maxfreq)) {
+		printk(EFX "transition frequency failed\n");
+		rc = -EIO;
+		goto targ_exit;
+	}
+
+	pol->cur = kfreq_from_fid(perproc[PP_OFF_CFID]);
+
+targ_exit:
+	preempt_enable_no_resched();
+	if (num_online_cpus()>1) {
+		set_cpus_allowed(current, cpumask_of_cpu(thiscpu));
+		schedule();			  
+		set_cpus_allowed(current, oldmask);
+	}
+        if ((POW_AC == query_ac()) && (need_poller()))
+		start_ac_poller(0);
+	return rc;
+}
+
+static int powernowk8_verify(struct cpufreq_policy *pol)
+{
+	u16 min = (u16)(pol->min / KHZ);
+	u16 max = (u16)(pol->max / KHZ);
+	u16 targ = min;
+	u8 *perproc;
+	int res;
+	u32 idx;
+	u8 fid;
+	u8 vid;
+
+	dprintk(DFX "ver: cpu%d, min %d, max %d, cur %d, pol %d\n",
+		pol->cpu, pol->min, pol->max, pol->cur, pol->policy);
+
+	if (pol->cpu > num_online_cpus()) {
+		printk(EFX "ver cpu out of range\n");
+		return -ENODEV;
+	}
+	if (procs == NULL) {
+		printk(EFX "verify - procs 0\n");
+		return -ENODEV;
+	}
+	perproc = procs[pol->cpu];
+	if (perproc == NULL) {
+		printk(EFX "verify: perproc 0 for cpu%d\n", pol->cpu);
+		return -ENODEV;
+	}
+
+	res = find_match(perproc, &targ, &min, &max, &fid, &vid, &idx);
+	if (!res) {
+		pol->min = min * KHZ;
+		pol->max = max * KHZ;
+	}
+	return res;
+}
+
+static int __init powernowk8_cpu_init(struct cpufreq_policy *pol)
+{
+	u8 *perproc = procs[smp_processor_id()];
+	struct pstate *pst = (struct pstate *)(perproc + PP_OFF_BYTES);
+
+	pol->governor = CPUFREQ_DEFAULT_GOVERNOR;
+	pol->cpuinfo.transition_latency =             /* crude guess */
+		((pst[0].rvo + 8) * pst[0].vstable * VST_UNITS_20US)
+		+ (3 * (1 << pst[0].irt) * 10);
+
+	pol->cur = kfreq_from_fid(perproc[PP_OFF_CFID]);
+	dprintk(DFX "policy cfreq %d kHz\n", pol->cur);
+
+	/* min/max this cpu is capable of */
+	pol->cpuinfo.min_freq =kfreq_from_fid(pst[perproc[PP_OFF_NUMPS]-1].fid);
+	pol->cpuinfo.max_freq = kfreq_from_fid(pst[0].fid);
+	pol->min = pol->cpuinfo.min_freq;
+	pol->max = pol->cpuinfo.max_freq;
+	return 0;
+}
+
+#ifdef CONFIG_SMP
+static void smp_k8_init( void *retval )
+{
+	u8 *perproc = procs[smp_processor_id()];
+	int *rc = (int *)retval;
+	rc += smp_processor_id();
+
+	dprintk(DFX "smp init on %d\n", smp_processor_id());
+	if (check_supported_cpu() == 0) {
+		*rc = -ENODEV;
+		return;
+	}
+	if (pending_bit_stuck()) {
+		printk(EFX "change pending bit set\n");
+		*rc = -EIO;
+		return;
+	}
+	if (query_current_values_with_pending_wait(perproc)) {
+		*rc = -EIO;
+		return;
+	}
+	fidvid_msr_init();
+}
+#endif
+
+static int __init powernowk8_init(void)
+{
+	int smprc[num_online_cpus()];
+	int rc;
+	int i;
+
+	printk(IFX VERSION " (%d cpus)\n", num_online_cpus());
+
+	if (check_supported_cpu() == 0)
+		return -ENODEV;
+	if (pending_bit_stuck()) {
+		printk(EFX "change pending bit set\n");
+		return -EIO;
+	}
+
+	procs = kmalloc(sizeof(u8 *) * num_online_cpus(), GFP_KERNEL);
+	if (!procs) {
+		printk(EFX "procs memory alloc failure\n");
+		return -ENOMEM;
+	}
+	memset(procs, 0, sizeof(u8 *) * num_online_cpus());
+	rc = find_acpi_table();
+	if (rc) {
+		cleanup_procs();
+		return rc;
+	}
+
+	for (i=0; i<num_online_cpus(); i++) {
+		if (procs[i]==0) {
+			printk(EFX "Error procs 0 for %d\n", i);
+			cleanup_procs();
+			return -ENOMEM;
+		}
+	}
+
+	if (query_current_values_with_pending_wait(procs[0])) {
+		cleanup_procs();
+		return -EIO;
+	}
+	fidvid_msr_init();
+        if (num_online_cpus() > 1) {
+		memset(smprc, 0, sizeof(smprc));
+		smp_call_function(smp_k8_init, &smprc, 0, 1);
+		for (i=0; i<num_online_cpus(); i++) {
+			if (smprc[i]) {
+				cleanup_procs();
+				return smprc[i];
+			}
+		}
+	}
+	for (i=0; i<num_online_cpus(); i++)
+		dprintk(DFX "at init%d : fid %x vid %x\n", i,
+			procs[i][PP_OFF_CFID], procs[i][PP_OFF_CVID] );
+
+	return cpufreq_register_driver(&cpufreq_amd64_driver);
+}
+
+static void __exit powernowk8_exit(void)
+{
+	int pollwait = num_online_cpus() == 1 ? 1 : 0;
+	u8 *perproc = procs[0];
+	struct pstate *pst = (struct pstate *)(perproc + PP_OFF_BYTES);
+        u32 maxidx = perproc[PP_OFF_NUMPS] - seenrst;
+	u16 rf = pst[maxidx].freq;
+	u16 minfreq = pst[perproc[PP_OFF_NUMPS]-1].freq;
+	u16 maxfreq = pst[maxidx].freq;
+
+	dprintk(IFX "powernowk8_exit, pollflg=%x\n", pollflg);
+
+	/* do not unload the driver until we are certain the poller is gone */
+	while (pollwait) {
+		down(&poll_sem);
+		if ((pollflg == POLLER_RUNNING) || (pollflg == POLLER_UNLOAD)) {
+			pollflg = POLLER_UNLOAD;
+		}
+		else {
+			pollflg = POLLER_DEAD;
+			pollwait = 0;
+		}
+		up(&poll_sem);
+		schedule();
+	}
+
+	/* need to be on a battery frequency when the module is unloaded */
+	pst += maxidx;
+	if (seenrst && (perproc[PP_OFF_CFID] > pst->fid )) {
+		if (POW_BAT == query_ac()) {
+			dprintk(DFX "unload emergency transition\n" );
+                        transition_frequency(perproc, &rf, &minfreq, &maxfreq);
+		}
+	}
+
+	cpufreq_unregister_driver(&cpufreq_amd64_driver);
+	cleanup_procs();
+}
+
+MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com>");
+MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver.");
+MODULE_LICENSE("GPL");
+
+module_init(powernowk8_init);
+module_exit(powernowk8_exit);
--- tmp/linux/arch/i386/kernel/cpu/cpufreq/powernow-k8-acpi.h	2004-03-03 12:30:40.000000000 +0100
+++ linux/arch/i386/kernel/cpu/cpufreq/powernow-k8-acpi.h	2004-03-03 12:38:17.000000000 +0100
@@ -0,0 +1,143 @@
+/*
+ *   (c) 2003, 2004 Advanced Micro Devices, Inc.
+ *  Your use of this code is subject to the terms and conditions of the
+ *  GNU general public license version 2. See "../../../../../COPYING" or
+ *  http://www.gnu.org/licenses/gpl.html
+ */
+
+/* processor's cpuid instruction support */
+#define CPUID_PROCESSOR_SIGNATURE             1	/* function 1               */
+#define CPUID_XFAM_MOD               0x0ff00ff0	/* xtended fam, fam + model */
+#define ATHLON64_XFAM_MOD            0x00000f40	/* xtended fam, fam + model */
+#define OPTERON_XFAM_MOD             0x00000f50	/* xtended fam, fam + model */
+#define CPUID_GET_MAX_CAPABILITIES   0x80000000
+#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
+#define P_STATE_TRANSITION_CAPABLE            6
+
+#define MSR_FIDVID_CTL      0xc0010041
+#define MSR_FIDVID_STAT     0xc0010042
+
+/* control MSR - low part */
+#define MSR_C_LO_INIT             0x00010000
+#define MSR_C_LO_NEW_VID          0x00001f00
+#define MSR_C_LO_NEW_FID          0x0000003f
+#define MSR_C_LO_VID_SHIFT        8
+
+/* control MSR - high part */
+#define MSR_C_HI_STP_GNT_TO       0x000fffff
+#define MSR_C_HI_STP_GNT_BENIGN   1
+
+/* status MSR - low part */
+#define MSR_S_LO_CHANGE_PENDING   0x80000000 /* cleared when completed */
+#define MSR_S_LO_MAX_RAMP_VID     0x1f000000
+#define MSR_S_LO_MAX_FID          0x003f0000
+#define MSR_S_LO_START_FID        0x00003f00
+#define MSR_S_LO_CURRENT_FID      0x0000003f
+
+/* status MSR - high part */
+#define MSR_S_HI_MAX_WORKING_VID  0x001f0000
+#define MSR_S_HI_START_VID        0x00001f00
+#define MSR_S_HI_CURRENT_VID      0x0000001f
+
+#define LO_FID_TABLE_TOP        6     /* valid fids exist in 2 tables */
+#define HI_FID_TABLE_BOTTOM     8
+#define LO_VCOFREQ_TABLE_TOP    1400  /* corresponding vco frequency values */
+#define HI_VCOFREQ_TABLE_BOTTOM 1600
+
+#define MIN_FREQ_RESOLUTION  200 /* fids jump by 2 matching freq jumps by 200 */
+#define FSTEP                  2
+#define KHZ                 1000
+
+#define MAX_FID 0x2a	/* Spec only gives FID values as far as 5 GHz */
+#define LEAST_VID 0x1e	/* Lowest (numerically highest) useful vid value */
+
+#define MIN_FREQ 800
+#define MAX_FREQ 5000
+
+#define INVALID_FID_MASK 0xc1
+#define INVALID_VID_MASK 0xe0
+
+#define STOP_GRANT_5NS    1 /* min memory access latency for voltage change   */
+#define MAXIMUM_VID_STEPS 1 /* Current cpus only allow a single step of 25mV  */
+#define VST_UNITS_20US   20 /* Voltage Stabalization Time is in units of 20us */
+
+#define PLL_LOCK_CONVERSION (1000/5) /* ms to ns, then divide by clock period */
+
+/* byte offsets into the perproc struct */
+#define PP_OFF_NUMPS  0   /* number of p-states      */
+#define PP_OFF_SHARE  1   /* index of shared control */
+#define PP_OFF_CVID   2   /* current voltage id      */
+#define PP_OFF_CFID   3   /* current frequency id    */
+#define PP_OFF_BYTES  4   /* size in bytes           */   
+
+struct pstate {    /* info on each performance state, per processor */
+	u16 freq;  /* frequency is in megahertz */
+	u8 fid;
+	u8 vid;
+	u8 irt;
+	u8 rvo;
+	u8 plllock;
+	u8 vidmvs;
+	u8 vstable;
+	u8 pad1;
+	u16 pad2;
+};
+
+/* Explanation of the perproc data structures:
+ * static u8 **procs; declared in the .c file is an array of pointers to u8.
+ * There is one such pointer for each cpu in the system.
+ * Each pointer points to 4 u8s (indexed by the PP_OFF constants above),
+ * followed by an array of struct pstate, where each processor may have
+ * a different number of entries in its array. I.e., processor 0 may have
+ * 3 pstates, processor 1 may have 5 pstates.
+ */
+
+struct proc_pss {  /* the acpi _PSS structure */
+	acpi_integer freq;
+	acpi_integer pow;
+	acpi_integer tlat;
+	acpi_integer blat;
+	acpi_integer cntl;
+	acpi_integer stat;
+};
+
+#define PSS_FMT_STR "NNNNNN"
+
+#define IRT_SHIFT      30
+#define RVO_SHIFT      28
+#define PLL_L_SHIFT    20
+#define MVS_SHIFT      18
+#define VST_SHIFT      11
+#define VID_SHIFT       6
+#define IRT_MASK        3
+#define RVO_MASK        3
+#define PLL_L_MASK   0x7f
+#define MVS_MASK        3
+#define VST_MASK     0x7f
+#define VID_MASK     0x1f
+#define FID_MASK     0x3f
+
+#define POW_AC  0  /* The power suppy states we care about - mains, battery,  */
+#define POW_BAT 1  /* or unknown, which presumably means that there is no     */
+#define POW_UNK 2  /* acpi support for the psr object, so there is no battery.*/
+
+#define POLLER_NOT_RUNNING 0  /* The state of the poller (which watches for   */
+#define POLLER_RUNNING     1  /* power transitions). It is only running if we */
+#define POLLER_UNLOAD      2  /* are on mains power, at a high frequency, and */
+#define POLLER_DEAD        3  /* if there are battery restrictions.           */
+
+#define PFX "powernow-k8: "
+#define DFX KERN_DEBUG PFX
+#define IFX KERN_INFO  PFX
+#define EFX KERN_ERR   PFX
+
+#ifdef DEBUG
+#define dprintk(msg...) printk(msg)
+#else
+#define dprintk(msg...) do { } while(0)
+#endif
+
+static void start_ac_poller(int frompoller);
+static int powernowk8_verify(struct cpufreq_policy *p);
+static int powernowk8_target(struct cpufreq_policy *p, unsigned t, unsigned r);
+static int __init powernowk8_cpu_init(struct cpufreq_policy *p);
 
-- 
When do you have a heart between your knees?
[Johanka's followup: and *two* hearts?]

^ permalink raw reply	[flat|nested] 21+ messages in thread
* RE: powernow-k8-acpi driver
@ 2004-03-03 23:48 paul.devriendt
  2004-03-04 12:35 ` Pavel Machek
  0 siblings, 1 reply; 21+ messages in thread
From: paul.devriendt @ 2004-03-03 23:48 UTC (permalink / raw)
  To: pavel, davej, cpufreq, linux-kernel, davej

>> you're aware of Dominik/Bruno's work on the 'acpilib'[1] stuff in this 
>> area right ? We'll need that anyway for Powernow-k7 and maybe longhaul 
>> too and its senseless duplicating this code.
> 
> That [1] looks like promise of url, but I don't see that url.

No, I am not aware of this, and I think Dominik is out for a few days.
If there is some sort of library functionality I can use and eliminate
code in the powernow-k8 driver, I will glad make the change to use it.

Paul.


^ permalink raw reply	[flat|nested] 21+ messages in thread
* RE: powernow-k8-acpi driver
@ 2004-03-03 23:57 paul.devriendt
  2004-03-04 13:44 ` Pavel Machek
  0 siblings, 1 reply; 21+ messages in thread
From: paul.devriendt @ 2004-03-03 23:57 UTC (permalink / raw)
  To: pavel, davej, cpufreq, linux-kernel, davej

> One more thing: is there any reason for "use-array-as-struct"?
> 
> static int query_current_values_with_pending_wait(u8 *perproc) { ...
>         perproc[PP_OFF_CVID] = hi & MSR_S_HI_CURRENT_VID;
>         perproc[PP_OFF_CFID] = lo & MSR_S_LO_CURRENT_FID; }
> 
> having 
> 
> struct cpu_power {
> 	int numps, share, cvid, cfid;
> 	char pstates[0];
> }
> 
> should do the trick...
> 							Pavel

Yes, I could get rid off the PP_OFF_crud using something like you
suggest. I'll do that while I am also doing the back port and get
them both out next week sometime.

Paul.


^ permalink raw reply	[flat|nested] 21+ messages in thread
* RE: powernow-k8-acpi driver
@ 2004-03-04  2:00 richard.brunner
  2004-03-04 23:16 ` Pavel Machek
  0 siblings, 1 reply; 21+ messages in thread
From: richard.brunner @ 2004-03-04  2:00 UTC (permalink / raw)
  To: pavel, davej, cpufreq, linux-kernel, paul.devriendt


> -----Original Message-----
> From: Pavel Machek [mailto:pavel@suse.cz] 
> 
> Hi!
> 
> >  > Dave, could you apply these? That are cleanups from Paul's new version
> >  > of driver (killed few unused defines, right names for MSR's
> >  > (hopefully!), more linux-like comments). No code changes.
> > 
> > Looks trivial enough. I just bounced it forward to Linus directly.
> > (I'm travelling this week, and bitkeeper on a laptop is unfunny)
> 
> :-)
> 
> >  > - *   (c) 2003 Advanced Micro Devices, Inc.
> >  > + *   (c) 2003, 2004 Advanced Micro Devices, Inc.
> >  >   *  Your use of this code is subject to the terms and conditions of the
> >  > - *  GNU general public license version 2. See "../../../COPYING" or
> >  > + *  GNU general public license version 2. See "../../../../../COPYING" or
> >  >   *  http://www.gnu.org/licenses/gpl.html
> >  >   */
> > 
> > This bit seems really silly though, but thats just my opinion 8-)
> > I'd just kill the ../'s completely.
> 
> Agreed, I assume AMD lawyers wanted that... If not, perhaps we can
> make it disappear?

Killing the ../'s is probably fine so long as we leave the 
"Your use ... version 2." and shrink the copying notice to 
"See COPYING or http ..."

We stopped paying our lawyers by the number of letters in 
copyright notices several months ago, so I think it is ok.


] -Rich ...
] AMD Fellow
] richard.brunner at amd com  


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2004-03-07 13:30 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-03-03 21:54 powernow-k8-acpi driver Pavel Machek
2004-03-03 22:27 ` Dave Jones
2004-03-03 22:35   ` Pavel Machek
2004-03-03 22:48     ` Dave Jones
2004-03-03 22:54       ` Pavel Machek
2004-03-03 23:36         ` Dave Jones
2004-03-04  0:07           ` Pavel Machek
2004-03-04  9:38             ` Russell King
2004-03-05 19:18             ` Bruno Ducrot
2004-03-07 13:15               ` Colin Marquardt
2004-03-03 23:09       ` Pavel Machek
2004-03-03 23:11       ` Pavel Machek
2004-03-03 23:39         ` Dave Jones
2004-03-03 23:45           ` Pavel Machek
2004-03-03 23:26       ` Pavel Machek
  -- strict thread matches above, loose matches on Subject: below --
2004-03-03 23:48 paul.devriendt
2004-03-04 12:35 ` Pavel Machek
2004-03-03 23:57 paul.devriendt
2004-03-04 13:44 ` Pavel Machek
2004-03-04  2:00 richard.brunner
2004-03-04 23:16 ` Pavel Machek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox