Index: conf/files.i386
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/conf/files.i386,v
retrieving revision 1.262
diff -u -d -r1.262 files.i386
--- conf/files.i386	14 Sep 2004 16:51:57 -0000	1.262
+++ conf/files.i386	16 Jan 2005 18:52:22 -0000
@@ -64,6 +64,9 @@
 # Enhanced SpeedStep
 defflag			ENHANCED_SPEEDSTEP
 
+# PowerNow K7
+defflag			POWERNOW_K7
+
 file	arch/i386/i386/autoconf.c
 file	arch/i386/i386/db_dbgreg.S	ddb | kstack_check_dr0
 file	arch/i386/i386/db_disasm.c	ddb
@@ -475,5 +478,8 @@
 # Enhanced SpeedStep
 file	arch/i386/i386/est.c		enhanced_speedstep
 defflag	opt_est.h	EST_FREQ_USERWRITE
+
+# PowerNow K7
+file   arch/i386/i386/pnow_k7.c		powernow_k7
 
 include "arch/i386/conf/majors.i386"
Index: i386/identcpu.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/identcpu.c,v
retrieving revision 1.16
diff -u -d -r1.16 identcpu.c
--- i386/identcpu.c	8 Aug 2004 05:21:01 -0000	1.16
+++ i386/identcpu.c	16 Jan 2005 18:52:23 -0000
@@ -41,6 +41,7 @@
 
 #include "opt_cputype.h"
 #include "opt_enhanced_speedstep.h"
+#include "opt_powernow_k7.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -137,6 +138,7 @@
 
 static void via_cpu_probe(struct cpu_info *);
 static void amd_family6_probe(struct cpu_info *);
+u_int amd_powernow_probe(struct cpu_info *);
 
 static const char *intel_family6_name(struct cpu_info *);
 
@@ -861,6 +863,20 @@
 	}
 }
 
+u_int
+amd_powernow_probe(struct cpu_info *ci)
+{
+	u_int32_t	eax, ebx, ecx, edx;
+
+	CPUID(0x80000007, eax, ebx, ecx, edx);
+
+	/* checking for Freq ID control (FID) and Voltage ID control (VID) */
+	if ((edx & (0x2 | 0x4)) > 0)
+		return (1);
+
+	return (0);
+}
+
 /*
  * Transmeta Crusoe LongRun Support by Tamotsu Hattori.
  * Port from FreeBSD-current(August, 2001) to NetBSD by tshiozak.
@@ -1374,5 +1390,11 @@
 			    cpuname);
 	}
 #endif /* ENHANCED_SPEEDSTEP */
+
+#ifdef POWERNOW_K7
+	if (amd_powernow_probe (ci)) {
+		pnowk7_init(ci);
+	}
+#endif /* POWERNOW_K7 */
 
 }
Index: i386/pnow_k7.c
===================================================================
RCS file: i386/pnow_k7.c
diff -N i386/pnow_k7.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ i386/pnow_k7.c	16 Jan 2005 18:52:24 -0000
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2004 Martin Végiard.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* AMD PowerNow! K7 driver */
+
+/* Sysctl related code was adapted from NetBSD's i386/est.c for compatibility */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+
+#include <dev/isa/isareg.h>
+
+#include <machine/cpu.h>
+#include <machine/isa_machdep.h>
+
+#define BIOS_START 		0xe0000
+#define BIOS_END		0x20000
+#define BIOS_LEN		BIOS_END - BIOS_START
+
+#define MSR_K7_CTL		0xC0010041
+#define CTL_SET_FID		0x0000000000010000ULL
+#define CTL_SET_VID		0x0000000000020000ULL
+
+#define cpufreq(x)	fsb * fid_codes[x] / 10
+
+struct psb_s {
+	char signature[10];  	/* AMDK7PNOW! */
+	uint8_t version;
+	uint8_t flags;
+	uint16_t ttime;		/* Min Settling time */
+	uint8_t reserved;
+	uint8_t n_pst;
+};
+
+struct pst_s {
+	cpuid_t cpuid;
+	uint8_t fsb;		/* Front Side Bus frequency (Mhz) */
+	uint8_t fid;		/* Max Frequency code */
+	uint8_t vid;		/* Max Voltage code */
+	uint8_t n_states;	/* Number of states */
+};	
+
+struct state_s {
+	uint8_t fid;  		/* Frequency code */
+	uint8_t vid;		/* Voltage code */
+};
+
+struct freq_table_s {
+	unsigned int frequency;
+	struct state_s *state;
+};
+
+/* Taken from powernow-k7.c/Linux by Dave Jones */
+static int fid_codes[32] = {
+	110, 115, 120, 125, 50, 55, 60, 65,
+	70, 75, 80, 85, 90, 95, 100, 105,
+	30, 190, 40, 200, 130, 135, 140, 210,
+	150, 225, 160, 165, 170, 180, -1, -1
+};
+
+/* Static variables */
+static unsigned int fsb;
+static unsigned int cur_freq;
+static unsigned int ttime;
+static unsigned int n_states;
+static struct freq_table_s *freq_table;
+static int powernow_node_target, powernow_node_current;
+
+
+/* Prototypes */
+struct state_s *pnowk7_getstates(cpuid_t cpuid);
+int pnowk7_setstate(unsigned int freq);
+static int powernow_sysctl_helper(SYSCTLFN_ARGS);
+
+static int
+powernow_sysctl_helper(SYSCTLFN_ARGS)
+{
+        struct sysctlnode node;
+        int fq, oldfq, error;
+
+        if (freq_table == NULL)
+                return (EOPNOTSUPP);
+
+        node = *rnode;
+        node.sysctl_data = &fq;
+
+	oldfq = 0;
+        if (rnode->sysctl_num == powernow_node_target)
+                fq = oldfq = cur_freq;
+        else if (rnode->sysctl_num == powernow_node_current)
+                fq = cur_freq;
+        else
+                return (EOPNOTSUPP);
+
+        error = sysctl_lookup(SYSCTLFN_CALL(&node));
+        if (error || newp == NULL)
+                return (error);
+
+	/* support writing to ...frequency.target */
+        if (rnode->sysctl_num == powernow_node_target && fq != oldfq) {
+	
+		if (pnowk7_setstate(fq) == 0)
+		{
+			cur_freq = fq;
+		} else
+			aprint_normal("\nInvalid CPU frequency request\n");
+        }
+
+        return (0);
+}
+
+struct state_s *
+pnowk7_getstates(cpuid_t cpuid)
+{
+	unsigned int i, j;
+	char *ptr;
+	
+	struct psb_s *psb;
+	struct pst_s *pst;
+
+	/*
+	 * Look in the 0xe0000 - 0x20000 physical address
+	 * range for the pst tables; 16 byte blocks
+	 */	
+
+	ptr = (char *)ISA_HOLE_VADDR(BIOS_START);
+
+	for (i = 0; i < BIOS_LEN; i += 16, ptr += 16) {
+		if (memcmp(ptr, "AMDK7PNOW!", 10) == 0)	
+		{
+			psb = (struct psb_s *) ptr;
+			ptr += sizeof(struct psb_s);			
+
+			ttime = psb->ttime;
+			
+			/* Only this version is supported */
+			if (psb->version != 0x12)
+				return 0;
+
+			/* Find the right PST */
+			for (j = 0; j < psb->n_pst; j++) {
+				pst = (struct pst_s *) ptr;
+				ptr += sizeof(struct pst_s);
+
+				/* Use the first PST with matching CPUID */
+				if ((cpuid == pst->cpuid) || (cpuid == (pst->cpuid - 0x100)))
+				/* XXX some braindead BIOS seems to expect signature 0x780 from Athlon
+				 * let's try to subtract 0x100 from the BIOS supplied values to fix */
+				{
+					/*
+					 * XXX I need more info on this.
+					 * For now, let's just ignore it
+					 */
+					if ((cpuid & 0xFF) == 0x60)
+						return 0;
+
+					fsb = pst->fsb;
+					n_states = pst->n_states;
+					return (struct state_s *) ptr;
+				} else
+					ptr += sizeof(struct state_s) * pst->n_states;
+			}
+			
+			/* aprint_normal("No match was found for your CPUID\n"); */
+			return 0;
+		}
+	}
+
+	/* aprint_normal("Power state table not found\n"); */
+	return 0;
+}
+
+int
+pnowk7_setstate(unsigned int freq)
+{
+	unsigned int i;
+	u_int32_t sgtc, vid, fid;
+	u_int64_t ctl;
+
+	vid = fid = 0;	
+
+	for (i = 0; i < n_states; i++) {
+		/* Do we know how to set that frequency? */
+		if (freq_table[i].frequency == freq)
+		{
+			fid = freq_table[i].state->fid;
+			vid = freq_table[i].state->vid;
+		}
+ 	}
+	
+	if (fid == 0 || vid == 0)
+		return -1;
+
+	/* Get CTL and only modify fid/vid/sgtc */
+	ctl = rdmsr(MSR_K7_CTL);
+	
+	/* FID */	
+	ctl &= 0xFFFFFFFFFFFFFF00ULL;
+	ctl |= fid;
+
+	/* VID */
+	ctl &= 0xFFFFFFFFFFFF00FFULL;
+	ctl |= vid << 8;
+
+	/* SGTC */
+	if ((sgtc = ttime * 100) < 10000) sgtc = 10000;
+	ctl &= 0xFFF00000FFFFFFFFULL;
+	ctl |= (u_int64_t)sgtc << 32; 
+
+	if (cur_freq > freq) {
+		wrmsr(MSR_K7_CTL, ctl | CTL_SET_FID);
+		wrmsr(MSR_K7_CTL, ctl | CTL_SET_VID);
+	} else {
+		wrmsr(MSR_K7_CTL, ctl | CTL_SET_VID);
+		wrmsr(MSR_K7_CTL, ctl | CTL_SET_FID);
+	}
+	
+	ctl = rdmsr(MSR_K7_CTL);
+
+	return 0;
+}
+
+void
+pnowk7_init(struct cpu_info *ci)
+{
+	int rc;
+	unsigned int i, freq_names_len, len = 0;
+	char *freq_names;
+	struct sysctlnode *node, *pnownode, *freqnode;
+	struct state_s *s = pnowk7_getstates(ci->ci_signature);
+	char *cpuname = ci->ci_dev->dv_xname;
+
+	if (s == 0) {
+		/* aprint_normal("AMD PowerNow not supported\n"); */
+		return;
+	}
+
+	freq_names_len =  n_states * (sizeof("9999 ")-1) + 1;
+	freq_names = malloc(freq_names_len, M_SYSCTLDATA, M_WAITOK);
+	
+	freq_table = malloc(sizeof(struct freq_table_s) * n_states, \
+		M_TEMP, M_WAITOK);		
+
+
+	for (i = 0; i < n_states; i++, s++) {		
+		freq_table[i].frequency = cpufreq(s->fid);
+		freq_table[i].state = s;
+
+                len += snprintf(freq_names + len, freq_names_len - len, "%d%s",
+                    freq_table[i].frequency, i < n_states - 1 ? " " : "");
+	}
+
+	/* On bootup the frequency should be at it's max */
+	cur_freq = freq_table[i-1].frequency;	
+
+	/* Create sysctl machdep.powernow.frequency. */
+        if ((rc = sysctl_createv(NULL, 0, NULL, &node,
+            CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
+            NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) != 0)
+                goto err;
+
+        if ((rc = sysctl_createv(NULL, 0, &node, &pnownode,
+            0, CTLTYPE_NODE, "powernow", NULL,
+            NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+                goto err;
+
+         if ((rc = sysctl_createv(NULL, 0, &pnownode, &freqnode,
+            0, CTLTYPE_NODE, "frequency", NULL,
+            NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+                goto err;
+
+        if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
+            CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
+            powernow_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+                goto err;
+        powernow_node_target = node->sysctl_num;
+
+        if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
+            0, CTLTYPE_INT, "current", NULL,
+            powernow_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+                goto err;
+        powernow_node_current = node->sysctl_num;
+
+        if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
+            0, CTLTYPE_STRING, "available", NULL,
+            NULL, 0, freq_names, freq_names_len, CTL_CREATE, CTL_EOL)) != 0)
+                goto err;
+
+	/* aprint_normal("AMD PowerNow supported current frequency : %d Mhz\n", \
+		cur_freq); */
+	aprint_normal("%s: AMD PowerNow! Technology supported\n", cpuname);
+	aprint_normal("%s: available frequencies (Mhz): %s\n", cpuname, freq_names); 
+	return;	
+  err:
+	aprint_normal("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
+}
Index: include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/include/cpu.h,v
retrieving revision 1.116
diff -u -d -r1.116 cpu.h
--- include/cpu.h	22 Sep 2004 11:32:02 -0000	1.116
+++ include/cpu.h	16 Jan 2005 18:52:24 -0000
@@ -422,6 +422,9 @@
 /* est.c */
 void	est_init(struct cpu_info *);
 
+/* pnow_k7.c */
+void	pnowk7_init(struct cpu_info *);
+
 #endif /* _KERNEL */
 
 /*
