Subject: Re: Re: Powernow support?
To: Juan RP <juan@xtrarom.org>
From: Joel CARNAT <joel@carnat.net>
List: port-amd64
Date: 07/31/2006 11:19:06
--WhfpMioaduB5tiZL
Content-Type: multipart/mixed; boundary="gBBFr7Ir9EOA20Yy"
Content-Disposition: inline


--gBBFr7Ir9EOA20Yy
Content-Type: text/plain; charset=iso-8859-15
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

Hi,

as my AMD64 box is running /i386, I had to apply your patches to the
i386 arch. maybe I did not made it well... but the kernel does not seem
to recognize anything new :
########################################################################
cpu0 at mainbus0: apid 0 (boot processor)
cpu0: AMD Unknown K7 (Athlon) (686-class), 2009.87 MHz, id 0xf4a
cpu0: features 78bfbff<FPU,VME,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR>
cpu0: features 78bfbff<PGE,MCA,CMOV,PAT,PSE36,MPC,MMX>
cpu0: features 78bfbff<FXSR,SSE,SSE2>
cpu0: "AMD Athlon(tm) 64 Processor 3200+"
########################################################################
machdep.biosbasemem =3D 638
machdep.biosextmem =3D 1047488
machdep.nkpde =3D 98
machdep.booted_kernel =3D netbsd
machdep.diskinfo: 80:234441648(1023/255/63),2  sd0 sd1 wd0:80
machdep.fpu_present =3D 1
machdep.osfxsr =3D 1
machdep.sse =3D 1
machdep.sse2 =3D 1
machdep.cpu_brand =3D AMD Athlon(tm) 64 Processor 3200+
machdep.sleep_state =3D 0
machdep.acpi_vbios_reset =3D 1
########################################################################

I'll try to install an amd64 version on an USB external drive when I'll
be back home and let you know if it is better.

On Sun, Jul 30 2006 - 23:14, Juan RP wrote:
> On Sun, 23 Jul 2006 10:01:59 -0700
> Scott Ellis <scotte@warped.com> wrote:
>=20
> > It's been awhile since I've seen this subject broached, so I wanted to=
=20
> > check the status.  Are there any pending patches to enable PowerNow=20
> > support on amd64?  I see that the i386 PowerNow support is in, but=20
> > nothing in-tree for amd64.  Looking at powernow-k7, it looks pretty=20
> > similar to the OpenBSD version.  OpenBSD also has powernow-k8 support.
> >=20
> > Google (and a search of the list archives) didn't turn up any "work in=
=20
> > progress" on Amd64 PowerNow support, so I figured I'd ask here: Is=20
> > anyone working on this, and in need of some feedback/broader testing?
>=20
> Ok, this is just the patch I made today... based in the openbsd powernow_=
k8.c
> driver for amd64.
>=20
> Anyone wants to review the patch or try to fix the code if something is w=
rong?
>=20
> http://www.xtrarom.org/~juan/powernow_k8.patch
>=20
> A precompiled NetBSD/amd64 kernel based on GENERIC_ACPI plus
> "options POWERNOW_K8" is available at:
>=20
> http://www.xtrarom.org/~juan/netbsd_amd64_powernow.gz
>=20
> I don't have amd64 hw.... so it's untested.

--gBBFr7Ir9EOA20Yy
Content-Type: text/plain; charset=iso-8859-15
Content-Disposition: attachment; filename="powernow_k8.i386.diff"
Content-Transfer-Encoding: quoted-printable

--- sys/arch/i386/conf/files.i386.orig	2006-07-29 20:37:29.000000000 +0200
+++ sys/arch/i386/conf/files.i386	2006-07-31 00:25:53.000000000 +0200
@@ -66,4 +66,7 @@
 defflag			POWERNOW_K7
=20
+# AMD Powernow/Cool`n'Quiet Technology
+defflag opt_powernow_k8.h	POWERNOW_K8
+
 file	arch/i386/i386/autoconf.c
 file	arch/i386/i386/db_dbgreg.S	ddb | kstack_check_dr0
@@ -520,3 +523,6 @@
 file   arch/i386/i386/powernow_k7.c	powernow_k7
=20
+# AMD PowerNow K8
+file	arch/i386/i386/powernow_k8.c	powernow_k8
+
 include "arch/i386/conf/majors.i386"
--- sys/arch/i386/i386/mainbus.c.orig	2006-07-29 20:37:31.000000000 +0200
+++ sys/arch/i386/i386/mainbus.c	2006-07-31 00:20:27.000000000 +0200
@@ -58,4 +58,5 @@
 #include "opt_acpi.h"
 #include "opt_mpbios.h"
+#include "opt_powernow_k8.h"
 #include "opt_pcifixup.h"
=20
@@ -355,4 +356,8 @@
 #endif
=20
+#if NPOWERNOW_K8 > 0
+	k8_powernow_init();
+#endif
+
 #if NAPMBIOS > 0
 #if NACPI > 0
--- sys/arch/i386/i386/powernow_k8.c.orig	2006-07-31 00:21:28.000000000 +02=
00
+++ sys/arch/i386/i386/powernow_k8.c	2006-07-31 00:23:03.000000000 +0200
@@ -0,0 +1,539 @@
+/*	$NetBSD$ */
+/*	$OpenBSD: powernow-k8.c,v 1.8 2006/06/16 05:58:50 gwk Exp $ */
+
+/*
+ * Copyright (c) 2004 Martin V=E9giard.
+ * 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 WARRANTI=
ES
+ * 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 US=
E,
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004-2005 Bruno Ducrot
+ * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTI=
ES
+ * 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 US=
E,
+ * 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 K8 driver */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#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/isa_machdep.h>
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/bus.h>
+
+#define SYSCTLLOG			NULL
+
+#define BIOS_START			0xe0000
+#define	BIOS_LEN			0x20000
+
+/*
+ * MSRs and bits used by Powernow technology
+ */
+#define MSR_AMDK7_FIDVID_CTL		0xc0010041
+#define MSR_AMDK7_FIDVID_STATUS		0xc0010042
+
+/* Bitfields used by K8 */
+
+#define PN8_CTR_FID(x)			((x) & 0x3f)
+#define PN8_CTR_VID(x)			(((x) & 0x1f) << 8)
+#define PN8_CTR_PENDING(x)		(((x) & 1) << 32)
+
+#define PN8_STA_CFID(x)			((x) & 0x3f)
+#define PN8_STA_SFID(x)			(((x) >> 8) & 0x3f)
+#define PN8_STA_MFID(x)			(((x) >> 16) & 0x3f)
+#define PN8_STA_PENDING(x)		(((x) >> 31) & 0x01)
+#define PN8_STA_CVID(x)			(((x) >> 32) & 0x1f)
+#define PN8_STA_SVID(x)			(((x) >> 40) & 0x1f)
+#define PN8_STA_MVID(x)			(((x) >> 48) & 0x1f)
+
+/* Reserved1 to powernow k8 configuration */
+#define PN8_PSB_TO_RVO(x)		((x) & 0x03)
+#define PN8_PSB_TO_IRT(x)		(((x) >> 2) & 0x03)
+#define PN8_PSB_TO_MVS(x)		(((x) >> 4) & 0x03)
+#define PN8_PSB_TO_BATT(x)		(((x) >> 6) & 0x03)
+
+/* ACPI ctr_val status register to powernow k8 configuration */
+#define ACPI_PN8_CTRL_TO_FID(x)		((x) & 0x3f)
+#define ACPI_PN8_CTRL_TO_VID(x)		(((x) >> 6) & 0x1f)
+#define ACPI_PN8_CTRL_TO_VST(x)		(((x) >> 11) & 0x1f)
+#define ACPI_PN8_CTRL_TO_MVS(x)		(((x) >> 18) & 0x03)
+#define ACPI_PN8_CTRL_TO_PLL(x)		(((x) >> 20) & 0x7f)
+#define ACPI_PN8_CTRL_TO_RVO(x)		(((x) >> 28) & 0x03)
+#define ACPI_PN8_CTRL_TO_IRT(x)		(((x) >> 30) & 0x03)
+
+#define WRITE_FIDVID(fid, vid, ctrl)	\
+	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
+	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
+
+
+#define COUNT_OFF_IRT(irt)	DELAY(10 * (1 << (irt)))
+#define COUNT_OFF_VST(vst)	DELAY(20 * (vst))
+
+#define FID_TO_VCO_FID(fid)	\
+	(((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
+
+#define POWERNOW_MAX_STATES		16
+
+struct k8pnow_state {
+	int freq;
+	uint8_t fid;
+	uint8_t vid;
+};
+
+struct k8pnow_cpu_state {
+	struct k8pnow_state state_table[POWERNOW_MAX_STATES];
+	unsigned int n_states;
+	unsigned int sgtc;
+	unsigned int vst;
+	unsigned int mvs;
+	unsigned int pll;
+	unsigned int rvo;
+	unsigned int irt;
+	int low;
+};
+
+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 {
+	uint32_t cpuid;
+	uint8_t pll;
+	uint8_t fid;
+	uint8_t vid;
+	uint8_t n_states;
+};
+
+struct k8pnow_cpu_state *k8pnow_current_state;
+struct k8pnow_state	*freq_table;
+unsigned int 		cur_freq;
+int			powernow_node_target, powernow_node_current;
+char			*freq_names;
+size_t 			freq_names_len;
+
+/*
+ * Prototypes
+ */
+int powernow_sysctl_helper(SYSCTLFN_PROTO);
+int k8pnow_read_pending_wait(uint64_t *);
+int k8pnow_decode_pst(struct k8pnow_cpu_state *, uint8_t *);
+int k8pnow_states(struct k8pnow_cpu_state *, uint32_t, unsigned int,
+    unsigned int);
+int k8_powernow_setperf(unsigned int);
+
+int powernow_sysctl_helper(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	int fq, oldfq, error;
+
+	if (freq_table =3D=3D NULL)
+		return EOPNOTSUPP;
+
+	node =3D *rnode;
+	node.sysctl_data =3D &fq;
+
+	oldfq =3D 0;
+	if (rnode->sysctl_num =3D=3D powernow_node_target)
+		fq =3D oldfq =3D cur_freq;
+	else if (rnode->sysctl_num =3D=3D powernow_node_current)
+		fq =3D cur_freq;
+	else
+		return EOPNOTSUPP;
+
+	error =3D sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error || newp =3D=3D NULL)
+		return error;
+
+	/* support writing to ...frequency.target */
+	if (rnode->sysctl_num =3D=3D powernow_node_target && fq !=3D oldfq) {
+		if (k8_powernow_setperf(fq) =3D=3D 0)
+			cur_freq =3D fq;
+		else
+			aprint_normal("\nInvalid CPU frequency request\n");
+	}
+
+	printf("%s called!\n", __func__);
+
+	return 0;
+}
+
+int
+k8pnow_read_pending_wait(uint64_t *status)
+{
+	unsigned int i =3D 1000;
+
+	while (i--) {
+		*status =3D rdmsr(MSR_AMDK7_FIDVID_STATUS);
+		if (!PN8_STA_PENDING(*status)) {
+			printf("%s called with 0\n", __func__);
+			return 0;
+		}
+	}
+	printf("k8pnow_read_pending_wait: change pending stuck.\n");
+	return 1;
+}
+
+int
+k8_powernow_setperf(unsigned int freq)
+{
+	unsigned int i, low, high;
+	uint64_t status;
+	int cfid, cvid, fid =3D 0, vid =3D 0;
+	int rvo;
+	u_int val;
+	struct k8pnow_cpu_state *cstate;
+
+	/*
+	 * We dont do a k8pnow_read_pending_wait here, need to ensure that the
+	 * change pending bit isn't stuck,
+	 */
+	status =3D rdmsr(MSR_AMDK7_FIDVID_STATUS);
+	if (PN8_STA_PENDING(status))
+		return 1;
+	cfid =3D PN8_STA_CFID(status);
+	cvid =3D PN8_STA_CVID(status);
+
+	cstate =3D k8pnow_current_state;
+	low =3D cstate->state_table[0].freq;
+	high =3D cstate->state_table[cstate->n_states-1].freq;
+
+	for (i =3D 0; i < cstate->n_states; i++) {
+		if (cstate->state_table[i].freq =3D=3D freq) {
+			fid =3D cstate->state_table[i].fid;
+			vid =3D cstate->state_table[i].vid;
+			break;
+		}
+	}
+
+	if (fid =3D=3D cfid && vid =3D=3D cvid)
+		return 0;
+
+	/*
+	 * Phase 1: Raise core voltage to requested VID if frequency is
+	 * going up.
+	 */
+	while (cvid > vid) {
+		val =3D cvid - (1 << cstate->mvs);
+		WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
+		if (k8pnow_read_pending_wait(&status))
+			return 1;
+		cvid =3D PN8_STA_CVID(status);
+		COUNT_OFF_VST(cstate->vst);
+	}
+
+	/* ... then raise to voltage + RVO (if required) */
+	for (rvo =3D cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
+		/* XXX It's not clear from spec if we have to do that
+		 * in 0.25 step or in MVS.  Therefore do it as it's done
+		 * under Linux */
+		WRITE_FIDVID(cfid, cvid - 1, 1ULL);
+		if (k8pnow_read_pending_wait(&status))
+			return 1;
+		cvid =3D PN8_STA_CVID(status);
+		COUNT_OFF_VST(cstate->vst);
+	}
+
+	/* Phase 2: change to requested core frequency */
+	if (cfid !=3D fid) {
+		u_int vco_fid, vco_cfid;
+
+		vco_fid =3D FID_TO_VCO_FID(fid);
+		vco_cfid =3D FID_TO_VCO_FID(cfid);
+
+		while (abs(vco_fid - vco_cfid) > 2) {
+			if (fid > cfid) {
+				if (cfid > 6)
+					val =3D cfid + 2;
+				else
+					val =3D FID_TO_VCO_FID(cfid) + 2;
+			} else
+				val =3D cfid - 2;
+			WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5);
+
+			if (k8pnow_read_pending_wait(&status))
+				return 1;
+			cfid =3D PN8_STA_CFID(status);
+			COUNT_OFF_IRT(cstate->irt);
+
+			vco_cfid =3D FID_TO_VCO_FID(cfid);
+		}
+
+		WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
+		if (k8pnow_read_pending_wait(&status))
+			return 1;
+		cfid =3D PN8_STA_CFID(status);
+		COUNT_OFF_IRT(cstate->irt);
+	}
+
+	/* Phase 3: change to requested voltage */
+	if (cvid !=3D vid) {
+		WRITE_FIDVID(cfid, vid, 1ULL);
+		if (k8pnow_read_pending_wait(&status))
+			return 1;
+		cvid =3D PN8_STA_CVID(status);
+		COUNT_OFF_VST(cstate->vst);
+	}
+
+	if (cfid =3D=3D fid || cvid =3D=3D vid)
+		freq =3D cstate->state_table[i].freq;
+
+	printf("%s called\n", __func__);
+	return 0;
+}
+
+/*
+ * Given a set of pair of fid/vid, and number of performance states,
+ * compute state_table via an insertion sort.
+ */
+int
+k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p)
+{
+	int i, j, n;
+	struct k8pnow_state state;
+	for (n =3D 0, i =3D 0; i < cstate->n_states; i++) {
+		state.fid =3D *p++;
+		state.vid =3D *p++;
+=09
+		/*
+		 * The minimum supported frequency per the data sheet is 800MHz
+		 * The maximum supported frequency is 5000MHz.
+		 */
+		state.freq =3D 800 + state.fid * 100;
+		j =3D n;
+		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
+			memcpy(&cstate->state_table[j],
+			    &cstate->state_table[j - 1],
+			    sizeof(struct k8pnow_state));
+			--j;
+		}
+		memcpy(&cstate->state_table[j], &state,
+		    sizeof(struct k8pnow_state));
+		n++;
+	}
+	return 1;
+}
+
+int
+k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
+    unsigned int fid, unsigned int vid)
+{
+	struct psb_s *psb;
+	struct pst_s *pst;
+	uint8_t *p;
+	int i;
+
+	for (p =3D (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
+	    p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p +=3D 16) {
+		if (memcmp(p, "AMDK7PNOW!", 10) =3D=3D 0) {
+			psb =3D (struct psb_s *)p;
+			if (psb->version !=3D 0x14)
+				return 0;
+
+			cstate->vst =3D psb->ttime;
+			cstate->rvo =3D PN8_PSB_TO_RVO(psb->reserved);
+			cstate->irt =3D PN8_PSB_TO_IRT(psb->reserved);
+			cstate->mvs =3D PN8_PSB_TO_MVS(psb->reserved);
+			cstate->low =3D PN8_PSB_TO_BATT(psb->reserved);
+			p+=3D sizeof(struct psb_s);
+
+			for(i =3D 0; i < psb->n_pst; ++i) {
+				pst =3D (struct pst_s *) p;
+
+				cstate->pll =3D pst->pll;
+				cstate->n_states =3D pst->n_states;
+				if (cpusig =3D=3D pst->cpuid &&
+				    pst->fid =3D=3D fid && pst->vid =3D=3D vid) {
+					return (k8pnow_decode_pst(cstate,
+					    p+=3D sizeof (struct pst_s)));
+				}
+				p +=3D sizeof(struct pst_s) + 2 * cstate->n_states;
+			}
+		}
+	}
+
+	return 0;
+
+}
+
+void
+k8_powernow_init(void)
+{
+	uint64_t status;
+	u_int maxfid, maxvid, i;
+	const struct sysctlnode *node, *pnownode, *freqnode;
+	struct k8pnow_cpu_state *cstate;
+	struct k8pnow_state *state;
+	struct cpu_info *ci;
+	char *cpuname;
+	const char *techname;
+	size_t len;
+	int rc;
+
+	ci =3D curcpu();
+
+	freq_names_len =3D len =3D 0;
+	cpuname =3D ci->ci_dev->dv_xname;
+
+
+	cstate =3D malloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_NOWAIT);
+	if (!cstate)
+		return;
+
+	status =3D rdmsr(MSR_AMDK7_FIDVID_STATUS);
+	maxfid =3D PN8_STA_MFID(status);
+	maxvid =3D PN8_STA_MVID(status);
+
+	/*
+	* If start FID is different to max FID, then it is a
+	* mobile processor.  If not, it is a low powered desktop
+	* processor.
+	*/
+	if (PN8_STA_SFID(status) !=3D PN8_STA_MFID(status))
+		techname =3D "PowerNow!";
+	else
+		techname =3D "Cool`n'Quiet";
+
+	freq_names_len =3D cstate->n_states *(sizeof("9999 ")-1) + 1;
+	freq_names =3D malloc(freq_names_len, M_SYSCTLDATA, M_WAITOK);
+
+	if (freq_names =3D=3D NULL)
+		panic("%s: freq_names alloc not possible", __func__);
+
+	freq_table =3D malloc(sizeof(struct k8pnow_state) *=20
+	    cstate->n_states , M_TEMP, M_WAITOK);
+
+	if (freq_table =3D=3D NULL)
+		panic("%s: freq_table alloc not possible", __func__);
+
+	if (k8pnow_states(cstate, ci->ci_signature, maxfid, maxvid)) {
+		if (cstate->n_states) {
+			for (i =3D cstate->n_states; i > 0; i--) {
+				state =3D &cstate->state_table[i-1];
+				freq_table[i].freq =3D state->freq;
+
+				len +=3D snprintf(freq_names + len,
+				    freq_names_len - len, "%d%s",
+				    freq_table[i].freq,
+				    i < cstate->n_states - 1 ? " " : "");
+			}
+			k8pnow_current_state =3D cstate;
+			printf("%s: cstate->n_states =3D %d", __func__,
+			    cstate->n_states);
+		}
+	}
+
+	/* Create sysctl machdep.powernow.frequency. */
+	if ((rc =3D sysctl_createv(SYSCTLLOG, 0, NULL, &node,
+	    CTLFLAG_PERMANENT,
+	    CTLTYPE_NODE, "machdep", NULL,
+	    NULL, 0, NULL, 0,
+	    CTL_MACHDEP, CTL_EOL)) !=3D 0)
+		goto err;
+
+	if ((rc =3D sysctl_createv(SYSCTLLOG, 0, &node, &pnownode,
+	    0,
+	    CTLTYPE_NODE, "powernow", NULL,
+	    NULL, 0, NULL, 0,
+	    CTL_CREATE, CTL_EOL)) !=3D 0)
+		goto err;
+
+	if ((rc =3D sysctl_createv(SYSCTLLOG, 0, &freqnode, &node,
+	    CTLFLAG_READWRITE,
+	    CTLTYPE_INT, "target", NULL,
+	    powernow_sysctl_helper, 0, NULL, 0,
+	    CTL_CREATE, CTL_EOL)) !=3D 0)
+		goto err;
+
+	powernow_node_target =3D node->sysctl_num;
+
+	if ((rc =3D sysctl_createv(SYSCTLLOG, 0, &freqnode, &node,
+	    0,
+	    CTLTYPE_INT, "current", NULL,
+	    powernow_sysctl_helper, 0, NULL, 0,
+	    CTL_CREATE, CTL_EOL)) !=3D 0)
+		goto err;
+
+	powernow_node_current =3D node->sysctl_num;
+
+	if ((rc =3D sysctl_createv(SYSCTLLOG, 0, &freqnode, &node,
+	    0,
+	    CTLTYPE_STRING, "available", NULL,
+	    NULL, 0, freq_names, freq_names_len,
+	    CTL_CREATE, CTL_EOL)) !=3D 0)
+	goto err;
+
+	/* On bootup the frequency should be at its max */
+	cur_freq =3D cstate->state_table[i-1].freq;
+	k8_powernow_setperf(cur_freq);
+
+	aprint_normal("\n");
+	aprint_normal("%s: AMD %s Technology\n", cpuname, techname);
+	aprint_normal("%s: available frequencies (Mhz): %s\n", cpuname,
+			freq_names);
+	aprint_normal("%s: current frequency (Mhz): %d", cpuname, cur_freq);
+
+	return;
+
+  err:
+	if (freq_names)
+		free(freq_names, M_SYSCTLDATA);
+
+	if (freq_table)
+		free(freq_table, M_TEMP);
+}
+
--- sys/arch/i386/include/cpu.h.orig	2006-06-08 20:44:12.000000000 +0200
+++ sys/arch/i386/include/cpu.h	2006-07-31 00:27:25.000000000 +0200
@@ -428,4 +428,7 @@
 void	pnowk7_init(struct cpu_info *);
=20
+/* powernow_k8.c */
+void	k8_powernow_init(void);
+
 #endif /* _KERNEL */
=20

--gBBFr7Ir9EOA20Yy--

--WhfpMioaduB5tiZL
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (NetBSD)

iD8DBQFEzcsK0/VH7L7F7Y4RAnYiAKCEkZwn8bk6ydIMtgqyNzgCjvYSZwCfWGqL
cly+CkaZZLNhITBBVJ8mQJ0=
=ylnR
-----END PGP SIGNATURE-----

--WhfpMioaduB5tiZL--