xref: /openbsd-src/sys/arch/i386/i386/powernow-k8.c (revision f4e7063748a2ac72b2bab4389c0a7efc72d82189)
1*f4e70637Sjsg /*	$OpenBSD: powernow-k8.c,v 1.35 2023/01/30 10:49:05 jsg Exp $ */
296771376Sderaadt 
37aa8d3a7Stedu /*
47aa8d3a7Stedu  * Copyright (c) 2004 Martin V�giard.
57aa8d3a7Stedu  * Copyright (c) 2004-2005 Bruno Ducrot
67aa8d3a7Stedu  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
77aa8d3a7Stedu  *
87aa8d3a7Stedu  * Redistribution and use in source and binary forms, with or without
97aa8d3a7Stedu  * modification, are permitted provided that the following conditions
107aa8d3a7Stedu  * are met:
117aa8d3a7Stedu  * 1. Redistributions of source code must retain the above copyright
127aa8d3a7Stedu  *    notice, this list of conditions and the following disclaimer.
137aa8d3a7Stedu  * 2. Redistributions in binary form must reproduce the above copyright
147aa8d3a7Stedu  *    notice, this list of conditions and the following disclaimer in the
157aa8d3a7Stedu  *    documentation and/or other materials provided with the distribution.
167aa8d3a7Stedu  *
177aa8d3a7Stedu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
187aa8d3a7Stedu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
197aa8d3a7Stedu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
207aa8d3a7Stedu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
217aa8d3a7Stedu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
227aa8d3a7Stedu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
237aa8d3a7Stedu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
247aa8d3a7Stedu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
257aa8d3a7Stedu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
267aa8d3a7Stedu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
277aa8d3a7Stedu  */
287aa8d3a7Stedu /* AMD POWERNOW K8 driver */
297aa8d3a7Stedu 
307aa8d3a7Stedu #include <sys/param.h>
317aa8d3a7Stedu #include <sys/systm.h>
327aa8d3a7Stedu #include <sys/malloc.h>
337aa8d3a7Stedu #include <sys/sysctl.h>
347aa8d3a7Stedu 
357aa8d3a7Stedu #include <dev/isa/isareg.h>
367aa8d3a7Stedu #include <i386/isa/isa_machdep.h>
377aa8d3a7Stedu 
387aa8d3a7Stedu #include <machine/cpufunc.h>
397aa8d3a7Stedu #include <machine/bus.h>
407aa8d3a7Stedu 
413cd93248Sgwk #include "acpicpu.h"
423cd93248Sgwk 
433cd93248Sgwk #if NACPICPU > 0
443cd93248Sgwk #include <dev/acpi/acpidev.h>
453cd93248Sgwk #endif
463cd93248Sgwk 
477aa8d3a7Stedu #define BIOS_START			0xe0000
487aa8d3a7Stedu #define	BIOS_LEN			0x20000
49668c25f3Sdlg #define BIOS_STEP			16
507aa8d3a7Stedu 
517aa8d3a7Stedu /*
52bda76761Stom  * MSRs and bits used by PowerNow! technology
537aa8d3a7Stedu  */
547aa8d3a7Stedu #define MSR_AMDK7_FIDVID_CTL		0xc0010041
557aa8d3a7Stedu #define MSR_AMDK7_FIDVID_STATUS		0xc0010042
56668c25f3Sdlg #define AMD_PN_FID_VID			0x06
577aa8d3a7Stedu 
587aa8d3a7Stedu /* Bitfields used by K8 */
597aa8d3a7Stedu 
607aa8d3a7Stedu #define PN8_CTR_FID(x)			((x) & 0x3f)
617aa8d3a7Stedu #define PN8_CTR_VID(x)			(((x) & 0x1f) << 8)
627aa8d3a7Stedu #define PN8_CTR_PENDING(x)		(((x) & 1) << 32)
637aa8d3a7Stedu 
647aa8d3a7Stedu #define PN8_STA_CFID(x)			((x) & 0x3f)
657aa8d3a7Stedu #define PN8_STA_SFID(x)			(((x) >> 8) & 0x3f)
667aa8d3a7Stedu #define PN8_STA_MFID(x)			(((x) >> 16) & 0x3f)
677aa8d3a7Stedu #define PN8_STA_PENDING(x)		(((x) >> 31) & 0x01)
687aa8d3a7Stedu #define PN8_STA_CVID(x)			(((x) >> 32) & 0x1f)
697aa8d3a7Stedu #define PN8_STA_SVID(x)			(((x) >> 40) & 0x1f)
707aa8d3a7Stedu #define PN8_STA_MVID(x)			(((x) >> 48) & 0x1f)
717aa8d3a7Stedu 
727aa8d3a7Stedu /* Reserved1 to powernow k8 configuration */
73668c25f3Sdlg #define PN8_PSB_VERSION			0x14
747aa8d3a7Stedu #define PN8_PSB_TO_RVO(x)		((x) & 0x03)
757aa8d3a7Stedu #define PN8_PSB_TO_IRT(x)		(((x) >> 2) & 0x03)
767aa8d3a7Stedu #define PN8_PSB_TO_MVS(x)		(((x) >> 4) & 0x03)
777aa8d3a7Stedu #define PN8_PSB_TO_BATT(x)		(((x) >> 6) & 0x03)
787aa8d3a7Stedu 
797aa8d3a7Stedu /* ACPI ctr_val status register to powernow k8 configuration */
80668c25f3Sdlg #define PN8_ACPI_CTRL_TO_FID(x)		((x) & 0x3f)
81668c25f3Sdlg #define PN8_ACPI_CTRL_TO_VID(x)		(((x) >> 6) & 0x1f)
82668c25f3Sdlg #define PN8_ACPI_CTRL_TO_VST(x)		(((x) >> 11) & 0x1f)
83668c25f3Sdlg #define PN8_ACPI_CTRL_TO_MVS(x)		(((x) >> 18) & 0x03)
84668c25f3Sdlg #define PN8_ACPI_CTRL_TO_PLL(x)		(((x) >> 20) & 0x7f)
85668c25f3Sdlg #define PN8_ACPI_CTRL_TO_RVO(x)		(((x) >> 28) & 0x03)
86668c25f3Sdlg #define PN8_ACPI_CTRL_TO_IRT(x)		(((x) >> 30) & 0x03)
877aa8d3a7Stedu 
88c5cdb568Sgwk #define PN8_PSS_CFID(x)			((x) & 0x3f)
89c5cdb568Sgwk #define PN8_PSS_CVID(x)			(((x) >> 6) & 0x1f)
90c5cdb568Sgwk 
91668c25f3Sdlg #define PN8_PLL_LOCK(x)			((x) * 1000/5)
927aa8d3a7Stedu #define WRITE_FIDVID(fid, vid, ctrl)	\
937aa8d3a7Stedu 	wrmsr(MSR_AMDK7_FIDVID_CTL,	\
947aa8d3a7Stedu 	    (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
957aa8d3a7Stedu 
967aa8d3a7Stedu #define COUNT_OFF_IRT(irt)		DELAY(10 * (1 << (irt)))
977aa8d3a7Stedu #define COUNT_OFF_VST(vst)		DELAY(20 * (vst))
987aa8d3a7Stedu 
997aa8d3a7Stedu #define FID_TO_VCO_FID(fid)		\
1007aa8d3a7Stedu 	(((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
1017aa8d3a7Stedu 
1027aa8d3a7Stedu #define POWERNOW_MAX_STATES		16
1037aa8d3a7Stedu 
1047aa8d3a7Stedu struct k8pnow_state {
1057aa8d3a7Stedu 	int freq;
10635958b2eStedu 	uint8_t fid;
10735958b2eStedu 	uint8_t vid;
1087aa8d3a7Stedu };
1097aa8d3a7Stedu 
1107aa8d3a7Stedu struct k8pnow_cpu_state {
1117aa8d3a7Stedu 	struct k8pnow_state state_table[POWERNOW_MAX_STATES];
1127aa8d3a7Stedu 	unsigned int n_states;
1137aa8d3a7Stedu 	unsigned int sgtc;
1147aa8d3a7Stedu 	unsigned int vst;
1157aa8d3a7Stedu 	unsigned int mvs;
1167aa8d3a7Stedu 	unsigned int pll;
1177aa8d3a7Stedu 	unsigned int rvo;
1187aa8d3a7Stedu 	unsigned int irt;
1197aa8d3a7Stedu 	int low;
1207aa8d3a7Stedu };
1217aa8d3a7Stedu 
1227aa8d3a7Stedu struct psb_s {
1237aa8d3a7Stedu 	char signature[10];	/* AMDK7PNOW! */
1247aa8d3a7Stedu 	uint8_t version;
1257aa8d3a7Stedu 	uint8_t flags;
1267aa8d3a7Stedu 	uint16_t ttime;		/* Min Settling time */
1277aa8d3a7Stedu 	uint8_t reserved;
1287aa8d3a7Stedu 	uint8_t n_pst;
1297aa8d3a7Stedu };
1307aa8d3a7Stedu 
1317aa8d3a7Stedu struct pst_s {
13235958b2eStedu 	uint32_t cpuid;
13335958b2eStedu 	uint8_t pll;
13435958b2eStedu 	uint8_t fid;
13535958b2eStedu 	uint8_t vid;
13635958b2eStedu 	uint8_t n_states;
1377aa8d3a7Stedu };
1387aa8d3a7Stedu 
13934b4ab5eSuwe struct k8pnow_cpu_state *k8pnow_current_state = NULL;
140aad5aaffSgwk extern int setperf_prio;
141c5cdb568Sgwk extern int perflevel;
1427aa8d3a7Stedu 
1437aa8d3a7Stedu int k8pnow_read_pending_wait(uint64_t *);
14435958b2eStedu int k8pnow_decode_pst(struct k8pnow_cpu_state *, uint8_t *);
145c5cdb568Sgwk int k8pnow_states(struct k8pnow_cpu_state *, uint32_t, unsigned int, unsigned int);
146c5cdb568Sgwk void k8pnow_transition(struct k8pnow_cpu_state *e, int);
1477aa8d3a7Stedu 
1483cd93248Sgwk #if NACPICPU > 0
1493cd93248Sgwk int k8pnow_acpi_init(struct k8pnow_cpu_state *, uint64_t);
1503cd93248Sgwk void k8pnow_acpi_pss_changed(struct acpicpu_pss *, int);
1513cd93248Sgwk int k8pnow_acpi_states(struct k8pnow_cpu_state *, struct acpicpu_pss *, int,
1523cd93248Sgwk     uint64_t);
1533cd93248Sgwk #endif
1543cd93248Sgwk 
15596771376Sderaadt int
k8pnow_read_pending_wait(uint64_t * status)15696771376Sderaadt k8pnow_read_pending_wait(uint64_t *status)
15796771376Sderaadt {
1589e71eeb7Sgwk 	unsigned int i = 100000;
15996771376Sderaadt 
16035958b2eStedu 	while (i--) {
1617aa8d3a7Stedu 		*status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
16235958b2eStedu 		if (!PN8_STA_PENDING(*status))
1637aa8d3a7Stedu 			return 0;
16435958b2eStedu 
16535958b2eStedu 	}
16635958b2eStedu 	printf("k8pnow_read_pending_wait: change pending stuck.\n");
16735958b2eStedu 	return 1;
1687aa8d3a7Stedu }
1697aa8d3a7Stedu 
17096773847Sdim void
k8_powernow_setperf(int level)1717aa8d3a7Stedu k8_powernow_setperf(int level)
1727aa8d3a7Stedu {
1731f3ce405Sgwk 	unsigned int i;
1747aa8d3a7Stedu 	struct k8pnow_cpu_state *cstate;
1757aa8d3a7Stedu 
176c5cdb568Sgwk 	cstate = k8pnow_current_state;
177c5cdb568Sgwk 
178c5cdb568Sgwk 	i = ((level * cstate->n_states) + 1) / 101;
179c5cdb568Sgwk 	if (i >= cstate->n_states)
180c5cdb568Sgwk 		i = cstate->n_states - 1;
181c5cdb568Sgwk 
182c5cdb568Sgwk 	k8pnow_transition(cstate, i);
183c5cdb568Sgwk }
184c5cdb568Sgwk 
185c5cdb568Sgwk void
k8pnow_transition(struct k8pnow_cpu_state * cstate,int level)186c5cdb568Sgwk k8pnow_transition(struct k8pnow_cpu_state *cstate, int level)
187c5cdb568Sgwk {
188c5cdb568Sgwk 	uint64_t status;
189c5cdb568Sgwk 	int cfid, cvid, fid = 0, vid = 0;
190c5cdb568Sgwk 	int rvo;
191c5cdb568Sgwk 	u_int val;
192c5cdb568Sgwk 
1937aa8d3a7Stedu 	/*
1947aa8d3a7Stedu 	 * We dont do a k8pnow_read_pending_wait here, need to ensure that the
1957aa8d3a7Stedu 	 * change pending bit isn't stuck,
1967aa8d3a7Stedu 	 */
1977aa8d3a7Stedu 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
1987aa8d3a7Stedu 	if (PN8_STA_PENDING(status))
19996773847Sdim 		return;
2007aa8d3a7Stedu 	cfid = PN8_STA_CFID(status);
2017aa8d3a7Stedu 	cvid = PN8_STA_CVID(status);
2027aa8d3a7Stedu 
203c5cdb568Sgwk 	fid = cstate->state_table[level].fid;
204c5cdb568Sgwk 	vid = cstate->state_table[level].vid;
2057aa8d3a7Stedu 
2067aa8d3a7Stedu 	if (fid == cfid && vid == cvid)
20796773847Sdim 		return;
2087aa8d3a7Stedu 
2097aa8d3a7Stedu 	/*
2107aa8d3a7Stedu 	 * Phase 1: Raise core voltage to requested VID if frequency is
2117aa8d3a7Stedu 	 * going up.
2127aa8d3a7Stedu 	 */
2137aa8d3a7Stedu 	while (cvid > vid) {
2147aa8d3a7Stedu 		val = cvid - (1 << cstate->mvs);
2157aa8d3a7Stedu 		WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
2167aa8d3a7Stedu 		if (k8pnow_read_pending_wait(&status))
21796773847Sdim 			return;
2187aa8d3a7Stedu 		cvid = PN8_STA_CVID(status);
2197aa8d3a7Stedu 		COUNT_OFF_VST(cstate->vst);
2207aa8d3a7Stedu 	}
2217aa8d3a7Stedu 
2227aa8d3a7Stedu 	/* ... then raise to voltage + RVO (if required) */
2237aa8d3a7Stedu 	for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
2247aa8d3a7Stedu 		/* XXX It's not clear from spec if we have to do that
2257aa8d3a7Stedu 		 * in 0.25 step or in MVS.  Therefore do it as it's done
2267aa8d3a7Stedu 		 * under Linux */
2277aa8d3a7Stedu 		WRITE_FIDVID(cfid, cvid - 1, 1ULL);
2287aa8d3a7Stedu 		if (k8pnow_read_pending_wait(&status))
22996773847Sdim 			return;
2307aa8d3a7Stedu 		cvid = PN8_STA_CVID(status);
2317aa8d3a7Stedu 		COUNT_OFF_VST(cstate->vst);
2327aa8d3a7Stedu 	}
2337aa8d3a7Stedu 
2347aa8d3a7Stedu 	/* Phase 2: change to requested core frequency */
2357aa8d3a7Stedu 	if (cfid != fid) {
2367aa8d3a7Stedu 		u_int vco_fid, vco_cfid;
2377aa8d3a7Stedu 
2387aa8d3a7Stedu 		vco_fid = FID_TO_VCO_FID(fid);
2397aa8d3a7Stedu 		vco_cfid = FID_TO_VCO_FID(cfid);
2407aa8d3a7Stedu 
2417aa8d3a7Stedu 		while (abs(vco_fid - vco_cfid) > 2) {
2427aa8d3a7Stedu 			if (fid > cfid) {
2437aa8d3a7Stedu 				if (cfid > 6)
2447aa8d3a7Stedu 					val = cfid + 2;
2457aa8d3a7Stedu 				else
2467aa8d3a7Stedu 					val = FID_TO_VCO_FID(cfid) + 2;
2477aa8d3a7Stedu 			} else
2487aa8d3a7Stedu 				val = cfid - 2;
249c5cdb568Sgwk 			WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5);
2507aa8d3a7Stedu 
2517aa8d3a7Stedu 			if (k8pnow_read_pending_wait(&status))
25296773847Sdim 				return;
2537aa8d3a7Stedu 			cfid = PN8_STA_CFID(status);
2547aa8d3a7Stedu 			COUNT_OFF_IRT(cstate->irt);
2557aa8d3a7Stedu 
2567aa8d3a7Stedu 			vco_cfid = FID_TO_VCO_FID(cfid);
2577aa8d3a7Stedu 		}
2587aa8d3a7Stedu 
259c5cdb568Sgwk 		WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
2607aa8d3a7Stedu 		if (k8pnow_read_pending_wait(&status))
26196773847Sdim 			return;
2627aa8d3a7Stedu 		cfid = PN8_STA_CFID(status);
2637aa8d3a7Stedu 		COUNT_OFF_IRT(cstate->irt);
2647aa8d3a7Stedu 	}
2657aa8d3a7Stedu 
2667aa8d3a7Stedu 	/* Phase 3: change to requested voltage */
2677aa8d3a7Stedu 	if (cvid != vid) {
2687aa8d3a7Stedu 		WRITE_FIDVID(cfid, vid, 1ULL);
2697aa8d3a7Stedu 		if (k8pnow_read_pending_wait(&status))
27096773847Sdim 			return;
2717aa8d3a7Stedu 		cvid = PN8_STA_CVID(status);
2727aa8d3a7Stedu 		COUNT_OFF_VST(cstate->vst);
2737aa8d3a7Stedu 	}
2747aa8d3a7Stedu 
27530634235Sgwk 	if (cfid == fid || cvid == vid)
276c5cdb568Sgwk 		cpuspeed = cstate->state_table[level].freq;
2777aa8d3a7Stedu }
2787aa8d3a7Stedu 
2797aa8d3a7Stedu /*
2807aa8d3a7Stedu  * Given a set of pair of fid/vid, and number of performance states,
2817aa8d3a7Stedu  * compute state_table via an insertion sort.
2827aa8d3a7Stedu  */
2837aa8d3a7Stedu int
k8pnow_decode_pst(struct k8pnow_cpu_state * cstate,uint8_t * p)28435958b2eStedu k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p)
2857aa8d3a7Stedu {
2867aa8d3a7Stedu 	int i, j, n;
2877aa8d3a7Stedu 	struct k8pnow_state state;
28896771376Sderaadt 
28935958b2eStedu 	for (n = 0, i = 0; i < cstate->n_states; i++) {
2907aa8d3a7Stedu 		state.fid = *p++;
2917aa8d3a7Stedu 		state.vid = *p++;
2927aa8d3a7Stedu 
29334b4ab5eSuwe 		/*
29434b4ab5eSuwe 		 * The minimum supported frequency per the data sheet is 800MHz
29534b4ab5eSuwe 		 * The maximum supported frequency is 5000MHz.
29634b4ab5eSuwe 		 */
29734b4ab5eSuwe 		state.freq = 800 + state.fid * 100;
2987aa8d3a7Stedu 		j = n;
29935958b2eStedu 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
3007aa8d3a7Stedu 			memcpy(&cstate->state_table[j],
3017aa8d3a7Stedu 			    &cstate->state_table[j - 1],
3027aa8d3a7Stedu 			    sizeof(struct k8pnow_state));
3037aa8d3a7Stedu 			--j;
3047aa8d3a7Stedu 		}
3057aa8d3a7Stedu 		memcpy(&cstate->state_table[j], &state,
3067aa8d3a7Stedu 		    sizeof(struct k8pnow_state));
3077aa8d3a7Stedu 		n++;
3087aa8d3a7Stedu 	}
3097aa8d3a7Stedu 	return 1;
3107aa8d3a7Stedu }
3117aa8d3a7Stedu 
3127aa8d3a7Stedu int
k8pnow_states(struct k8pnow_cpu_state * cstate,uint32_t cpusig,unsigned int fid,unsigned int vid)3137aa8d3a7Stedu k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
3147aa8d3a7Stedu     unsigned int fid, unsigned int vid)
3157aa8d3a7Stedu {
3167aa8d3a7Stedu 	struct psb_s *psb;
3177aa8d3a7Stedu 	struct pst_s *pst;
3187aa8d3a7Stedu 	uint8_t *p;
31935958b2eStedu 	int i;
3207aa8d3a7Stedu 
3210b2508fdSmlarkin 	/*
3220b2508fdSmlarkin 	 * Look in the 0xe0000 - 0x100000 physical address
3230b2508fdSmlarkin 	 * range for the pst tables; 16 byte blocks. End 10 bytes
3240b2508fdSmlarkin 	 * before the end of the range to avoid memcmp across a
3250b2508fdSmlarkin 	 * page boundary into unmapped memory.
3260b2508fdSmlarkin 	 */
3277aa8d3a7Stedu 	for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
3280b2508fdSmlarkin 	    p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN) - 10;
3290b2508fdSmlarkin 	    p += BIOS_STEP) {
3307aa8d3a7Stedu 		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
3317aa8d3a7Stedu 			psb = (struct psb_s *)p;
332668c25f3Sdlg 			if (psb->version != PN8_PSB_VERSION)
3337aa8d3a7Stedu 				return 0;
3347aa8d3a7Stedu 
3357aa8d3a7Stedu 			cstate->vst = psb->ttime;
3367aa8d3a7Stedu 			cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
3377aa8d3a7Stedu 			cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
3387aa8d3a7Stedu 			cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
3397aa8d3a7Stedu 			cstate->low = PN8_PSB_TO_BATT(psb->reserved);
3407aa8d3a7Stedu 			p+= sizeof(struct psb_s);
34135958b2eStedu 
34235958b2eStedu 			for (i = 0; i < psb->n_pst; ++i) {
3437aa8d3a7Stedu 				pst = (struct pst_s *) p;
34435958b2eStedu 
34535958b2eStedu 				cstate->pll = pst->pll;
3467aa8d3a7Stedu 				cstate->n_states = pst->n_states;
34735958b2eStedu 				if (cpusig == pst->cpuid &&
34835958b2eStedu 				    pst->fid == fid && pst->vid == vid) {
3497aa8d3a7Stedu 					return (k8pnow_decode_pst(cstate,
35035958b2eStedu 					    p+= sizeof (struct pst_s)));
3517aa8d3a7Stedu 				}
3521f3ce405Sgwk 				p += sizeof(struct pst_s) + 2
3531f3ce405Sgwk 				     * cstate->n_states;
3547aa8d3a7Stedu 			}
3557aa8d3a7Stedu 		}
3567aa8d3a7Stedu 	}
3577aa8d3a7Stedu 
3587aa8d3a7Stedu 	return 0;
3597aa8d3a7Stedu 
3607aa8d3a7Stedu }
3617aa8d3a7Stedu 
3623cd93248Sgwk #if NACPICPU > 0
3633cd93248Sgwk 
3643cd93248Sgwk int
k8pnow_acpi_states(struct k8pnow_cpu_state * cstate,struct acpicpu_pss * pss,int nstates,uint64_t status)3653cd93248Sgwk k8pnow_acpi_states(struct k8pnow_cpu_state * cstate, struct acpicpu_pss * pss,
3663cd93248Sgwk     int nstates, uint64_t status)
3673cd93248Sgwk {
3683cd93248Sgwk 	struct k8pnow_state state;
3693cd93248Sgwk 	int j, k, n;
3703cd93248Sgwk 	uint32_t ctrl;
3713cd93248Sgwk 
3723cd93248Sgwk 	k = -1;
3733cd93248Sgwk 
3743cd93248Sgwk 	for (n = 0; n < cstate->n_states; n++) {
375c5cdb568Sgwk 		if ((PN8_STA_CFID(status) == PN8_PSS_CFID(pss[n].pss_status)) &&
376c5cdb568Sgwk 		    (PN8_STA_CVID(status) == PN8_PSS_CVID(pss[n].pss_status)))
3773cd93248Sgwk 			k = n;
3783cd93248Sgwk 		ctrl = pss[n].pss_ctrl;
3793cd93248Sgwk 		state.fid = PN8_ACPI_CTRL_TO_FID(ctrl);
3803cd93248Sgwk 		state.vid = PN8_ACPI_CTRL_TO_VID(ctrl);
3813cd93248Sgwk 
3823cd93248Sgwk 		state.freq = pss[n].pss_core_freq;
3833cd93248Sgwk 		j = n;
3843cd93248Sgwk 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
3853cd93248Sgwk 			memcpy(&cstate->state_table[j],
3863cd93248Sgwk 			    &cstate->state_table[j - 1],
3873cd93248Sgwk 			    sizeof(struct k8pnow_state));
3883cd93248Sgwk 			--j;
3893cd93248Sgwk 		}
3903cd93248Sgwk 		memcpy(&cstate->state_table[j], &state,
3913cd93248Sgwk 		    sizeof(struct k8pnow_state));
3923cd93248Sgwk 	}
3933cd93248Sgwk 
3943cd93248Sgwk 	return k;
3953cd93248Sgwk }
3963cd93248Sgwk 
3973cd93248Sgwk void
k8pnow_acpi_pss_changed(struct acpicpu_pss * pss,int npss)3983cd93248Sgwk k8pnow_acpi_pss_changed(struct acpicpu_pss * pss, int npss)
3993cd93248Sgwk {
4003cd93248Sgwk 	int curs;
4013cd93248Sgwk 	struct k8pnow_cpu_state * cstate;
4023cd93248Sgwk 	uint32_t ctrl;
4033cd93248Sgwk 	uint64_t status;
4043cd93248Sgwk 
4053cd93248Sgwk 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
4063cd93248Sgwk 	cstate = k8pnow_current_state;
4073cd93248Sgwk 
4083cd93248Sgwk 	curs = k8pnow_acpi_states(cstate, pss, npss, status);
4093cd93248Sgwk 	ctrl = pss[curs].pss_ctrl;
410940ac101Sgwk 
411940ac101Sgwk 	cstate->rvo = PN8_ACPI_CTRL_TO_RVO(ctrl);
4123cd93248Sgwk 	cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
4133cd93248Sgwk 	cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
4143cd93248Sgwk 	cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
4153cd93248Sgwk 	cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
4163cd93248Sgwk 	cstate->low = 0;
4173cd93248Sgwk 	cstate->n_states = npss;
4183cd93248Sgwk }
4193cd93248Sgwk 
4203cd93248Sgwk int
k8pnow_acpi_init(struct k8pnow_cpu_state * cstate,uint64_t status)4213cd93248Sgwk k8pnow_acpi_init(struct k8pnow_cpu_state * cstate, uint64_t status)
4223cd93248Sgwk {
4233cd93248Sgwk 	int curs;
4243cd93248Sgwk 	uint32_t ctrl;
4253cd93248Sgwk 	struct acpicpu_pss *pss;
4263cd93248Sgwk 
4273cd93248Sgwk 	cstate->n_states = acpicpu_fetch_pss(&pss);
4283cd93248Sgwk 	if (cstate->n_states == 0)
4293cd93248Sgwk 		return 0;
4303cd93248Sgwk 	acpicpu_set_notify(k8pnow_acpi_pss_changed);
4313cd93248Sgwk 
4323cd93248Sgwk 	curs = k8pnow_acpi_states(cstate, pss, cstate->n_states, status);
4333cd93248Sgwk 	ctrl = pss[curs].pss_ctrl;
4343cd93248Sgwk 
435940ac101Sgwk 	cstate->rvo = PN8_ACPI_CTRL_TO_RVO(ctrl);
4363cd93248Sgwk 	cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
4373cd93248Sgwk 	cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
4383cd93248Sgwk 	cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
4393cd93248Sgwk 	cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
4403cd93248Sgwk 	cstate->low = 0;
4413cd93248Sgwk 
4423cd93248Sgwk 	return 1;
4433cd93248Sgwk }
4443cd93248Sgwk 
4453cd93248Sgwk #endif /* NACPICPU */
4463cd93248Sgwk 
4477aa8d3a7Stedu void
k8_powernow_init(void)4487aa8d3a7Stedu k8_powernow_init(void)
4497aa8d3a7Stedu {
45035958b2eStedu 	uint64_t status;
45134b4ab5eSuwe 	u_int maxfid, maxvid, i;
4527aa8d3a7Stedu 	struct k8pnow_cpu_state *cstate;
45335958b2eStedu 	struct k8pnow_state *state;
4547aa8d3a7Stedu 	struct cpu_info * ci;
4557aa8d3a7Stedu 	char * techname = NULL;
456668c25f3Sdlg 	u_int32_t regs[4];
457668c25f3Sdlg 
4587aa8d3a7Stedu 	ci = curcpu();
4597aa8d3a7Stedu 
460aad5aaffSgwk 	if (setperf_prio > 1)
461aad5aaffSgwk 		return;
462aad5aaffSgwk 
46334b4ab5eSuwe 	if (k8pnow_current_state)
46434b4ab5eSuwe 		return;
46534b4ab5eSuwe 
466668c25f3Sdlg 	cpuid(0x80000000, regs);
467668c25f3Sdlg 	if (regs[0] < 0x80000007)
468668c25f3Sdlg 		return;
469668c25f3Sdlg 
470668c25f3Sdlg 	cpuid(0x80000007, regs);
471668c25f3Sdlg 	if (!(regs[3] & AMD_PN_FID_VID))
472668c25f3Sdlg 		return;
473668c25f3Sdlg 
47402fa6f89Sgwk 	/* Extended CPUID signature value */
47502fa6f89Sgwk 	cpuid(0x80000001, regs);
47602fa6f89Sgwk 
47716427c27Stedu 	cstate = malloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_NOWAIT);
47816427c27Stedu 	if (!cstate)
47916427c27Stedu 		return;
4807aa8d3a7Stedu 
48102fa6f89Sgwk 	cstate->n_states = 0;
4827aa8d3a7Stedu 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
4837aa8d3a7Stedu 	maxfid = PN8_STA_MFID(status);
4847aa8d3a7Stedu 	maxvid = PN8_STA_MVID(status);
4857aa8d3a7Stedu 
4867aa8d3a7Stedu 	/*
4877aa8d3a7Stedu 	* If start FID is different to max FID, then it is a
4887aa8d3a7Stedu 	* mobile processor.  If not, it is a low powered desktop
4897aa8d3a7Stedu 	* processor.
4907aa8d3a7Stedu 	*/
4917aa8d3a7Stedu 	if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
4927aa8d3a7Stedu 		techname = "PowerNow! K8";
4937aa8d3a7Stedu 	else
494bda76761Stom 		techname = "Cool'n'Quiet K8";
4957aa8d3a7Stedu 
4963cd93248Sgwk #if NACPICPU > 0
4973cd93248Sgwk 	/* If we have acpi check acpi first */
4983cd93248Sgwk 	if (!k8pnow_acpi_init(cstate, status))
4993cd93248Sgwk #endif
5003cd93248Sgwk 	{
50102fa6f89Sgwk 		if (!k8pnow_states(cstate, ci->ci_signature, maxfid, maxvid))
50202fa6f89Sgwk 			k8pnow_states(cstate, regs[0], maxfid, maxvid);
5033cd93248Sgwk 	}
5047aa8d3a7Stedu 	if (cstate->n_states) {
505e959411aStom 		printf("%s: %s %d MHz: speeds:",
5068fa1e2ebSbluhm 		    ci->ci_dev->dv_xname, techname, cpuspeed);
507b4cc6758Sdlg 		for (i = cstate->n_states; i > 0; i--) {
508b4cc6758Sdlg 			state = &cstate->state_table[i-1];
509b4cc6758Sdlg 			printf(" %d", state->freq);
51035958b2eStedu 		}
511e959411aStom 		printf(" MHz\n");
51234b4ab5eSuwe 		k8pnow_current_state = cstate;
5137aa8d3a7Stedu 		cpu_setperf = k8_powernow_setperf;
514aad5aaffSgwk 		setperf_prio = 1;
515f0ff5ec6Sdlg 		return;
5167aa8d3a7Stedu 	}
517864cf24eSderaadt 	free(cstate, M_DEVBUF, sizeof(*cstate));
5187aa8d3a7Stedu }
519