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