xref: /dflybsd-src/sys/dev/powermng/powernow/powernow.c (revision 7078f92bf00cafccd0c0191c7aa92b0bb784f70e)
1231d9860SAlexander Polakov /*
2231d9860SAlexander Polakov  * Copyright (c) 2004 Martin V\xe9giard. Copyright (c) 2004-2005 Bruno Ducrot
3231d9860SAlexander Polakov  * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> Copyright
4231d9860SAlexander Polakov  * (c) 2004, 2006 The NetBSD Foundation, Inc. All rights reserved.
5231d9860SAlexander Polakov  *
6231d9860SAlexander Polakov  * This code is derived from software contributed to The NetBSD Foundation by
7231d9860SAlexander Polakov  * Juan Romero Pardines and Martin Vegiard.
8231d9860SAlexander Polakov  *
9231d9860SAlexander Polakov  * Redistribution and use in source and binary forms, with or without
10231d9860SAlexander Polakov  * modification, are permitted provided that the following conditions are
11231d9860SAlexander Polakov  * met: 1. Redistributions of source code must retain the above copyright
12231d9860SAlexander Polakov  * notice, this list of conditions and the following disclaimer. 2.
13231d9860SAlexander Polakov  * Redistributions in binary form must reproduce the above copyright notice,
14231d9860SAlexander Polakov  * this list of conditions and the following disclaimer in the documentation
15231d9860SAlexander Polakov  * and/or other materials provided with the distribution. THIS SOFTWARE IS
16231d9860SAlexander Polakov  * PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17231d9860SAlexander Polakov  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18231d9860SAlexander Polakov  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19231d9860SAlexander Polakov  * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20231d9860SAlexander Polakov  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21231d9860SAlexander Polakov  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22231d9860SAlexander Polakov  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23231d9860SAlexander Polakov  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24231d9860SAlexander Polakov  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25231d9860SAlexander Polakov  * POSSIBILITY OF SUCH DAMAGE.
26231d9860SAlexander Polakov  */
27231d9860SAlexander Polakov /* AMD POWERNOW K8 driver */
28231d9860SAlexander Polakov 
29231d9860SAlexander Polakov #include <sys/param.h>
30231d9860SAlexander Polakov #include <sys/systm.h>
31231d9860SAlexander Polakov #include <sys/malloc.h>
32231d9860SAlexander Polakov #include <sys/kernel.h>
33231d9860SAlexander Polakov #include <sys/module.h>
34231d9860SAlexander Polakov #include <sys/sysctl.h>
35231d9860SAlexander Polakov #include <bus/isa/isa.h>
36231d9860SAlexander Polakov #include <machine/cpu.h>
37231d9860SAlexander Polakov #include <machine/pmap.h>
38231d9860SAlexander Polakov #include <machine/pc/bios.h>
39231d9860SAlexander Polakov #include <machine/cpufunc.h>
40231d9860SAlexander Polakov #include <machine/md_var.h>
41231d9860SAlexander Polakov #include <machine/specialreg.h>
42db06e065SSascha Wildner #include <machine/vmparam.h>
43231d9860SAlexander Polakov 
44231d9860SAlexander Polakov #define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
45231d9860SAlexander Polakov #define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
46231d9860SAlexander Polakov #define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
47231d9860SAlexander Polakov 
48231d9860SAlexander Polakov /*
49231d9860SAlexander Polakov  * MSRs and bits used by PowerNow! technology
50231d9860SAlexander Polakov  */
51231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_CTL            0xc0010041
52231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_STATUS         0xc0010042
53231d9860SAlexander Polakov #define AMD_PN_FID_VID                  0x06
54231d9860SAlexander Polakov 
55231d9860SAlexander Polakov #define BIOS_START                      0xe0000
56231d9860SAlexander Polakov #define BIOS_LEN                        0x20000
57231d9860SAlexander Polakov #define BIOS_STEP                       16
58231d9860SAlexander Polakov 
59231d9860SAlexander Polakov #define PN8_PSB_VERSION                 0x14
60231d9860SAlexander Polakov #define PN8_PSB_TO_RVO(x)               ((x) & 0x03)
61231d9860SAlexander Polakov #define PN8_PSB_TO_IRT(x)               (((x) >> 2) & 0x03)
62231d9860SAlexander Polakov #define PN8_PSB_TO_MVS(x)               (((x) >> 4) & 0x03)
63231d9860SAlexander Polakov #define PN8_PSB_TO_BATT(x)              (((x) >> 6) & 0x03)
64231d9860SAlexander Polakov /* Bitfields used by K8 */
65231d9860SAlexander Polakov #define PN8_CTR_FID(x)                  ((x) & 0x3f)
66231d9860SAlexander Polakov #define PN8_CTR_VID(x)                  (((x) & 0x1f) << 8)
67231d9860SAlexander Polakov #define PN8_CTR_PENDING(x)              (((x) & 1) << 32)
68231d9860SAlexander Polakov #define PN8_STA_CFID(x)                 ((x) & 0x3f)
69231d9860SAlexander Polakov #define PN8_STA_SFID(x)                 (((x) >> 8) & 0x3f)
70231d9860SAlexander Polakov #define PN8_STA_MFID(x)                 (((x) >> 16) & 0x3f)
71231d9860SAlexander Polakov #define PN8_STA_PENDING(x)              (((x) >> 31) & 0x01)
72231d9860SAlexander Polakov #define PN8_STA_CVID(x)                 (((x) >> 32) & 0x1f)
73231d9860SAlexander Polakov #define PN8_STA_SVID(x)                 (((x) >> 40) & 0x1f)
74231d9860SAlexander Polakov #define PN8_STA_MVID(x)                 (((x) >> 48) & 0x1f)
75231d9860SAlexander Polakov #define PN8_PLL_LOCK(x)                 ((x) * 1000/5)
76231d9860SAlexander Polakov #define WRITE_FIDVID(fid, vid, ctrl)    \
77231d9860SAlexander Polakov         wrmsr(MSR_AMDK7_FIDVID_CTL,     \
78231d9860SAlexander Polakov             (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
79231d9860SAlexander Polakov #define COUNT_OFF_IRT(irt)              DELAY(10 * (1 << (irt)))
80231d9860SAlexander Polakov #define COUNT_OFF_VST(vst)              DELAY(20 * (vst))
81231d9860SAlexander Polakov #define FID_TO_VCO_FID(fid)             \
82231d9860SAlexander Polakov         (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
83231d9860SAlexander Polakov 
84231d9860SAlexander Polakov #define READ_PENDING_WAIT(status)                               \
85231d9860SAlexander Polakov         do {                                                    \
86231d9860SAlexander Polakov                 (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS);      \
87231d9860SAlexander Polakov         } while (PN8_STA_PENDING(status))
88124abffeSMaurizio Lombardi #define abs(x) ( (x) < 0 ? -(x) : (x) )
89231d9860SAlexander Polakov 
90231d9860SAlexander Polakov #define POWERNOW_MAX_STATES             16
91231d9860SAlexander Polakov 
92231d9860SAlexander Polakov struct k8pnow_state {
93231d9860SAlexander Polakov 	int		freq;
94231d9860SAlexander Polakov 	uint8_t		fid;
95231d9860SAlexander Polakov 	uint8_t		vid;
96231d9860SAlexander Polakov };
97231d9860SAlexander Polakov 
98231d9860SAlexander Polakov struct k8pnow_cpu_state {
99231d9860SAlexander Polakov 	struct k8pnow_state state_table[POWERNOW_MAX_STATES];
100231d9860SAlexander Polakov 	unsigned int	n_states;
101231d9860SAlexander Polakov 	unsigned int	vst;
102231d9860SAlexander Polakov 	unsigned int	mvs;
103231d9860SAlexander Polakov 	unsigned int	pll;
104231d9860SAlexander Polakov 	unsigned int	rvo;
105231d9860SAlexander Polakov 	unsigned int	irt;
106231d9860SAlexander Polakov 	int		low;
107231d9860SAlexander Polakov };
108231d9860SAlexander Polakov 
109231d9860SAlexander Polakov struct psb_s {
110231d9860SAlexander Polakov 	char		signature [10];	/* AMDK7PNOW! */
111231d9860SAlexander Polakov 	uint8_t		version;
112231d9860SAlexander Polakov 	uint8_t		flags;
113231d9860SAlexander Polakov 	uint16_t	ttime;	/* Min Settling time */
114231d9860SAlexander Polakov 	uint8_t		reserved;
115231d9860SAlexander Polakov 	uint8_t		n_pst;
116231d9860SAlexander Polakov };
117231d9860SAlexander Polakov struct pst_s {
118231d9860SAlexander Polakov 	uint32_t	cpuid;
119231d9860SAlexander Polakov 	uint8_t		pll;
120231d9860SAlexander Polakov 	uint8_t		fid;
121231d9860SAlexander Polakov 	uint8_t		vid;
122231d9860SAlexander Polakov 	uint8_t		n_states;
123231d9860SAlexander Polakov };
124231d9860SAlexander Polakov 
125231d9860SAlexander Polakov static struct k8pnow_cpu_state *k8pnow_current_state = NULL;
126231d9860SAlexander Polakov int		cpuspeed;
127231d9860SAlexander Polakov 
128231d9860SAlexander Polakov int
129231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
130231d9860SAlexander Polakov 	      unsigned int fid, unsigned int vid);
131231d9860SAlexander Polakov int
132231d9860SAlexander Polakov 		k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p);
133231d9860SAlexander Polakov 
134231d9860SAlexander Polakov /*
135231d9860SAlexander Polakov  * Given a set of pair of fid/vid, and number of performance states, compute
136231d9860SAlexander Polakov  * state_table via an insertion sort.
137231d9860SAlexander Polakov  */
138231d9860SAlexander Polakov int
k8pnow_decode_pst(struct k8pnow_cpu_state * cstate,uint8_t * p)139231d9860SAlexander Polakov k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p)
140231d9860SAlexander Polakov {
141231d9860SAlexander Polakov 	int		i         , j, n;
142231d9860SAlexander Polakov 	struct k8pnow_state state;
143231d9860SAlexander Polakov 	for (n = 0, i = 0; i < cstate->n_states; i++) {
144231d9860SAlexander Polakov 		state.fid = *p++;
145231d9860SAlexander Polakov 		state.vid = *p++;
146231d9860SAlexander Polakov 
147231d9860SAlexander Polakov 		/*
148231d9860SAlexander Polakov 		 * The minimum supported frequency per the data sheet is
149231d9860SAlexander Polakov 		 * 800MHz The maximum supported frequency is 5000MHz.
150231d9860SAlexander Polakov 		 */
151231d9860SAlexander Polakov 		state.freq = 800 + state.fid * 100;
152231d9860SAlexander Polakov 		j = n;
153231d9860SAlexander Polakov 		while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
154231d9860SAlexander Polakov 			memcpy(&cstate->state_table[j],
155231d9860SAlexander Polakov 			       &cstate->state_table[j - 1],
156231d9860SAlexander Polakov 			       sizeof(struct k8pnow_state));
157231d9860SAlexander Polakov 			--j;
158231d9860SAlexander Polakov 		}
159231d9860SAlexander Polakov 		memcpy(&cstate->state_table[j], &state,
160231d9860SAlexander Polakov 		       sizeof(struct k8pnow_state));
161231d9860SAlexander Polakov 		n++;
162231d9860SAlexander Polakov 	}
163231d9860SAlexander Polakov 	return 1;
164231d9860SAlexander Polakov }
165231d9860SAlexander Polakov 
166231d9860SAlexander Polakov int
k8pnow_states(struct k8pnow_cpu_state * cstate,uint32_t cpusig,unsigned int fid,unsigned int vid)167231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
168231d9860SAlexander Polakov 	      unsigned int fid, unsigned int vid)
169231d9860SAlexander Polakov {
170231d9860SAlexander Polakov 	struct psb_s   *psb;
171231d9860SAlexander Polakov 	struct pst_s   *pst;
172231d9860SAlexander Polakov 	uint8_t        *p;
173231d9860SAlexander Polakov 	int		i;
174231d9860SAlexander Polakov 	for (p = (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START);
175231d9860SAlexander Polakov 	     p < (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p +=
176231d9860SAlexander Polakov 	     BIOS_STEP) {
177231d9860SAlexander Polakov 		if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
178231d9860SAlexander Polakov 			psb = (struct psb_s *)p;
179231d9860SAlexander Polakov 			if (psb->version != PN8_PSB_VERSION)
180231d9860SAlexander Polakov 				return 0;
181231d9860SAlexander Polakov 			cstate->vst = psb->ttime;
182231d9860SAlexander Polakov 			cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
183231d9860SAlexander Polakov 			cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
184231d9860SAlexander Polakov 			cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
185231d9860SAlexander Polakov 			cstate->low = PN8_PSB_TO_BATT(psb->reserved);
186231d9860SAlexander Polakov 			p += sizeof(struct psb_s);
187231d9860SAlexander Polakov 			for (i = 0; i < psb->n_pst; ++i) {
188231d9860SAlexander Polakov 				pst = (struct pst_s *)p;
189231d9860SAlexander Polakov 				cstate->pll = pst->pll;
190231d9860SAlexander Polakov 				cstate->n_states = pst->n_states;
191231d9860SAlexander Polakov 				if (cpusig == pst->cpuid &&
192231d9860SAlexander Polakov 				    pst->fid == fid && pst->vid == vid) {
193231d9860SAlexander Polakov 					return (k8pnow_decode_pst(cstate,
194231d9860SAlexander Polakov 						p += sizeof(struct pst_s)));
195231d9860SAlexander Polakov 				}
196231d9860SAlexander Polakov 				p += sizeof(struct pst_s) + 2
197231d9860SAlexander Polakov 					* cstate->n_states;
198231d9860SAlexander Polakov 			}
199231d9860SAlexander Polakov 		}
200231d9860SAlexander Polakov 	}
201231d9860SAlexander Polakov 	return 0;
202231d9860SAlexander Polakov }
203231d9860SAlexander Polakov 
204231d9860SAlexander Polakov static int
k8_get_curfreq(void)205231d9860SAlexander Polakov k8_get_curfreq(void)
206231d9860SAlexander Polakov {
207231d9860SAlexander Polakov 	unsigned int	i;
208231d9860SAlexander Polakov 	uint64_t	status;
209*74205289SSascha Wildner 	int		cfid, cvid;
210231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
211231d9860SAlexander Polakov 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
212231d9860SAlexander Polakov 	if (PN8_STA_PENDING(status))
213231d9860SAlexander Polakov 		return 1;
214231d9860SAlexander Polakov 	cfid = PN8_STA_CFID(status);
215231d9860SAlexander Polakov 	cvid = PN8_STA_CVID(status);
216231d9860SAlexander Polakov 	cstate = k8pnow_current_state;
217231d9860SAlexander Polakov 	for (i = 0; i < cstate->n_states; i++) {
218231d9860SAlexander Polakov 		if (cstate->state_table[i].fid == cfid &&
219*74205289SSascha Wildner 		    cstate->state_table[i].vid == cvid)
220231d9860SAlexander Polakov 			return (cstate->state_table[i].freq);
221231d9860SAlexander Polakov 	}
222231d9860SAlexander Polakov 	/* Not reached */
223231d9860SAlexander Polakov 	return -1;
224231d9860SAlexander Polakov }
225231d9860SAlexander Polakov 
226231d9860SAlexander Polakov static int
k8_powernow_setperf(unsigned int freq)227231d9860SAlexander Polakov k8_powernow_setperf(unsigned int freq)
228231d9860SAlexander Polakov {
229231d9860SAlexander Polakov 	unsigned int	i;
230231d9860SAlexander Polakov 	uint64_t	status;
231231d9860SAlexander Polakov 	uint32_t	val;
232231d9860SAlexander Polakov 	int		cfid      , cvid, fid = 0, vid = 0;
233231d9860SAlexander Polakov 	int		rvo;
234231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
235231d9860SAlexander Polakov 	/*
236231d9860SAlexander Polakov 	 * We dont do a k8pnow_read_pending_wait here, need to ensure that
237231d9860SAlexander Polakov 	 * the change pending bit isn't stuck,
238231d9860SAlexander Polakov 	 */
239231d9860SAlexander Polakov 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
240231d9860SAlexander Polakov 	if (PN8_STA_PENDING(status))
241231d9860SAlexander Polakov 		return 1;
242231d9860SAlexander Polakov 	cfid = PN8_STA_CFID(status);
243231d9860SAlexander Polakov 	cvid = PN8_STA_CVID(status);
244231d9860SAlexander Polakov 	cstate = k8pnow_current_state;
245231d9860SAlexander Polakov 	for (i = 0; i < cstate->n_states; i++) {
246231d9860SAlexander Polakov 		if (cstate->state_table[i].freq >= freq) {
247231d9860SAlexander Polakov 			fid = cstate->state_table[i].fid;
248231d9860SAlexander Polakov 			vid = cstate->state_table[i].vid;
249231d9860SAlexander Polakov 			break;
250231d9860SAlexander Polakov 		}
251231d9860SAlexander Polakov 	}
252231d9860SAlexander Polakov 	if (fid == cfid && vid == cvid) {
253231d9860SAlexander Polakov 		cpuspeed = freq;
254231d9860SAlexander Polakov 		return 0;
255231d9860SAlexander Polakov 	}
256231d9860SAlexander Polakov 	/*
257231d9860SAlexander Polakov 	 * Phase 1: Raise core voltage to requested VID if frequency is going
258231d9860SAlexander Polakov 	 * up.
259231d9860SAlexander Polakov 	 */
260231d9860SAlexander Polakov 	while (cvid > vid) {
261231d9860SAlexander Polakov 		val = cvid - (1 << cstate->mvs);
262231d9860SAlexander Polakov 		WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
263231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
264231d9860SAlexander Polakov 		cvid = PN8_STA_CVID(status);
265231d9860SAlexander Polakov 		COUNT_OFF_VST(cstate->vst);
266231d9860SAlexander Polakov 	}
267231d9860SAlexander Polakov 
268231d9860SAlexander Polakov 	/* ... then raise to voltage + RVO (if required) */
269231d9860SAlexander Polakov 	for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
270231d9860SAlexander Polakov 		/*
271231d9860SAlexander Polakov 		 * XXX It's not clear from spec if we have to do that in 0.25
272231d9860SAlexander Polakov 		 * step or in MVS.  Therefore do it as it's done under Linux
273231d9860SAlexander Polakov 		 */
274231d9860SAlexander Polakov 		WRITE_FIDVID(cfid, cvid - 1, 1ULL);
275231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
276231d9860SAlexander Polakov 		cvid = PN8_STA_CVID(status);
277231d9860SAlexander Polakov 		COUNT_OFF_VST(cstate->vst);
278231d9860SAlexander Polakov 	}
279231d9860SAlexander Polakov 	/* Phase 2: change to requested core frequency */
280231d9860SAlexander Polakov 	if (cfid != fid) {
281231d9860SAlexander Polakov 		uint32_t	vco_fid, vco_cfid;
282231d9860SAlexander Polakov 		vco_fid = FID_TO_VCO_FID(fid);
283231d9860SAlexander Polakov 		vco_cfid = FID_TO_VCO_FID(cfid);
284231d9860SAlexander Polakov 		while (abs(vco_fid - vco_cfid) > 2) {
285231d9860SAlexander Polakov 			if (fid > cfid) {
286231d9860SAlexander Polakov 				if (cfid > 6)
287231d9860SAlexander Polakov 					val = cfid + 2;
288231d9860SAlexander Polakov 				else
289231d9860SAlexander Polakov 					val = FID_TO_VCO_FID(cfid) + 2;
290231d9860SAlexander Polakov 			} else
291231d9860SAlexander Polakov 				val = cfid - 2;
292231d9860SAlexander Polakov 			WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5);
293231d9860SAlexander Polakov 			READ_PENDING_WAIT(status);
294231d9860SAlexander Polakov 			cfid = PN8_STA_CFID(status);
295231d9860SAlexander Polakov 			COUNT_OFF_IRT(cstate->irt);
296231d9860SAlexander Polakov 			vco_cfid = FID_TO_VCO_FID(cfid);
297231d9860SAlexander Polakov 		}
298231d9860SAlexander Polakov 		WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
299231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
300231d9860SAlexander Polakov 		cfid = PN8_STA_CFID(status);
301231d9860SAlexander Polakov 		COUNT_OFF_IRT(cstate->irt);
302231d9860SAlexander Polakov 	}
303231d9860SAlexander Polakov 	/* Phase 3: change to requested voltage */
304231d9860SAlexander Polakov 	if (cvid != vid) {
305231d9860SAlexander Polakov 		WRITE_FIDVID(cfid, vid, 1ULL);
306231d9860SAlexander Polakov 		READ_PENDING_WAIT(status);
307231d9860SAlexander Polakov 		cvid = PN8_STA_CVID(status);
308231d9860SAlexander Polakov 		COUNT_OFF_VST(cstate->vst);
309231d9860SAlexander Polakov 	}
310231d9860SAlexander Polakov 	if (cfid == fid || cvid == vid)
311231d9860SAlexander Polakov 		cpuspeed = cstate->state_table[i].freq;
312231d9860SAlexander Polakov 	return 0;
313231d9860SAlexander Polakov }
314231d9860SAlexander Polakov 
315231d9860SAlexander Polakov static int
powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)316231d9860SAlexander Polakov powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)
317231d9860SAlexander Polakov {
318231d9860SAlexander Polakov 	int		fq        , err = 0;
319231d9860SAlexander Polakov 	int		i;
320231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
321231d9860SAlexander Polakov 	struct k8pnow_state *state;
322231d9860SAlexander Polakov 	cstate = k8pnow_current_state;
323231d9860SAlexander Polakov 	if (req->newptr != NULL) {
324231d9860SAlexander Polakov 		err = SYSCTL_IN(req, &fq, sizeof(fq));
325231d9860SAlexander Polakov 		if (err)
326231d9860SAlexander Polakov 			return err;
327231d9860SAlexander Polakov 		if (fq != cpuspeed) {
328231d9860SAlexander Polakov 			for (i = cstate->n_states; i > 0; i--) {
329231d9860SAlexander Polakov 				state = &cstate->state_table[i - 1];
330231d9860SAlexander Polakov 				if (fq == state->freq) {
331231d9860SAlexander Polakov 					k8_powernow_setperf(fq);
332231d9860SAlexander Polakov 					break;
333231d9860SAlexander Polakov 				}
334231d9860SAlexander Polakov 			}
335231d9860SAlexander Polakov 		}
336231d9860SAlexander Polakov 	} else {
337231d9860SAlexander Polakov 		err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed));
338231d9860SAlexander Polakov 	}
339231d9860SAlexander Polakov 	return err;
340231d9860SAlexander Polakov }
341231d9860SAlexander Polakov 
342231d9860SAlexander Polakov static struct sysctl_ctx_list machdep_powernow_ctx;
343231d9860SAlexander Polakov static char	freqs_available[80];
344231d9860SAlexander Polakov 
345231d9860SAlexander Polakov static int
powernow_init(void)346231d9860SAlexander Polakov powernow_init(void)
347231d9860SAlexander Polakov {
348231d9860SAlexander Polakov 	uint64_t	status;
349231d9860SAlexander Polakov 	size_t		len    , freq_len;
350231d9860SAlexander Polakov 	uint32_t	maxfid, maxvid, i;
351231d9860SAlexander Polakov 	struct k8pnow_cpu_state *cstate;
352231d9860SAlexander Polakov 	struct k8pnow_state *state;
353231d9860SAlexander Polakov 	const char     *techname;
354231d9860SAlexander Polakov 	u_int32_t	regs  [4];
355231d9860SAlexander Polakov 	cpuspeed = 0;
356231d9860SAlexander Polakov 	struct sysctl_oid *oid, *leaf;
357231d9860SAlexander Polakov 
358231d9860SAlexander Polakov 	do_cpuid(0x80000000, regs);
359231d9860SAlexander Polakov 	if (regs[0] < 0x80000007)
360231d9860SAlexander Polakov 		return 1;
361231d9860SAlexander Polakov 	do_cpuid(0x80000007, regs);
362231d9860SAlexander Polakov 	if (!(regs[3] & AMD_PN_FID_VID))
363231d9860SAlexander Polakov 		return 2;
364231d9860SAlexander Polakov 	/* Extended CPUID signature value */
365231d9860SAlexander Polakov 	do_cpuid(0x80000001, regs);
366231d9860SAlexander Polakov 	cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK);
367231d9860SAlexander Polakov 	cstate->n_states = 0;
368231d9860SAlexander Polakov 
369231d9860SAlexander Polakov 	status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
370231d9860SAlexander Polakov 	maxfid = PN8_STA_MFID(status);
371231d9860SAlexander Polakov 	maxvid = PN8_STA_MVID(status);
372231d9860SAlexander Polakov 
373231d9860SAlexander Polakov 	if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
374231d9860SAlexander Polakov 		techname = "PowerNow!";
375231d9860SAlexander Polakov 	else
376231d9860SAlexander Polakov 		techname = "Cool`n'Quiet";
377231d9860SAlexander Polakov 	k8pnow_states(cstate, regs[0], maxfid, maxvid);
378231d9860SAlexander Polakov 	len = 0;
379231d9860SAlexander Polakov 	if (cstate->n_states) {
380231d9860SAlexander Polakov 		freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1;
381231d9860SAlexander Polakov 		kprintf("%s speeds:",
382231d9860SAlexander Polakov 			techname);
383231d9860SAlexander Polakov 		for (i = cstate->n_states; i > 0; i--) {
384231d9860SAlexander Polakov 			state = &cstate->state_table[i - 1];
385231d9860SAlexander Polakov 			kprintf(" %d", state->freq);
386231d9860SAlexander Polakov 			len += ksnprintf(freqs_available + len, freq_len - len, "%d%s",
387231d9860SAlexander Polakov 					 state->freq,
388231d9860SAlexander Polakov 					 i > 1 ? " " : "");
389231d9860SAlexander Polakov 		}
390231d9860SAlexander Polakov 		kprintf(" MHz\n");
391231d9860SAlexander Polakov 		k8pnow_current_state = cstate;
392231d9860SAlexander Polakov 		k8_powernow_setperf(k8_get_curfreq());
393231d9860SAlexander Polakov 	} else {
394231d9860SAlexander Polakov 		kfree(cstate, M_DEVBUF);
395231d9860SAlexander Polakov 		kprintf("powernow: no power states found\n");
396231d9860SAlexander Polakov 		return 3;
397231d9860SAlexander Polakov 	}
398231d9860SAlexander Polakov 
399231d9860SAlexander Polakov 	/*
400231d9860SAlexander Polakov 	 * Setup the sysctl sub-tree machdep.powernow.*
401231d9860SAlexander Polakov 	 */
402231d9860SAlexander Polakov 	oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx,
403231d9860SAlexander Polakov 		     SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow",
404231d9860SAlexander Polakov 			      CTLFLAG_RD, NULL, "");
405231d9860SAlexander Polakov 	if (oid == NULL)
406231d9860SAlexander Polakov 		return (EOPNOTSUPP);
407231d9860SAlexander Polakov 	oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
408231d9860SAlexander Polakov 			      OID_AUTO, "frequency", CTLFLAG_RD, NULL, "");
409231d9860SAlexander Polakov 	if (oid == NULL)
410231d9860SAlexander Polakov 		return (EOPNOTSUPP);
411231d9860SAlexander Polakov 	leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
412231d9860SAlexander Polakov 		      OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
413231d9860SAlexander Polakov 			       powernow_sysctl_helper, "I",
414231d9860SAlexander Polakov 			       "Target CPU frequency for AMD PowerNow!");
415231d9860SAlexander Polakov 	if (leaf == NULL)
416231d9860SAlexander Polakov 		return (EOPNOTSUPP);
417231d9860SAlexander Polakov 	leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
418231d9860SAlexander Polakov 		     OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0,
419231d9860SAlexander Polakov 			       powernow_sysctl_helper, "I",
420231d9860SAlexander Polakov 			       "Current CPU frequency for AMD PowerNow!");
421231d9860SAlexander Polakov 	if (leaf == NULL)
422231d9860SAlexander Polakov 		return (EOPNOTSUPP);
423231d9860SAlexander Polakov 	leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
424231d9860SAlexander Polakov 			 OID_AUTO, "available", CTLFLAG_RD, freqs_available,
425231d9860SAlexander Polakov 				 sizeof(freqs_available),
426231d9860SAlexander Polakov 			      "CPU frequencies supported by AMD PowerNow!");
427231d9860SAlexander Polakov 	if (leaf == NULL)
428231d9860SAlexander Polakov 		return (EOPNOTSUPP);
429231d9860SAlexander Polakov 	return (0);
430231d9860SAlexander Polakov }
431231d9860SAlexander Polakov 
432231d9860SAlexander Polakov static int
powernow_modevh(struct module * m,int what,void * arg __unused)433231d9860SAlexander Polakov powernow_modevh(struct module *m, int what, void *arg __unused)
434231d9860SAlexander Polakov {
435231d9860SAlexander Polakov 	int		error;
436231d9860SAlexander Polakov 
437231d9860SAlexander Polakov 	switch (what) {
438231d9860SAlexander Polakov 	case MOD_LOAD:
439231d9860SAlexander Polakov 		error = sysctl_ctx_init(&machdep_powernow_ctx);
440231d9860SAlexander Polakov 		if (error != 0)
441231d9860SAlexander Polakov 			break;
442231d9860SAlexander Polakov 		error = powernow_init();
443231d9860SAlexander Polakov 		break;
444231d9860SAlexander Polakov 	case MOD_UNLOAD:
445231d9860SAlexander Polakov 		if (k8pnow_current_state)
446231d9860SAlexander Polakov 			kfree(k8pnow_current_state, M_DEVBUF);
447231d9860SAlexander Polakov 		error = sysctl_ctx_free(&machdep_powernow_ctx);
448231d9860SAlexander Polakov 		break;
449231d9860SAlexander Polakov 	default:
450231d9860SAlexander Polakov 		error = EINVAL;
451231d9860SAlexander Polakov 		break;
452231d9860SAlexander Polakov 	}
453231d9860SAlexander Polakov 	return (error);
454231d9860SAlexander Polakov }
455231d9860SAlexander Polakov static moduledata_t powernow_mod = {
456231d9860SAlexander Polakov 	"powernow",
457231d9860SAlexander Polakov 	powernow_modevh,
458231d9860SAlexander Polakov 	NULL,
459231d9860SAlexander Polakov };
460231d9860SAlexander Polakov 
461231d9860SAlexander Polakov DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY);
462