1/* $NetBSD: powernow.c,v 1.9 2016/03/30 21:16:22 christos Exp $ */
2/* $OpenBSD: powernow-k8.c,v 1.8 2006/06/16 05:58:50 gwk Exp $ */
3
4/*-
5 * Copyright (c) 2004, 2006, 2008 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Juan Romero Pardines and Martin Vegiard.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 2004-2005 Bruno Ducrot
35 * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
36 * Copyright (c) 2004 Martin Vegiard
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 *
47 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 */
58
59#include <sys/cdefs.h>
60__KERNEL_RCSID(0, "$NetBSD: powernow.c,v 1.9 2016/03/30 21:16:22 christos Exp $");
61
62#include <sys/param.h>
63#include <sys/device.h>
64#include <sys/kmem.h>
65#include <sys/module.h>
66#include <sys/sysctl.h>
67#include <sys/xcall.h>
68
69#include <dev/isa/isareg.h>
70
71#include <x86/cpuvar.h>
72#include <x86/cputypes.h>
73#include <x86/cpu_msr.h>
74#include <x86/isa_machdep.h>
75#include <x86/powernow.h>
76#include <x86/specialreg.h>
77
78#ifdef __i386__
79/*
80 * Divide each value by 10 to get the processor multiplier.
81 * Taken from powernow-k7.c/Linux by Dave Jones.
82 */
83static int powernow_k7_fidtomult[32] = {
84 110, 115, 120, 125, 50, 55, 60, 65,
85 70, 75, 80, 85, 90, 95, 100, 105,
86 30, 190, 40, 200, 130, 135, 140, 210,
87 150, 225, 160, 165, 170, 180, -1, -1
88};
89
90/*
91 * The following CPUs do not have same CPUID signature
92 * in the PST tables, but they have correct FID/VID values.
93 */
94static struct powernow_k7_quirk {
95 uint32_t rcpusig; /* Correct cpu signature */
96 uint32_t pcpusig; /* PST cpu signature */
97} powernow_k7_quirk[] = {
98 { 0x680, 0x780 }, /* Reported by Olaf 'Rhialto' Seibert */
99 { 0x6a0, 0x781 }, /* Reported by Eric Schnoebelen */
100 { 0x6a0, 0x7a0 }, /* Reported by Nino Dehne */
101 { 0, 0 }
102};
103
104#endif /* __i386__ */
105
106struct powernow_softc {
107 device_t sc_dev;
108 struct cpu_info *sc_ci;
109 struct sysctllog *sc_log;
110 struct powernow_cpu_state *sc_state;
111 const char *sc_tech;
112 char *sc_freqs;
113 size_t sc_freqs_len;
114 unsigned int sc_freqs_cur;
115 int sc_node_target;
116 int sc_node_current;
117 int sc_flags;
118};
119
120static int powernow_match(device_t, cfdata_t, void *);
121static void powernow_attach(device_t, device_t, void *);
122static int powernow_detach(device_t, int);
123static int powernow_sysctl(device_t);
124static int powernow_sysctl_helper(SYSCTLFN_PROTO);
125
126#ifdef __i386__
127static int powernow_k7_init(device_t);
128static int powernow_k7_states(device_t, unsigned int, unsigned int);
129static int powernow_k7_setperf(device_t, unsigned int);
130static bool powernow_k7_check(uint32_t, uint32_t);
131static int powernow_k7_decode_pst(struct powernow_softc *, uint8_t *,int);
132#endif
133
134static int powernow_k8_init(device_t);
135static int powernow_k8_states(device_t, unsigned int, unsigned int);
136static int powernow_k8_setperf(device_t, unsigned int);
137static uint64_t powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl);
138static int powernow_k8_decode_pst(struct powernow_cpu_state *, uint8_t *);
139
140CFATTACH_DECL_NEW(powernow, sizeof(struct powernow_softc),
141 powernow_match, powernow_attach, powernow_detach, NULL);
142
143static int
144powernow_match(device_t parent, cfdata_t cf, void *aux)
145{
146 struct cpufeature_attach_args *cfaa = aux;
147 struct cpu_info *ci = cfaa->ci;
148 uint32_t family, regs[4];
149
150 if (strcmp(cfaa->name, "frequency") != 0)
151 return 0;
152
153 if (cpu_vendor != CPUVENDOR_AMD)
154 return 0;
155
156 family = CPUID_TO_BASEFAMILY(ci->ci_signature);
157
158 if (family != 0x06 && family != 0x0f)
159 return 0;
160 else {
161 x86_cpuid(0x80000000, regs);
162
163 if (regs[0] < 0x80000007)
164 return 0;
165
166 x86_cpuid(0x80000007, regs);
167
168 if ((regs[3] & AMD_PN_FID_VID) == AMD_PN_FID_VID)
169 return 5;
170 }
171
172 return 0;
173}
174
175static void
176powernow_attach(device_t parent, device_t self, void *aux)
177{
178 struct powernow_softc *sc = device_private(self);
179 struct cpufeature_attach_args *cfaa = aux;
180 struct cpu_info *ci = cfaa->ci;
181 uint32_t family;
182 int rv;
183
184 sc->sc_ci = ci;
185 sc->sc_dev = self;
186
187 sc->sc_flags = 0;
188 sc->sc_log = NULL;
189 sc->sc_tech = NULL;
190 sc->sc_state = NULL;
191 sc->sc_freqs = NULL;
192
193 family = CPUID_TO_BASEFAMILY(ci->ci_signature);
194
195 switch (family) {
196
197#ifdef __i386__
198 case 0x06:
199 rv = powernow_k7_init(self);
200 break;
201#endif
202 case 0x0f:
203 rv = powernow_k8_init(self);
204 break;
205
206 default:
207 rv = ENODEV;
208 }
209
210 if (rv != 0) {
211 aprint_normal(": failed to initialize (err %d)\n", rv);
212 return;
213 }
214
215 rv = powernow_sysctl(self);
216
217 if (rv != 0) {
218 aprint_normal(": failed to initialize sysctl (err %d)\n", rv);
219 return;
220 }
221
222 KASSERT(sc->sc_tech != NULL);
223
224 aprint_naive("\n");
225 aprint_normal(": AMD %s\n", sc->sc_tech);
226
227 (void)pmf_device_register(self, NULL, NULL);
228}
229
230static int
231powernow_detach(device_t self, int flags)
232{
233 struct powernow_softc *sc = device_private(self);
234
235 if (sc->sc_log != NULL)
236 sysctl_teardown(&sc->sc_log);
237
238 if (sc->sc_state != NULL)
239 kmem_free(sc->sc_state, sizeof(*sc->sc_state));
240
241 if (sc->sc_freqs != NULL)
242 kmem_free(sc->sc_freqs, sc->sc_freqs_len);
243
244 pmf_device_deregister(self);
245
246 return 0;
247}
248
249static int
250powernow_sysctl(device_t self)
251{
252 const struct sysctlnode *freqnode, *node, *pnownode;
253 struct powernow_softc *sc = device_private(self);
254 int rv;
255
256 /*
257 * Setup the sysctl sub-tree machdep.powernow.
258 */
259 rv = sysctl_createv(&sc->sc_log, 0, NULL, &node,
260 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
261 NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
262
263 if (rv != 0)
264 goto fail;
265
266 rv = sysctl_createv(&sc->sc_log, 0, &node, &pnownode,
267 0, CTLTYPE_NODE, "powernow", NULL,
268 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
269
270 if (rv != 0)
271 goto fail;
272
273 rv = sysctl_createv(&sc->sc_log, 0, &pnownode, &freqnode,
274 0, CTLTYPE_NODE, "frequency", NULL,
275 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
276
277 if (rv != 0)
278 goto fail;
279
280 rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
281 CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
282 powernow_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
283
284 if (rv != 0)
285 goto fail;
286
287 sc->sc_node_target = node->sysctl_num;
288
289 rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
290 0, CTLTYPE_INT, "current", NULL,
291 powernow_sysctl_helper, 0, (void *)sc, 0, CTL_CREATE, CTL_EOL);
292
293 if (rv != 0)
294 goto fail;
295
296 sc->sc_node_current = node->sysctl_num;
297
298 rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node,
299 0, CTLTYPE_STRING, "available", NULL,
300 NULL, 0, sc->sc_freqs, sc->sc_freqs_len, CTL_CREATE, CTL_EOL);
301
302 if (rv != 0)
303 goto fail;
304
305 return 0;
306
307fail:
308 sysctl_teardown(&sc->sc_log);
309 sc->sc_log = NULL;
310
311 return rv;
312}
313
314static int
315powernow_sysctl_helper(SYSCTLFN_ARGS)
316{
317 struct powernow_softc *sc;
318 struct sysctlnode node;
319 int fq, oldfq, error;
320 uint32_t family;
321 int rv;
322
323 fq = oldfq = 0;
324
325 node = *rnode;
326 sc = node.sysctl_data;
327
328 if (sc->sc_state == NULL)
329 return EOPNOTSUPP;
330
331 node.sysctl_data = &fq;
332
333 if (rnode->sysctl_num == sc->sc_node_target)
334 fq = oldfq = sc->sc_freqs_cur;
335 else if (rnode->sysctl_num == sc->sc_node_current)
336 fq = sc->sc_freqs_cur;
337 else
338 return EOPNOTSUPP;
339
340 error = sysctl_lookup(SYSCTLFN_CALL(&node));
341
342 if (error || newp == NULL)
343 return error;
344
345 family = CPUID_TO_BASEFAMILY(sc->sc_ci->ci_signature);
346
347 if (rnode->sysctl_num == sc->sc_node_target && fq != oldfq) {
348
349 switch (family) {
350
351#ifdef __i386__
352 case 0x06:
353 rv = powernow_k7_setperf(sc->sc_dev, fq);
354 break;
355#endif
356 case 0x0f:
357 rv = powernow_k8_setperf(sc->sc_dev, fq);
358 break;
359
360 default:
361 return ENODEV;
362 }
363
364 if (rv != 0)
365 return EINVAL;
366
367 sc->sc_freqs_cur = fq;
368 }
369
370 return 0;
371}
372
373#ifdef __i386__
374
375static int
376powernow_k7_init(device_t self)
377{
378 struct powernow_softc *sc = device_private(self);
379 uint32_t currentfid, maxfid, mhz, startvid;
380 uint64_t status;
381 int i, rv;
382 char tmp[6];
383
384 sc->sc_state = kmem_alloc(sizeof(*sc->sc_state), KM_SLEEP);
385
386 if (sc->sc_state == NULL)
387 return ENOMEM;
388
389 if (sc->sc_ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
390 sc->sc_flags |= PN7_FLAG_ERRATA_A0;
391
392 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
393 maxfid = PN7_STA_MFID(status);
394 startvid = PN7_STA_SVID(status);
395 currentfid = PN7_STA_CFID(status);
396
397 mhz = sc->sc_ci->ci_data.cpu_cc_freq / 1000000;
398 sc->sc_state->fsb = mhz / (powernow_k7_fidtomult[currentfid] / 10);
399
400 if (powernow_k7_states(self, maxfid, startvid) == 0) {
401 rv = ENXIO;
402 goto fail;
403 }
404
405 if (sc->sc_state->n_states == 0) {
406 rv = ENODEV;
407 goto fail;
408 }
409
410 sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1;
411 sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP);
412
413 if (sc->sc_freqs == NULL) {
414 rv = ENOMEM;
415 goto fail;
416 }
417
418 for (i = 0; i < sc->sc_state->n_states; i++) {
419
420 /* Skip duplicated matches. */
421 if (i > 0 && (sc->sc_state->state_table[i].freq ==
422 sc->sc_state->state_table[i - 1].freq))
423 continue;
424
425 DPRINTF(("%s: cstate->state_table.freq=%d\n",
426 __func__, sc->sc_state->state_table[i].freq));
427 DPRINTF(("%s: fid=%d vid=%d\n", __func__,
428 sc->sc_state->state_table[i].fid,
429 sc->sc_state->state_table[i].vid));
430
431 snprintf(tmp, sizeof(tmp), "%d%s",
432 sc->sc_state->state_table[i].freq,
433 i < sc->sc_state->n_states - 1 ? " " : "");
434
435 DPRINTF(("%s: tmp=%s\n", __func__, tmp));
436
437 (void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len);
438 }
439
440 sc->sc_tech = ((sc->sc_flags & PN7_FLAG_DESKTOP_VRM) != 0) ?
441 "Cool`n'Quiet K7" : "Powernow! K7";
442
443 return 0;
444
445fail:
446 kmem_free(sc->sc_state, sizeof(*sc->sc_state));
447 sc->sc_state = NULL;
448
449 return rv;
450}
451
452static int
453powernow_k7_states(device_t self, unsigned int fid, unsigned int vid)
454{
455 struct powernow_softc *sc = device_private(self);
456 struct powernow_cpu_state *cstate;
457 struct powernow_psb_s *psb;
458 struct powernow_pst_s *pst;
459 uint32_t cpusig, maxpst;
460 bool cpusig_ok;
461 uint8_t *p;
462
463 cstate = sc->sc_state;
464 cpusig = sc->sc_ci->ci_signature;
465
466 /*
467 * Look in the 0xe0000 - 0x100000 physical address
468 * range for the pst tables; 16 byte blocks
469 */
470 for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START);
471 p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN);
472 p+= BIOS_STEP) {
473
474 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
475
476 psb = (struct powernow_psb_s *)p;
477
478 if (psb->version != PN7_PSB_VERSION)
479 return 0;
480
481 cstate->sgtc = psb->ttime * cstate->fsb;
482
483 if (cstate->sgtc < 100 * cstate->fsb)
484 cstate->sgtc = 100 * cstate->fsb;
485
486 if (psb->flags & 1)
487 sc->sc_flags |= PN7_FLAG_DESKTOP_VRM;
488
489 p += sizeof(struct powernow_psb_s);
490
491 for (maxpst = 0; maxpst < psb->n_pst; maxpst++) {
492 pst = (struct powernow_pst_s*) p;
493
494 /*
495 * Some models do not have same CPUID
496 * signature in the PST table. Accept to
497 * report frequencies via a quirk table
498 * and fid/vid match.
499 */
500 DPRINTF(("%s: cpusig=0x%x pst->sig:0x%x\n",
501 __func__, cpusig, pst->signature));
502
503 cpusig_ok = powernow_k7_check(cpusig,
504 pst->signature);
505
506 if ((cpusig_ok != false &&
507 (fid == pst->fid && vid == pst->vid))) {
508 DPRINTF(("%s: pst->signature ok\n",
509 __func__));
510 if (abs(cstate->fsb - pst->pll) > 5)
511 continue;
512 cstate->n_states = pst->n_states;
513 return (powernow_k7_decode_pst(sc,
514 p + sizeof(struct powernow_pst_s),
515 cstate->n_states));
516
517 }
518
519 p += sizeof(struct powernow_pst_s) +
520 (2 * pst->n_states);
521 }
522 }
523 }
524
525 return 0;
526}
527
528static int
529powernow_k7_setperf(device_t self, unsigned int freq)
530{
531 struct powernow_softc *sc = device_private(self);
532 int cvid, cfid, vid = 0, fid = 0;
533 uint64_t status, ctl;
534 unsigned int i;
535
536 DPRINTF(("%s: cstate->n_states=%d\n",
537 __func__, sc->sc_state->n_states));
538
539 for (i = 0; i < sc->sc_state->n_states; i++) {
540
541 if (sc->sc_state->state_table[i].freq >= freq) {
542 DPRINTF(("%s: freq=%d\n", __func__, freq));
543 fid = sc->sc_state->state_table[i].fid;
544 vid = sc->sc_state->state_table[i].vid;
545 DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid));
546 break;
547 }
548 }
549
550 if (fid == 0 || vid == 0)
551 return 0;
552
553 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
554 cfid = PN7_STA_CFID(status);
555 cvid = PN7_STA_CVID(status);
556
557 /*
558 * We're already at the requested level.
559 */
560 if (fid == cfid && vid == cvid)
561 return 0;
562
563 ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
564
565 ctl |= PN7_CTR_FID(fid);
566 ctl |= PN7_CTR_VID(vid);
567 ctl |= PN7_CTR_SGTC(sc->sc_state->sgtc);
568
569 if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0)
570 x86_disable_intr();
571
572 if (powernow_k7_fidtomult[fid] < powernow_k7_fidtomult[cfid]) {
573
574 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
575
576 if (vid != cvid)
577 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
578
579 } else {
580
581 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
582
583 if (fid != cfid)
584 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
585 }
586
587 if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0)
588 x86_enable_intr();
589
590 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
591
592 cfid = PN7_STA_CFID(status);
593 cvid = PN7_STA_CVID(status);
594
595 if (cfid == fid || cvid == vid)
596 freq = sc->sc_state->state_table[i].freq;
597
598 return 0;
599}
600
601static bool
602powernow_k7_check(uint32_t real_cpusig, uint32_t pst_cpusig)
603{
604 int j;
605
606 if (real_cpusig == pst_cpusig)
607 return true;
608
609 for (j = 0; powernow_k7_quirk[j].rcpusig != 0; j++) {
610
611 if ((real_cpusig == powernow_k7_quirk[j].rcpusig) &&
612 (pst_cpusig == powernow_k7_quirk[j].pcpusig))
613 return true;
614 }
615
616 return false;
617}
618
619static int
620powernow_k7_decode_pst(struct powernow_softc *sc, uint8_t *p, int npst)
621{
622 struct powernow_cpu_state *cstate = sc->sc_state;
623 struct powernow_state state;
624 int i, j, n;
625
626 for (n = 0, i = 0; i < npst; ++i) {
627
628 state.fid = *p++;
629 state.vid = *p++;
630 state.freq = powernow_k7_fidtomult[state.fid]/10 * cstate->fsb;
631
632 if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0 &&
633 (powernow_k7_fidtomult[state.fid] % 10) == 5)
634 continue;
635
636 j = n;
637
638 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
639
640 (void)memcpy(&cstate->state_table[j],
641 &cstate->state_table[j - 1],
642 sizeof(struct powernow_state));
643
644 --j;
645 }
646
647 (void)memcpy(&cstate->state_table[j], &state,
648 sizeof(struct powernow_state));
649
650 ++n;
651 }
652
653 /*
654 * Fix powernow_max_states, if errata_a0 gave
655 * us less states than expected.
656 */
657 cstate->n_states = n;
658
659 return 1;
660}
661
662#endif /* __i386__ */
663
664static int
665powernow_k8_init(device_t self)
666{
667 struct powernow_softc *sc = device_private(self);
668 uint32_t i, maxfid, maxvid;
669 uint64_t status;
670 int rv;
671 char tmp[6];
672
673 sc->sc_state = kmem_alloc(sizeof(*sc->sc_state), KM_SLEEP);
674
675 if (sc->sc_state == NULL)
676 return ENOMEM;
677
678 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
679 maxfid = PN8_STA_MFID(status);
680 maxvid = PN8_STA_MVID(status);
681
682 if (powernow_k8_states(self, maxfid, maxvid) == 0) {
683 rv = ENXIO;
684 goto fail;
685 }
686
687 if (sc->sc_state->n_states == 0) {
688 rv = ENODEV;
689 goto fail;
690 }
691
692 sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1;
693 sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP);
694
695 if (sc->sc_freqs == NULL) {
696 rv = ENOMEM;
697 goto fail;
698 }
699
700 for (i = 0; i < sc->sc_state->n_states; i++) {
701
702 DPRINTF(("%s: cstate->state_table.freq=%d\n",
703 __func__, sc->sc_state->state_table[i].freq));
704
705 DPRINTF(("%s: fid=%d vid=%d\n", __func__,
706 sc->sc_state->state_table[i].fid,
707 sc->sc_state->state_table[i].vid));
708
709 snprintf(tmp, sizeof(tmp), "%d%s",
710 sc->sc_state->state_table[i].freq,
711 i < sc->sc_state->n_states - 1 ? " " : "");
712
713 DPRINTF(("%s: tmp=%s\n", __func__, tmp));
714
715 (void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len);
716 }
717
718 /*
719 * If start FID is different to max FID, then it is a mobile
720 * processor. If not, it is a low powered desktop processor.
721 */
722 sc->sc_tech = (PN8_STA_SFID(status) != PN8_STA_MFID(status)) ?
723 "PowerNow!" : "Cool`n'Quiet";
724
725 return 0;
726
727fail:
728 kmem_free(sc->sc_state, sizeof(*sc->sc_state));
729 sc->sc_state = NULL;
730
731 return rv;
732}
733
734static int
735powernow_k8_states(device_t self, unsigned int fid, unsigned int vid)
736{
737 struct powernow_softc *sc = device_private(self);
738 struct powernow_cpu_state *cstate;
739 struct powernow_psb_s *psb;
740 struct powernow_pst_s *pst;
741 uint32_t cpusig;
742 uint8_t *p;
743 int i;
744
745 cstate = sc->sc_state;
746 cpusig = sc->sc_ci->ci_signature;
747
748 DPRINTF(("%s: before the for loop\n", __func__));
749
750 for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START);
751 p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p += 16) {
752
753 if (memcmp(p, "AMDK7PNOW!", 10) != 0)
754 continue;
755
756 DPRINTF(("%s: inside the for loop\n", __func__));
757
758 psb = (struct powernow_psb_s *)p;
759
760 if (psb->version != 0x14) {
761 DPRINTF(("%s: psb->version != 0x14\n", __func__));
762 return 0;
763 }
764
765 cstate->vst = psb->ttime;
766 cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
767 cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
768 cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
769 cstate->low = PN8_PSB_TO_BATT(psb->reserved);
770
771 p += sizeof(struct powernow_psb_s);
772
773 for(i = 0; i < psb->n_pst; ++i) {
774
775 pst = (struct powernow_pst_s *)p;
776
777 cstate->pll = pst->pll;
778 cstate->n_states = pst->n_states;
779
780 if (cpusig == pst->signature &&
781 pst->fid == fid && pst->vid == vid) {
782
783 DPRINTF(("%s: cpusig = sig\n", __func__));
784
785 return (powernow_k8_decode_pst(cstate,
786 p+= sizeof(struct powernow_pst_s)));
787 }
788
789 p += sizeof(struct powernow_pst_s) +
790 2 * cstate->n_states;
791 }
792 }
793
794 DPRINTF(("%s: returns 0!\n", __func__));
795
796 return 0;
797}
798
799static int
800powernow_k8_setperf(device_t self, unsigned int freq)
801{
802 struct powernow_softc *sc = device_private(self);
803 int cfid, cvid, fid = 0, vid = 0;
804 uint64_t status;
805 unsigned int i;
806 uint32_t val;
807 int rvo;
808
809 /*
810 * We dont do a "pending wait" here, given
811 * that we need to ensure that the change
812 * pending bit is not stuck.
813 */
814 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
815
816 if (PN8_STA_PENDING(status))
817 return 1;
818
819 cfid = PN8_STA_CFID(status);
820 cvid = PN8_STA_CVID(status);
821
822 DPRINTF(("%s: sc->sc_state->n_states=%d\n",
823 __func__, sc->sc_state->n_states));
824
825 for (i = 0; i < sc->sc_state->n_states; i++) {
826
827 if (sc->sc_state->state_table[i].freq >= freq) {
828 DPRINTF(("%s: freq=%d\n", __func__, freq));
829 fid = sc->sc_state->state_table[i].fid;
830 vid = sc->sc_state->state_table[i].vid;
831 DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid));
832 break;
833 }
834
835 }
836
837 if (fid == cfid && vid == cvid)
838 return 0;
839
840 /*
841 * Phase 1: raise the core voltage to the
842 * requested VID if frequency is going up.
843 */
844 while (cvid > vid) {
845 val = cvid - (1 << sc->sc_state->mvs);
846 status = powernow_k8_fidvid(cfid, (val > 0) ? val : 0, 1ULL);
847 cvid = PN8_STA_CVID(status);
848 COUNT_OFF_VST(sc->sc_state->vst);
849 }
850
851 /*
852 * Then raise to voltage + RVO (if required).
853 */
854 for (rvo = sc->sc_state->rvo; rvo > 0 && cvid > 0; --rvo) {
855 /*
856 * XXX: It is not clear from the specification if we have
857 * to do that in 0.25 step or in MVS. Therefore do it
858 * as it is done under Linux.
859 */
860 status = powernow_k8_fidvid(cfid, cvid - 1, 1ULL);
861 cvid = PN8_STA_CVID(status);
862 COUNT_OFF_VST(sc->sc_state->vst);
863 }
864
865 /*
866 * Phase 2: change to requested core frequency.
867 */
868 if (cfid != fid) {
869 uint32_t vco_fid, vco_cfid;
870
871 vco_fid = FID_TO_VCO_FID(fid);
872 vco_cfid = FID_TO_VCO_FID(cfid);
873
874 while (abs(vco_fid - vco_cfid) > 2) {
875
876 if (fid > cfid) {
877 if (cfid > 6)
878 val = cfid + 2;
879 else
880 val = FID_TO_VCO_FID(cfid) + 2;
881 } else
882 val = cfid - 2;
883
884 status = powernow_k8_fidvid(val, cvid,
885 (uint64_t)sc->sc_state->pll * 1000 / 5);
886
887 cfid = PN8_STA_CFID(status);
888 COUNT_OFF_IRT(sc->sc_state->irt);
889 vco_cfid = FID_TO_VCO_FID(cfid);
890 }
891
892
893 status = powernow_k8_fidvid(fid, cvid,
894 (uint64_t)sc->sc_state->pll * 1000 / 5);
895
896 cfid = PN8_STA_CFID(status);
897 COUNT_OFF_IRT(sc->sc_state->irt);
898 }
899
900 /*
901 * Phase 3: change to requested voltage.
902 */
903 if (cvid != vid) {
904 status = powernow_k8_fidvid(cfid, vid, 1ULL);
905 cvid = PN8_STA_CVID(status);
906 COUNT_OFF_VST(sc->sc_state->vst);
907 }
908
909#if 0
910 if (cfid == fid || cvid == vid)
911 freq = sc->sc_state->state_table[i].freq;
912#endif
913
914 return 0;
915}
916
917static uint64_t
918powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl)
919{
920 struct msr_rw_info msr;
921 uint64_t status, xc;
922
923 msr.msr_read = false;
924 msr.msr_value = (ctrl << 32) | (1ULL << 16) | (vid << 8) | fid;
925 msr.msr_type = MSR_AMDK7_FIDVID_CTL;
926
927 xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL);
928 xc_wait(xc);
929
930 do {
931 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
932
933 } while (PN8_STA_PENDING(status));
934
935 return status;
936}
937
938/*
939 * Given a set of pair of FID/VID, and a number of performance
940 * states, compute a state table via an insertion sort.
941 */
942static int
943powernow_k8_decode_pst(struct powernow_cpu_state *cstate, uint8_t *p)
944{
945 struct powernow_state state;
946 int i, j, n;
947
948 for (n = 0, i = 0; i < cstate->n_states; i++) {
949
950 state.fid = *p++;
951 state.vid = *p++;
952
953 /*
954 * The minimum supported frequency per the data sheet is
955 * 800 MHz. The maximum supported frequency is 5000 MHz.
956 */
957 state.freq = 800 + state.fid * 100;
958 j = n;
959
960 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
961
962 (void)memcpy(&cstate->state_table[j],
963 &cstate->state_table[j - 1],
964 sizeof(struct powernow_state));
965
966 --j;
967 }
968
969 (void)memcpy(&cstate->state_table[j], &state,
970 sizeof(struct powernow_state));
971
972 n++;
973 }
974
975 return 1;
976}
977
978MODULE(MODULE_CLASS_DRIVER, powernow, NULL);
979
980#ifdef _MODULE
981#include "ioconf.c"
982#endif
983
984static int
985powernow_modcmd(modcmd_t cmd, void *aux)
986{
987 int error = 0;
988
989 switch (cmd) {
990 case MODULE_CMD_INIT:
991#ifdef _MODULE
992 error = config_init_component(cfdriver_ioconf_powernow,
993 cfattach_ioconf_powernow, cfdata_ioconf_powernow);
994#endif
995 return error;
996 case MODULE_CMD_FINI:
997#ifdef _MODULE
998 error = config_fini_component(cfdriver_ioconf_powernow,
999 cfattach_ioconf_powernow, cfdata_ioconf_powernow);
1000#endif
1001 return error;
1002 default:
1003 return ENOTTY;
1004 }
1005}
1006