xref: /dflybsd-src/sys/dev/powermng/powernow/powernow.c (revision 231d986092a296778bf9a368a27fb50a92116a75)
1*231d9860SAlexander Polakov /*
2*231d9860SAlexander Polakov  * Copyright (c) 2004 Martin V\xe9giard. Copyright (c) 2004-2005 Bruno Ducrot
3*231d9860SAlexander Polakov  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> Copyright
4*231d9860SAlexander Polakov  * (c) 2004, 2006 The NetBSD Foundation, Inc. All rights reserved.
5*231d9860SAlexander Polakov  *
6*231d9860SAlexander Polakov  * This code is derived from software contributed to The NetBSD Foundation by
7*231d9860SAlexander Polakov  * Juan Romero Pardines and Martin Vegiard.
8*231d9860SAlexander Polakov  *
9*231d9860SAlexander Polakov  * Redistribution and use in source and binary forms, with or without
10*231d9860SAlexander Polakov  * modification, are permitted provided that the following conditions are
11*231d9860SAlexander Polakov  * met: 1. Redistributions of source code must retain the above copyright
12*231d9860SAlexander Polakov  * notice, this list of conditions and the following disclaimer. 2.
13*231d9860SAlexander Polakov  * Redistributions in binary form must reproduce the above copyright notice,
14*231d9860SAlexander Polakov  * this list of conditions and the following disclaimer in the documentation
15*231d9860SAlexander Polakov  * and/or other materials provided with the distribution. THIS SOFTWARE IS
16*231d9860SAlexander Polakov  * PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17*231d9860SAlexander Polakov  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18*231d9860SAlexander Polakov  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19*231d9860SAlexander Polakov  * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20*231d9860SAlexander Polakov  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21*231d9860SAlexander Polakov  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22*231d9860SAlexander Polakov  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23*231d9860SAlexander Polakov  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24*231d9860SAlexander Polakov  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25*231d9860SAlexander Polakov  * POSSIBILITY OF SUCH DAMAGE.
26*231d9860SAlexander Polakov  */
27*231d9860SAlexander Polakov /* AMD POWERNOW K8 driver */
28*231d9860SAlexander Polakov 
29*231d9860SAlexander Polakov #include <sys/param.h>
30*231d9860SAlexander Polakov #include <sys/systm.h>
31*231d9860SAlexander Polakov #include <sys/malloc.h>
32*231d9860SAlexander Polakov #include <sys/kernel.h>
33*231d9860SAlexander Polakov #include <sys/module.h>
34*231d9860SAlexander Polakov #include <sys/sysctl.h>
35*231d9860SAlexander Polakov #include <bus/isa/isa.h>
36*231d9860SAlexander Polakov #include <machine/cpu.h>
37*231d9860SAlexander Polakov #include <machine/pmap.h>
38*231d9860SAlexander Polakov #include <machine/pc/bios.h>
39*231d9860SAlexander Polakov #include <machine/cpufunc.h>
40*231d9860SAlexander Polakov #include <machine/md_var.h>
41*231d9860SAlexander Polakov #include <machine/specialreg.h>
42*231d9860SAlexander Polakov 
43*231d9860SAlexander Polakov #define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
44*231d9860SAlexander Polakov #define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
45*231d9860SAlexander Polakov #define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
46*231d9860SAlexander Polakov 
47*231d9860SAlexander Polakov /*
48*231d9860SAlexander Polakov  * MSRs and bits used by PowerNow! technology
49*231d9860SAlexander Polakov  */
50*231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_CTL            0xc0010041
51*231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_STATUS         0xc0010042
52*231d9860SAlexander Polakov #define AMD_PN_FID_VID                  0x06
53*231d9860SAlexander Polakov 
54*231d9860SAlexander Polakov #define BIOS_START                      0xe0000
55*231d9860SAlexander Polakov #define BIOS_LEN                        0x20000
56*231d9860SAlexander Polakov #define BIOS_STEP                       16
57*231d9860SAlexander Polakov 
58*231d9860SAlexander Polakov #define PN8_PSB_VERSION                 0x14
59*231d9860SAlexander Polakov #define PN8_PSB_TO_RVO(x)               ((x) & 0x03)
60*231d9860SAlexander Polakov #define PN8_PSB_TO_IRT(x)               (((x) >> 2) & 0x03)
61*231d9860SAlexander Polakov #define PN8_PSB_TO_MVS(x)               (((x) >> 4) & 0x03)
62*231d9860SAlexander Polakov #define PN8_PSB_TO_BATT(x)              (((x) >> 6) & 0x03)
63*231d9860SAlexander Polakov /* Bitfields used by K8 */
64*231d9860SAlexander Polakov #define PN8_CTR_FID(x)                  ((x) & 0x3f)
65*231d9860SAlexander Polakov #define PN8_CTR_VID(x)                  (((x) & 0x1f) << 8)
66*231d9860SAlexander Polakov #define PN8_CTR_PENDING(x)              (((x) & 1) << 32)
67*231d9860SAlexander Polakov #define PN8_STA_CFID(x)                 ((x) & 0x3f)
68*231d9860SAlexander Polakov #define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
69*231d9860SAlexander Polakov #define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
70*231d9860SAlexander Polakov #define PN8_STA_PENDING(x)              (((x) >> 31) & 0x01)
71*231d9860SAlexander Polakov #define PN8_STA_CVID(x)                 (((x) >> 32) & 0x1f)
72*231d9860SAlexander Polakov #define PN8_STA_SVID(x)                 (((x) >> 40) & 0x1f)
73*231d9860SAlexander Polakov #define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
74*231d9860SAlexander Polakov #define PN8_PLL_LOCK(x)                 ((x) * 1000/5)
75*231d9860SAlexander Polakov #define WRITE_FIDVID(fid, vid, ctrl)    \
76*231d9860SAlexander Polakov         wrmsr(MSR_AMDK7_FIDVID_CTL,     \
77*231d9860SAlexander Polakov             (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
78*231d9860SAlexander Polakov #define COUNT_OFF_IRT(irt)              DELAY(10 * (1 << (irt)))
79*231d9860SAlexander Polakov #define COUNT_OFF_VST(vst)              DELAY(20 * (vst))
80*231d9860SAlexander Polakov #define FID_TO_VCO_FID(fid)             \
81*231d9860SAlexander Polakov         (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
82*231d9860SAlexander Polakov 
83*231d9860SAlexander Polakov #define READ_PENDING_WAIT(status)                               \
84*231d9860SAlexander Polakov         do {                                                    \
85*231d9860SAlexander Polakov                 (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS);      \
86*231d9860SAlexander Polakov         } while (PN8_STA_PENDING(status))
87*231d9860SAlexander Polakov #define abs(x) ( x < 0 ? -x : x )
88*231d9860SAlexander Polakov 
89*231d9860SAlexander Polakov #define POWERNOW_MAX_STATES             16
90*231d9860SAlexander Polakov 
91*231d9860SAlexander Polakov struct k8pnow_state {
92*231d9860SAlexander Polakov 	int		freq;
93*231d9860SAlexander Polakov 	uint8_t		fid;
94*231d9860SAlexander Polakov 	uint8_t		vid;
95*231d9860SAlexander Polakov };
96*231d9860SAlexander Polakov 
97*231d9860SAlexander Polakov struct k8pnow_cpu_state {
98*231d9860SAlexander Polakov 	struct k8pnow_state state_table[POWERNOW_MAX_STATES];
99*231d9860SAlexander Polakov 	unsigned int	n_states;
100*231d9860SAlexander Polakov 	unsigned int	vst;
101*231d9860SAlexander Polakov 	unsigned int	mvs;
102*231d9860SAlexander Polakov 	unsigned int	pll;
103*231d9860SAlexander Polakov 	unsigned int	rvo;
104*231d9860SAlexander Polakov 	unsigned int	irt;
105*231d9860SAlexander Polakov 	int		low;
106*231d9860SAlexander Polakov };
107*231d9860SAlexander Polakov 
108*231d9860SAlexander Polakov struct psb_s {
109*231d9860SAlexander Polakov 	char		signature [10];	/* AMDK7PNOW! */
110*231d9860SAlexander Polakov 	uint8_t		version;
111*231d9860SAlexander Polakov 	uint8_t		flags;
112*231d9860SAlexander Polakov 	uint16_t	ttime;	/* Min Settling time */
113*231d9860SAlexander Polakov 	uint8_t		reserved;
114*231d9860SAlexander Polakov 	uint8_t		n_pst;
115*231d9860SAlexander Polakov };
116*231d9860SAlexander Polakov struct pst_s {
117*231d9860SAlexander Polakov 	uint32_t	cpuid;
118*231d9860SAlexander Polakov 	uint8_t		pll;
119*231d9860SAlexander Polakov 	uint8_t		fid;
120*231d9860SAlexander Polakov 	uint8_t		vid;
121*231d9860SAlexander Polakov 	uint8_t		n_states;
122*231d9860SAlexander Polakov };
123*231d9860SAlexander Polakov 
124*231d9860SAlexander Polakov static struct k8pnow_cpu_state *k8pnow_current_state = NULL;
125*231d9860SAlexander Polakov int		cpuspeed;
126*231d9860SAlexander Polakov 
127*231d9860SAlexander Polakov int
128*231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
129*231d9860SAlexander Polakov 	      unsigned int fid, unsigned int vid);
130*231d9860SAlexander Polakov int
131*231d9860SAlexander Polakov 		k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p);
132*231d9860SAlexander Polakov 
133*231d9860SAlexander Polakov /*
134*231d9860SAlexander Polakov  * Given a set of pair of fid/vid, and number of performance states, compute
135*231d9860SAlexander Polakov  * state_table via an insertion sort.
136*231d9860SAlexander Polakov  */
137*231d9860SAlexander Polakov int
138*231d9860SAlexander Polakov k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p)
139*231d9860SAlexander Polakov {
140*231d9860SAlexander Polakov 	int		i         , j, n;
141*231d9860SAlexander Polakov 	struct k8pnow_state state;
142*231d9860SAlexander Polakov 	for (n = 0, i = 0; i < cstate->n_states; i++) {
143*231d9860SAlexander Polakov 		state.fid = *p++;
144*231d9860SAlexander Polakov 		state.vid = *p++;
145*231d9860SAlexander Polakov 
146*231d9860SAlexander Polakov 		/*
147*231d9860SAlexander Polakov 		 * The minimum supported frequency per the data sheet is
148*231d9860SAlexander Polakov 		 * 800MHz The maximum supported frequency is 5000MHz.
149*231d9860SAlexander Polakov 		 */
150*231d9860SAlexander Polakov 		state.freq = 800 + state.fid * 100;
151*231d9860SAlexander Polakov 		j = n;
152*231d9860SAlexander Polakov 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
153*231d9860SAlexander Polakov 			memcpy(&cstate->state_table[j],
154*231d9860SAlexander Polakov 			       &cstate->state_table[j - 1],
155*231d9860SAlexander Polakov 			       sizeof(struct k8pnow_state));
156*231d9860SAlexander Polakov 			--j;
157*231d9860SAlexander Polakov 		}
158*231d9860SAlexander Polakov 		memcpy(&cstate->state_table[j], &state,
159*231d9860SAlexander Polakov 		       sizeof(struct k8pnow_state));
160*231d9860SAlexander Polakov 		n++;
161*231d9860SAlexander Polakov 	}
162*231d9860SAlexander Polakov 	return 1;
163*231d9860SAlexander Polakov }
164*231d9860SAlexander Polakov 
165*231d9860SAlexander Polakov int
166*231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
167*231d9860SAlexander Polakov 	      unsigned int fid, unsigned int vid)
168*231d9860SAlexander Polakov {
169*231d9860SAlexander Polakov 	struct psb_s   *psb;
170*231d9860SAlexander Polakov 	struct pst_s   *pst;
171*231d9860SAlexander Polakov 	uint8_t        *p;
172*231d9860SAlexander Polakov 	int		i;
173*231d9860SAlexander Polakov 	for (p = (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START);
174*231d9860SAlexander Polakov 	     p < (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p +=
175*231d9860SAlexander Polakov 	     BIOS_STEP) {
176*231d9860SAlexander Polakov 		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
177*231d9860SAlexander Polakov 			psb = (struct psb_s *)p;
178*231d9860SAlexander Polakov 			if (psb->version != PN8_PSB_VERSION)
179*231d9860SAlexander Polakov 				return 0;
180*231d9860SAlexander Polakov 			cstate->vst = psb->ttime;
181*231d9860SAlexander Polakov 			cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
182*231d9860SAlexander Polakov 			cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
183*231d9860SAlexander Polakov 			cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
184*231d9860SAlexander Polakov 			cstate->low = PN8_PSB_TO_BATT(psb->reserved);
185*231d9860SAlexander Polakov 			p += sizeof(struct psb_s);
186*231d9860SAlexander Polakov 			for (i = 0; i < psb->n_pst; ++i) {
187*231d9860SAlexander Polakov 				pst = (struct pst_s *)p;
188*231d9860SAlexander Polakov 				cstate->pll = pst->pll;
189*231d9860SAlexander Polakov 				cstate->n_states = pst->n_states;
190*231d9860SAlexander Polakov 				if (cpusig == pst->cpuid &&
191*231d9860SAlexander Polakov 				    pst->fid == fid && pst->vid == vid) {
192*231d9860SAlexander Polakov 					return (k8pnow_decode_pst(cstate,
193*231d9860SAlexander Polakov 						p += sizeof(struct pst_s)));
194*231d9860SAlexander Polakov 				}
195*231d9860SAlexander Polakov 				p += sizeof(struct pst_s) + 2
196*231d9860SAlexander Polakov 					* cstate->n_states;
197*231d9860SAlexander Polakov 			}
198*231d9860SAlexander Polakov 		}
199*231d9860SAlexander Polakov 	}
200*231d9860SAlexander Polakov 	return 0;
201*231d9860SAlexander Polakov }
202*231d9860SAlexander Polakov 
203*231d9860SAlexander Polakov static int
204*231d9860SAlexander Polakov k8_get_curfreq(void)
205*231d9860SAlexander Polakov {
206*231d9860SAlexander Polakov 	unsigned int	i;
207*231d9860SAlexander Polakov 	uint64_t	status;
208*231d9860SAlexander Polakov 	int		cfid      , cvid, fid = 0, vid = 0;
209*231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
210*231d9860SAlexander Polakov 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
211*231d9860SAlexander Polakov 	if (PN8_STA_PENDING(status))
212*231d9860SAlexander Polakov 		return 1;
213*231d9860SAlexander Polakov 	cfid = PN8_STA_CFID(status);
214*231d9860SAlexander Polakov 	cvid = PN8_STA_CVID(status);
215*231d9860SAlexander Polakov 	cstate = k8pnow_current_state;
216*231d9860SAlexander Polakov 	for (i = 0; i < cstate->n_states; i++) {
217*231d9860SAlexander Polakov 		if (cstate->state_table[i].fid == cfid &&
218*231d9860SAlexander Polakov 		    cstate->state_table[i].vid == cvid) {
219*231d9860SAlexander Polakov 			fid = cstate->state_table[i].fid;
220*231d9860SAlexander Polakov 			vid = cstate->state_table[i].vid;
221*231d9860SAlexander Polakov 			return (cstate->state_table[i].freq);
222*231d9860SAlexander Polakov 		}
223*231d9860SAlexander Polakov 	}
224*231d9860SAlexander Polakov 	/* Not reached */
225*231d9860SAlexander Polakov 	return -1;
226*231d9860SAlexander Polakov }
227*231d9860SAlexander Polakov 
228*231d9860SAlexander Polakov static int
229*231d9860SAlexander Polakov k8_powernow_setperf(unsigned int freq)
230*231d9860SAlexander Polakov {
231*231d9860SAlexander Polakov 	unsigned int	i;
232*231d9860SAlexander Polakov 	uint64_t	status;
233*231d9860SAlexander Polakov 	uint32_t	val;
234*231d9860SAlexander Polakov 	int		cfid      , cvid, fid = 0, vid = 0;
235*231d9860SAlexander Polakov 	int		rvo;
236*231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
237*231d9860SAlexander Polakov 	/*
238*231d9860SAlexander Polakov 	 * We dont do a k8pnow_read_pending_wait here, need to ensure that
239*231d9860SAlexander Polakov 	 * the change pending bit isn't stuck,
240*231d9860SAlexander Polakov 	 */
241*231d9860SAlexander Polakov 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
242*231d9860SAlexander Polakov 	if (PN8_STA_PENDING(status))
243*231d9860SAlexander Polakov 		return 1;
244*231d9860SAlexander Polakov 	cfid = PN8_STA_CFID(status);
245*231d9860SAlexander Polakov 	cvid = PN8_STA_CVID(status);
246*231d9860SAlexander Polakov 	cstate = k8pnow_current_state;
247*231d9860SAlexander Polakov 	for (i = 0; i < cstate->n_states; i++) {
248*231d9860SAlexander Polakov 		if (cstate->state_table[i].freq >= freq) {
249*231d9860SAlexander Polakov 			fid = cstate->state_table[i].fid;
250*231d9860SAlexander Polakov 			vid = cstate->state_table[i].vid;
251*231d9860SAlexander Polakov 			break;
252*231d9860SAlexander Polakov 		}
253*231d9860SAlexander Polakov 	}
254*231d9860SAlexander Polakov 	if (fid == cfid && vid == cvid) {
255*231d9860SAlexander Polakov 		cpuspeed = freq;
256*231d9860SAlexander Polakov 		return 0;
257*231d9860SAlexander Polakov 	}
258*231d9860SAlexander Polakov 	/*
259*231d9860SAlexander Polakov 	 * Phase 1: Raise core voltage to requested VID if frequency is going
260*231d9860SAlexander Polakov 	 * up.
261*231d9860SAlexander Polakov 	 */
262*231d9860SAlexander Polakov 	while (cvid > vid) {
263*231d9860SAlexander Polakov 		val = cvid - (1 << cstate->mvs);
264*231d9860SAlexander Polakov 		WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
265*231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
266*231d9860SAlexander Polakov 		cvid = PN8_STA_CVID(status);
267*231d9860SAlexander Polakov 		COUNT_OFF_VST(cstate->vst);
268*231d9860SAlexander Polakov 	}
269*231d9860SAlexander Polakov 
270*231d9860SAlexander Polakov 	/* ... then raise to voltage + RVO (if required) */
271*231d9860SAlexander Polakov 	for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
272*231d9860SAlexander Polakov 		/*
273*231d9860SAlexander Polakov 		 * XXX It's not clear from spec if we have to do that in 0.25
274*231d9860SAlexander Polakov 		 * step or in MVS.  Therefore do it as it's done under Linux
275*231d9860SAlexander Polakov 		 */
276*231d9860SAlexander Polakov 		WRITE_FIDVID(cfid, cvid - 1, 1ULL);
277*231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
278*231d9860SAlexander Polakov 		cvid = PN8_STA_CVID(status);
279*231d9860SAlexander Polakov 		COUNT_OFF_VST(cstate->vst);
280*231d9860SAlexander Polakov 	}
281*231d9860SAlexander Polakov 	/* Phase 2: change to requested core frequency */
282*231d9860SAlexander Polakov 	if (cfid != fid) {
283*231d9860SAlexander Polakov 		uint32_t	vco_fid, vco_cfid;
284*231d9860SAlexander Polakov 		vco_fid = FID_TO_VCO_FID(fid);
285*231d9860SAlexander Polakov 		vco_cfid = FID_TO_VCO_FID(cfid);
286*231d9860SAlexander Polakov 		while (abs(vco_fid - vco_cfid) > 2) {
287*231d9860SAlexander Polakov 			if (fid > cfid) {
288*231d9860SAlexander Polakov 				if (cfid > 6)
289*231d9860SAlexander Polakov 					val = cfid + 2;
290*231d9860SAlexander Polakov 				else
291*231d9860SAlexander Polakov 					val = FID_TO_VCO_FID(cfid) + 2;
292*231d9860SAlexander Polakov 			} else
293*231d9860SAlexander Polakov 				val = cfid - 2;
294*231d9860SAlexander Polakov 			WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5);
295*231d9860SAlexander Polakov 			READ_PENDING_WAIT(status);
296*231d9860SAlexander Polakov 			cfid = PN8_STA_CFID(status);
297*231d9860SAlexander Polakov 			COUNT_OFF_IRT(cstate->irt);
298*231d9860SAlexander Polakov 			vco_cfid = FID_TO_VCO_FID(cfid);
299*231d9860SAlexander Polakov 		}
300*231d9860SAlexander Polakov 		WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
301*231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
302*231d9860SAlexander Polakov 		cfid = PN8_STA_CFID(status);
303*231d9860SAlexander Polakov 		COUNT_OFF_IRT(cstate->irt);
304*231d9860SAlexander Polakov 	}
305*231d9860SAlexander Polakov 	/* Phase 3: change to requested voltage */
306*231d9860SAlexander Polakov 	if (cvid != vid) {
307*231d9860SAlexander Polakov 		WRITE_FIDVID(cfid, vid, 1ULL);
308*231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
309*231d9860SAlexander Polakov 		cvid = PN8_STA_CVID(status);
310*231d9860SAlexander Polakov 		COUNT_OFF_VST(cstate->vst);
311*231d9860SAlexander Polakov 	}
312*231d9860SAlexander Polakov 	if (cfid == fid || cvid == vid)
313*231d9860SAlexander Polakov 		cpuspeed = cstate->state_table[i].freq;
314*231d9860SAlexander Polakov 	return 0;
315*231d9860SAlexander Polakov }
316*231d9860SAlexander Polakov 
317*231d9860SAlexander Polakov static int
318*231d9860SAlexander Polakov powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)
319*231d9860SAlexander Polakov {
320*231d9860SAlexander Polakov 	int		fq        , err = 0;
321*231d9860SAlexander Polakov 	int		i;
322*231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
323*231d9860SAlexander Polakov 	struct k8pnow_state *state;
324*231d9860SAlexander Polakov 	cstate = k8pnow_current_state;
325*231d9860SAlexander Polakov 	if (req->newptr != NULL) {
326*231d9860SAlexander Polakov 		err = SYSCTL_IN(req, &fq, sizeof(fq));
327*231d9860SAlexander Polakov 		if (err)
328*231d9860SAlexander Polakov 			return err;
329*231d9860SAlexander Polakov 		if (fq != cpuspeed) {
330*231d9860SAlexander Polakov 			for (i = cstate->n_states; i > 0; i--) {
331*231d9860SAlexander Polakov 				state = &cstate->state_table[i - 1];
332*231d9860SAlexander Polakov 				if (fq == state->freq) {
333*231d9860SAlexander Polakov 					k8_powernow_setperf(fq);
334*231d9860SAlexander Polakov 					break;
335*231d9860SAlexander Polakov 				}
336*231d9860SAlexander Polakov 			}
337*231d9860SAlexander Polakov 		}
338*231d9860SAlexander Polakov 	} else {
339*231d9860SAlexander Polakov 		err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed));
340*231d9860SAlexander Polakov 	}
341*231d9860SAlexander Polakov 	return err;
342*231d9860SAlexander Polakov }
343*231d9860SAlexander Polakov 
344*231d9860SAlexander Polakov static struct sysctl_ctx_list machdep_powernow_ctx;
345*231d9860SAlexander Polakov static char	freqs_available[80];
346*231d9860SAlexander Polakov 
347*231d9860SAlexander Polakov static int
348*231d9860SAlexander Polakov powernow_init(void)
349*231d9860SAlexander Polakov {
350*231d9860SAlexander Polakov 	uint64_t	status;
351*231d9860SAlexander Polakov 	size_t		len    , freq_len;
352*231d9860SAlexander Polakov 	uint32_t	maxfid, maxvid, i;
353*231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
354*231d9860SAlexander Polakov 	struct k8pnow_state *state;
355*231d9860SAlexander Polakov 	const char     *techname;
356*231d9860SAlexander Polakov 	u_int32_t	regs  [4];
357*231d9860SAlexander Polakov 	cpuspeed = 0;
358*231d9860SAlexander Polakov 	struct sysctl_oid *oid, *leaf;
359*231d9860SAlexander Polakov 
360*231d9860SAlexander Polakov 	do_cpuid(0x80000000, regs);
361*231d9860SAlexander Polakov 	if (regs[0] < 0x80000007)
362*231d9860SAlexander Polakov 		return 1;
363*231d9860SAlexander Polakov 	do_cpuid(0x80000007, regs);
364*231d9860SAlexander Polakov 	if (!(regs[3] & AMD_PN_FID_VID))
365*231d9860SAlexander Polakov 		return 2;
366*231d9860SAlexander Polakov 	/* Extended CPUID signature value */
367*231d9860SAlexander Polakov 	do_cpuid(0x80000001, regs);
368*231d9860SAlexander Polakov 	cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK);
369*231d9860SAlexander Polakov 	cstate->n_states = 0;
370*231d9860SAlexander Polakov 
371*231d9860SAlexander Polakov 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
372*231d9860SAlexander Polakov 	maxfid = PN8_STA_MFID(status);
373*231d9860SAlexander Polakov 	maxvid = PN8_STA_MVID(status);
374*231d9860SAlexander Polakov 
375*231d9860SAlexander Polakov 	if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
376*231d9860SAlexander Polakov 		techname = "PowerNow!";
377*231d9860SAlexander Polakov 	else
378*231d9860SAlexander Polakov 		techname = "Cool`n'Quiet";
379*231d9860SAlexander Polakov 	k8pnow_states(cstate, regs[0], maxfid, maxvid);
380*231d9860SAlexander Polakov 	len = 0;
381*231d9860SAlexander Polakov 	if (cstate->n_states) {
382*231d9860SAlexander Polakov 		freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1;
383*231d9860SAlexander Polakov 		kprintf("%s speeds:",
384*231d9860SAlexander Polakov 			techname);
385*231d9860SAlexander Polakov 		for (i = cstate->n_states; i > 0; i--) {
386*231d9860SAlexander Polakov 			state = &cstate->state_table[i - 1];
387*231d9860SAlexander Polakov 			kprintf(" %d", state->freq);
388*231d9860SAlexander Polakov 			len += ksnprintf(freqs_available + len, freq_len - len, "%d%s",
389*231d9860SAlexander Polakov 					 state->freq,
390*231d9860SAlexander Polakov 					 i > 1 ? " " : "");
391*231d9860SAlexander Polakov 		}
392*231d9860SAlexander Polakov 		kprintf(" MHz\n");
393*231d9860SAlexander Polakov 		k8pnow_current_state = cstate;
394*231d9860SAlexander Polakov 		k8_powernow_setperf(k8_get_curfreq());
395*231d9860SAlexander Polakov 	} else {
396*231d9860SAlexander Polakov 		kfree(cstate, M_DEVBUF);
397*231d9860SAlexander Polakov 		kprintf("powernow: no power states found\n");
398*231d9860SAlexander Polakov 		return 3;
399*231d9860SAlexander Polakov 	}
400*231d9860SAlexander Polakov 
401*231d9860SAlexander Polakov 	/*
402*231d9860SAlexander Polakov 	 * Setup the sysctl sub-tree machdep.powernow.*
403*231d9860SAlexander Polakov 	 */
404*231d9860SAlexander Polakov 	oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx,
405*231d9860SAlexander Polakov 		     SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow",
406*231d9860SAlexander Polakov 			      CTLFLAG_RD, NULL, "");
407*231d9860SAlexander Polakov 	if (oid == NULL)
408*231d9860SAlexander Polakov 		return (EOPNOTSUPP);
409*231d9860SAlexander Polakov 	oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
410*231d9860SAlexander Polakov 			      OID_AUTO, "frequency", CTLFLAG_RD, NULL, "");
411*231d9860SAlexander Polakov 	if (oid == NULL)
412*231d9860SAlexander Polakov 		return (EOPNOTSUPP);
413*231d9860SAlexander Polakov 	leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
414*231d9860SAlexander Polakov 		      OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
415*231d9860SAlexander Polakov 			       powernow_sysctl_helper, "I",
416*231d9860SAlexander Polakov 			       "Target CPU frequency for AMD PowerNow!");
417*231d9860SAlexander Polakov 	if (leaf == NULL)
418*231d9860SAlexander Polakov 		return (EOPNOTSUPP);
419*231d9860SAlexander Polakov 	leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
420*231d9860SAlexander Polakov 		     OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0,
421*231d9860SAlexander Polakov 			       powernow_sysctl_helper, "I",
422*231d9860SAlexander Polakov 			       "Current CPU frequency for AMD PowerNow!");
423*231d9860SAlexander Polakov 	if (leaf == NULL)
424*231d9860SAlexander Polakov 		return (EOPNOTSUPP);
425*231d9860SAlexander Polakov 	leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
426*231d9860SAlexander Polakov 			 OID_AUTO, "available", CTLFLAG_RD, freqs_available,
427*231d9860SAlexander Polakov 				 sizeof(freqs_available),
428*231d9860SAlexander Polakov 			      "CPU frequencies supported by AMD PowerNow!");
429*231d9860SAlexander Polakov 	if (leaf == NULL)
430*231d9860SAlexander Polakov 		return (EOPNOTSUPP);
431*231d9860SAlexander Polakov 	return (0);
432*231d9860SAlexander Polakov }
433*231d9860SAlexander Polakov 
434*231d9860SAlexander Polakov static int
435*231d9860SAlexander Polakov powernow_modevh(struct module *m, int what, void *arg __unused)
436*231d9860SAlexander Polakov {
437*231d9860SAlexander Polakov 	int		error;
438*231d9860SAlexander Polakov 
439*231d9860SAlexander Polakov 	switch (what) {
440*231d9860SAlexander Polakov 	case MOD_LOAD:
441*231d9860SAlexander Polakov 		error = sysctl_ctx_init(&machdep_powernow_ctx);
442*231d9860SAlexander Polakov 		if (error != 0)
443*231d9860SAlexander Polakov 			break;
444*231d9860SAlexander Polakov 		error = powernow_init();
445*231d9860SAlexander Polakov 		break;
446*231d9860SAlexander Polakov 	case MOD_UNLOAD:
447*231d9860SAlexander Polakov 		if (k8pnow_current_state)
448*231d9860SAlexander Polakov 			kfree(k8pnow_current_state, M_DEVBUF);
449*231d9860SAlexander Polakov 		error = sysctl_ctx_free(&machdep_powernow_ctx);
450*231d9860SAlexander Polakov 		break;
451*231d9860SAlexander Polakov 	default:
452*231d9860SAlexander Polakov 		error = EINVAL;
453*231d9860SAlexander Polakov 		break;
454*231d9860SAlexander Polakov 	}
455*231d9860SAlexander Polakov 	return (error);
456*231d9860SAlexander Polakov }
457*231d9860SAlexander Polakov static moduledata_t powernow_mod = {
458*231d9860SAlexander Polakov 	"powernow",
459*231d9860SAlexander Polakov 	powernow_modevh,
460*231d9860SAlexander Polakov 	NULL,
461*231d9860SAlexander Polakov };
462*231d9860SAlexander Polakov 
463*231d9860SAlexander Polakov DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY);
464