1 /* $NetBSD: powernow.c,v 1.12 2021/10/07 12:52:27 msaitoh 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.12 2021/10/07 12:52:27 msaitoh 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 */
83 static 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 */
94 static 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
106 struct 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
120 static int powernow_match(device_t, cfdata_t, void *);
121 static void powernow_attach(device_t, device_t, void *);
122 static int powernow_detach(device_t, int);
123 static int powernow_sysctl(device_t);
124 static int powernow_sysctl_helper(SYSCTLFN_PROTO);
125
126 #ifdef __i386__
127 static int powernow_k7_init(device_t);
128 static int powernow_k7_states(device_t, unsigned int, unsigned int);
129 static int powernow_k7_setperf(device_t, unsigned int);
130 static bool powernow_k7_check(uint32_t, uint32_t);
131 static int powernow_k7_decode_pst(struct powernow_softc *, uint8_t *,int);
132 #endif
133
134 static int powernow_k8_init(device_t);
135 static int powernow_k8_states(device_t, unsigned int, unsigned int);
136 static int powernow_k8_setperf(device_t, unsigned int);
137 static uint64_t powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl);
138 static int powernow_k8_decode_pst(struct powernow_cpu_state *, uint8_t *);
139
140 CFATTACH_DECL_NEW(powernow, sizeof(struct powernow_softc),
141 powernow_match, powernow_attach, powernow_detach, NULL);
142
143 static int
powernow_match(device_t parent,cfdata_t cf,void * aux)144 powernow_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
175 static void
powernow_attach(device_t parent,device_t self,void * aux)176 powernow_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
230 static int
powernow_detach(device_t self,int flags)231 powernow_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
249 static int
powernow_sysctl(device_t self)250 powernow_sysctl(device_t self)
251 {
252 const struct sysctlnode *freqnode, *node, *cpunode;
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, &cpunode,
267 0, CTLTYPE_NODE, "cpu", 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, &cpunode, &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
307 fail:
308 sysctl_teardown(&sc->sc_log);
309 sc->sc_log = NULL;
310
311 return rv;
312 }
313
314 static int
powernow_sysctl_helper(SYSCTLFN_ARGS)315 powernow_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
375 static int
powernow_k7_init(device_t self)376 powernow_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_ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
387 sc->sc_flags |= PN7_FLAG_ERRATA_A0;
388
389 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
390 maxfid = PN7_STA_MFID(status);
391 startvid = PN7_STA_SVID(status);
392 currentfid = PN7_STA_CFID(status);
393
394 mhz = sc->sc_ci->ci_data.cpu_cc_freq / 1000000;
395 sc->sc_state->fsb = mhz / (powernow_k7_fidtomult[currentfid] / 10);
396
397 if (powernow_k7_states(self, maxfid, startvid) == 0) {
398 rv = ENXIO;
399 goto fail;
400 }
401
402 if (sc->sc_state->n_states == 0) {
403 rv = ENODEV;
404 goto fail;
405 }
406
407 sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1;
408 sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP);
409
410 for (i = 0; i < sc->sc_state->n_states; i++) {
411
412 /* Skip duplicated matches. */
413 if (i > 0 && (sc->sc_state->state_table[i].freq ==
414 sc->sc_state->state_table[i - 1].freq))
415 continue;
416
417 DPRINTF(("%s: cstate->state_table.freq=%d\n",
418 __func__, sc->sc_state->state_table[i].freq));
419 DPRINTF(("%s: fid=%d vid=%d\n", __func__,
420 sc->sc_state->state_table[i].fid,
421 sc->sc_state->state_table[i].vid));
422
423 snprintf(tmp, sizeof(tmp), "%d%s",
424 sc->sc_state->state_table[i].freq,
425 i < sc->sc_state->n_states - 1 ? " " : "");
426
427 DPRINTF(("%s: tmp=%s\n", __func__, tmp));
428
429 (void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len);
430 }
431
432 sc->sc_tech = ((sc->sc_flags & PN7_FLAG_DESKTOP_VRM) != 0) ?
433 "Cool`n'Quiet K7" : "Powernow! K7";
434
435 return 0;
436
437 fail:
438 kmem_free(sc->sc_state, sizeof(*sc->sc_state));
439 sc->sc_state = NULL;
440
441 return rv;
442 }
443
444 static int
powernow_k7_states(device_t self,unsigned int fid,unsigned int vid)445 powernow_k7_states(device_t self, unsigned int fid, unsigned int vid)
446 {
447 struct powernow_softc *sc = device_private(self);
448 struct powernow_cpu_state *cstate;
449 struct powernow_psb_s *psb;
450 struct powernow_pst_s *pst;
451 uint32_t cpusig, maxpst;
452 bool cpusig_ok;
453 uint8_t *p;
454
455 cstate = sc->sc_state;
456 cpusig = sc->sc_ci->ci_signature;
457
458 /*
459 * Look in the 0xe0000 - 0x100000 physical address
460 * range for the pst tables; 16 byte blocks
461 */
462 for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START);
463 p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN);
464 p+= BIOS_STEP) {
465
466 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
467
468 psb = (struct powernow_psb_s *)p;
469
470 if (psb->version != PN7_PSB_VERSION)
471 return 0;
472
473 cstate->sgtc = psb->ttime * cstate->fsb;
474
475 if (cstate->sgtc < 100 * cstate->fsb)
476 cstate->sgtc = 100 * cstate->fsb;
477
478 if (psb->flags & 1)
479 sc->sc_flags |= PN7_FLAG_DESKTOP_VRM;
480
481 p += sizeof(struct powernow_psb_s);
482
483 for (maxpst = 0; maxpst < psb->n_pst; maxpst++) {
484 pst = (struct powernow_pst_s*) p;
485
486 /*
487 * Some models do not have same CPUID
488 * signature in the PST table. Accept to
489 * report frequencies via a quirk table
490 * and fid/vid match.
491 */
492 DPRINTF(("%s: cpusig=0x%x pst->sig:0x%x\n",
493 __func__, cpusig, pst->signature));
494
495 cpusig_ok = powernow_k7_check(cpusig,
496 pst->signature);
497
498 if ((cpusig_ok != false &&
499 (fid == pst->fid && vid == pst->vid))) {
500 DPRINTF(("%s: pst->signature ok\n",
501 __func__));
502 if (abs(cstate->fsb - pst->pll) > 5)
503 continue;
504 cstate->n_states = pst->n_states;
505 return (powernow_k7_decode_pst(sc,
506 p + sizeof(struct powernow_pst_s),
507 cstate->n_states));
508
509 }
510
511 p += sizeof(struct powernow_pst_s) +
512 (2 * pst->n_states);
513 }
514 }
515 }
516
517 return 0;
518 }
519
520 static int
powernow_k7_setperf(device_t self,unsigned int freq)521 powernow_k7_setperf(device_t self, unsigned int freq)
522 {
523 struct powernow_softc *sc = device_private(self);
524 int cvid, cfid, vid = 0, fid = 0;
525 uint64_t status, ctl;
526 unsigned int i;
527
528 DPRINTF(("%s: cstate->n_states=%d\n",
529 __func__, sc->sc_state->n_states));
530
531 for (i = 0; i < sc->sc_state->n_states; i++) {
532
533 if (sc->sc_state->state_table[i].freq >= freq) {
534 DPRINTF(("%s: freq=%d\n", __func__, freq));
535 fid = sc->sc_state->state_table[i].fid;
536 vid = sc->sc_state->state_table[i].vid;
537 DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid));
538 break;
539 }
540 }
541
542 if (fid == 0 || vid == 0)
543 return 0;
544
545 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
546 cfid = PN7_STA_CFID(status);
547 cvid = PN7_STA_CVID(status);
548
549 /*
550 * We're already at the requested level.
551 */
552 if (fid == cfid && vid == cvid)
553 return 0;
554
555 ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
556
557 ctl |= PN7_CTR_FID(fid);
558 ctl |= PN7_CTR_VID(vid);
559 ctl |= PN7_CTR_SGTC(sc->sc_state->sgtc);
560
561 if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0)
562 x86_disable_intr();
563
564 if (powernow_k7_fidtomult[fid] < powernow_k7_fidtomult[cfid]) {
565
566 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
567
568 if (vid != cvid)
569 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
570
571 } else {
572
573 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
574
575 if (fid != cfid)
576 wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
577 }
578
579 if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0)
580 x86_enable_intr();
581
582 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
583
584 cfid = PN7_STA_CFID(status);
585 cvid = PN7_STA_CVID(status);
586
587 if (cfid == fid || cvid == vid)
588 freq = sc->sc_state->state_table[i].freq;
589
590 return 0;
591 }
592
593 static bool
powernow_k7_check(uint32_t real_cpusig,uint32_t pst_cpusig)594 powernow_k7_check(uint32_t real_cpusig, uint32_t pst_cpusig)
595 {
596 int j;
597
598 if (real_cpusig == pst_cpusig)
599 return true;
600
601 for (j = 0; powernow_k7_quirk[j].rcpusig != 0; j++) {
602
603 if ((real_cpusig == powernow_k7_quirk[j].rcpusig) &&
604 (pst_cpusig == powernow_k7_quirk[j].pcpusig))
605 return true;
606 }
607
608 return false;
609 }
610
611 static int
powernow_k7_decode_pst(struct powernow_softc * sc,uint8_t * p,int npst)612 powernow_k7_decode_pst(struct powernow_softc *sc, uint8_t *p, int npst)
613 {
614 struct powernow_cpu_state *cstate = sc->sc_state;
615 struct powernow_state state;
616 int i, j, n;
617
618 for (n = 0, i = 0; i < npst; ++i) {
619
620 state.fid = *p++;
621 state.vid = *p++;
622 state.freq = powernow_k7_fidtomult[state.fid]/10 * cstate->fsb;
623
624 if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0 &&
625 (powernow_k7_fidtomult[state.fid] % 10) == 5)
626 continue;
627
628 j = n;
629
630 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
631
632 (void)memcpy(&cstate->state_table[j],
633 &cstate->state_table[j - 1],
634 sizeof(struct powernow_state));
635
636 --j;
637 }
638
639 (void)memcpy(&cstate->state_table[j], &state,
640 sizeof(struct powernow_state));
641
642 ++n;
643 }
644
645 /*
646 * Fix powernow_max_states, if errata_a0 gave
647 * us less states than expected.
648 */
649 cstate->n_states = n;
650
651 return 1;
652 }
653
654 #endif /* __i386__ */
655
656 static int
powernow_k8_init(device_t self)657 powernow_k8_init(device_t self)
658 {
659 struct powernow_softc *sc = device_private(self);
660 uint32_t i, maxfid, maxvid;
661 uint64_t status;
662 int rv;
663 char tmp[6];
664
665 sc->sc_state = kmem_alloc(sizeof(*sc->sc_state), KM_SLEEP);
666
667 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
668 maxfid = PN8_STA_MFID(status);
669 maxvid = PN8_STA_MVID(status);
670
671 if (powernow_k8_states(self, maxfid, maxvid) == 0) {
672 rv = ENXIO;
673 goto fail;
674 }
675
676 if (sc->sc_state->n_states == 0) {
677 rv = ENODEV;
678 goto fail;
679 }
680
681 sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1;
682 sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP);
683
684 for (i = 0; i < sc->sc_state->n_states; i++) {
685
686 DPRINTF(("%s: cstate->state_table.freq=%d\n",
687 __func__, sc->sc_state->state_table[i].freq));
688
689 DPRINTF(("%s: fid=%d vid=%d\n", __func__,
690 sc->sc_state->state_table[i].fid,
691 sc->sc_state->state_table[i].vid));
692
693 snprintf(tmp, sizeof(tmp), "%d%s",
694 sc->sc_state->state_table[i].freq,
695 i < sc->sc_state->n_states - 1 ? " " : "");
696
697 DPRINTF(("%s: tmp=%s\n", __func__, tmp));
698
699 (void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len);
700 }
701
702 /*
703 * If start FID is different to max FID, then it is a mobile
704 * processor. If not, it is a low powered desktop processor.
705 */
706 sc->sc_tech = (PN8_STA_SFID(status) != PN8_STA_MFID(status)) ?
707 "PowerNow!" : "Cool`n'Quiet";
708
709 return 0;
710
711 fail:
712 kmem_free(sc->sc_state, sizeof(*sc->sc_state));
713 sc->sc_state = NULL;
714
715 return rv;
716 }
717
718 static int
powernow_k8_states(device_t self,unsigned int fid,unsigned int vid)719 powernow_k8_states(device_t self, unsigned int fid, unsigned int vid)
720 {
721 struct powernow_softc *sc = device_private(self);
722 struct powernow_cpu_state *cstate;
723 struct powernow_psb_s *psb;
724 struct powernow_pst_s *pst;
725 uint32_t cpusig;
726 uint8_t *p;
727 int i;
728
729 cstate = sc->sc_state;
730 cpusig = sc->sc_ci->ci_signature;
731
732 DPRINTF(("%s: before the for loop\n", __func__));
733
734 for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START);
735 p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p += 16) {
736
737 if (memcmp(p, "AMDK7PNOW!", 10) != 0)
738 continue;
739
740 DPRINTF(("%s: inside the for loop\n", __func__));
741
742 psb = (struct powernow_psb_s *)p;
743
744 if (psb->version != 0x14) {
745 DPRINTF(("%s: psb->version != 0x14\n", __func__));
746 return 0;
747 }
748
749 cstate->vst = psb->ttime;
750 cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
751 cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
752 cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
753 cstate->low = PN8_PSB_TO_BATT(psb->reserved);
754
755 p += sizeof(struct powernow_psb_s);
756
757 for (i = 0; i < psb->n_pst; ++i) {
758
759 pst = (struct powernow_pst_s *)p;
760
761 cstate->pll = pst->pll;
762 cstate->n_states = pst->n_states;
763
764 if (cpusig == pst->signature &&
765 pst->fid == fid && pst->vid == vid) {
766
767 DPRINTF(("%s: cpusig = sig\n", __func__));
768
769 return (powernow_k8_decode_pst(cstate,
770 p+= sizeof(struct powernow_pst_s)));
771 }
772
773 p += sizeof(struct powernow_pst_s) +
774 2 * cstate->n_states;
775 }
776 }
777
778 DPRINTF(("%s: returns 0!\n", __func__));
779
780 return 0;
781 }
782
783 static int
powernow_k8_setperf(device_t self,unsigned int freq)784 powernow_k8_setperf(device_t self, unsigned int freq)
785 {
786 struct powernow_softc *sc = device_private(self);
787 int cfid, cvid, fid = 0, vid = 0;
788 uint64_t status;
789 unsigned int i;
790 uint32_t val;
791 int rvo;
792
793 /*
794 * We dont do a "pending wait" here, given
795 * that we need to ensure that the change
796 * pending bit is not stuck.
797 */
798 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
799
800 if (PN8_STA_PENDING(status))
801 return 1;
802
803 cfid = PN8_STA_CFID(status);
804 cvid = PN8_STA_CVID(status);
805
806 DPRINTF(("%s: sc->sc_state->n_states=%d\n",
807 __func__, sc->sc_state->n_states));
808
809 for (i = 0; i < sc->sc_state->n_states; i++) {
810
811 if (sc->sc_state->state_table[i].freq >= freq) {
812 DPRINTF(("%s: freq=%d\n", __func__, freq));
813 fid = sc->sc_state->state_table[i].fid;
814 vid = sc->sc_state->state_table[i].vid;
815 DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid));
816 break;
817 }
818
819 }
820
821 if (fid == cfid && vid == cvid)
822 return 0;
823
824 /*
825 * Phase 1: raise the core voltage to the
826 * requested VID if frequency is going up.
827 */
828 while (cvid > vid) {
829 val = cvid - (1 << sc->sc_state->mvs);
830 status = powernow_k8_fidvid(cfid, (val > 0) ? val : 0, 1ULL);
831 cvid = PN8_STA_CVID(status);
832 COUNT_OFF_VST(sc->sc_state->vst);
833 }
834
835 /*
836 * Then raise to voltage + RVO (if required).
837 */
838 for (rvo = sc->sc_state->rvo; rvo > 0 && cvid > 0; --rvo) {
839 /*
840 * XXX: It is not clear from the specification if we have
841 * to do that in 0.25 step or in MVS. Therefore do it
842 * as it is done under Linux.
843 */
844 status = powernow_k8_fidvid(cfid, cvid - 1, 1ULL);
845 cvid = PN8_STA_CVID(status);
846 COUNT_OFF_VST(sc->sc_state->vst);
847 }
848
849 /*
850 * Phase 2: change to requested core frequency.
851 */
852 if (cfid != fid) {
853 uint32_t vco_fid, vco_cfid;
854
855 vco_fid = FID_TO_VCO_FID(fid);
856 vco_cfid = FID_TO_VCO_FID(cfid);
857
858 while (abs(vco_fid - vco_cfid) > 2) {
859
860 if (fid > cfid) {
861 if (cfid > 6)
862 val = cfid + 2;
863 else
864 val = FID_TO_VCO_FID(cfid) + 2;
865 } else
866 val = cfid - 2;
867
868 status = powernow_k8_fidvid(val, cvid,
869 (uint64_t)sc->sc_state->pll * 1000 / 5);
870
871 cfid = PN8_STA_CFID(status);
872 COUNT_OFF_IRT(sc->sc_state->irt);
873 vco_cfid = FID_TO_VCO_FID(cfid);
874 }
875
876
877 status = powernow_k8_fidvid(fid, cvid,
878 (uint64_t)sc->sc_state->pll * 1000 / 5);
879
880 cfid = PN8_STA_CFID(status);
881 COUNT_OFF_IRT(sc->sc_state->irt);
882 }
883
884 /*
885 * Phase 3: change to requested voltage.
886 */
887 if (cvid != vid) {
888 status = powernow_k8_fidvid(cfid, vid, 1ULL);
889 cvid = PN8_STA_CVID(status);
890 COUNT_OFF_VST(sc->sc_state->vst);
891 }
892
893 #if 0
894 if (cfid == fid || cvid == vid)
895 freq = sc->sc_state->state_table[i].freq;
896 #endif
897
898 return 0;
899 }
900
901 static uint64_t
powernow_k8_fidvid(u_int fid,uint64_t vid,uint64_t ctrl)902 powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl)
903 {
904 struct msr_rw_info msr;
905 uint64_t status, xc;
906
907 msr.msr_read = false;
908 msr.msr_value = (ctrl << 32) | (1ULL << 16) | (vid << 8) | fid;
909 msr.msr_type = MSR_AMDK7_FIDVID_CTL;
910
911 xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL);
912 xc_wait(xc);
913
914 do {
915 status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
916
917 } while (PN8_STA_PENDING(status));
918
919 return status;
920 }
921
922 /*
923 * Given a set of pair of FID/VID, and a number of performance
924 * states, compute a state table via an insertion sort.
925 */
926 static int
powernow_k8_decode_pst(struct powernow_cpu_state * cstate,uint8_t * p)927 powernow_k8_decode_pst(struct powernow_cpu_state *cstate, uint8_t *p)
928 {
929 struct powernow_state state;
930 int i, j, n;
931
932 for (n = 0, i = 0; i < cstate->n_states; i++) {
933
934 state.fid = *p++;
935 state.vid = *p++;
936
937 /*
938 * The minimum supported frequency per the data sheet is
939 * 800 MHz. The maximum supported frequency is 5000 MHz.
940 */
941 state.freq = 800 + state.fid * 100;
942 j = n;
943
944 while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
945
946 (void)memcpy(&cstate->state_table[j],
947 &cstate->state_table[j - 1],
948 sizeof(struct powernow_state));
949
950 --j;
951 }
952
953 (void)memcpy(&cstate->state_table[j], &state,
954 sizeof(struct powernow_state));
955
956 n++;
957 }
958
959 return 1;
960 }
961
962 MODULE(MODULE_CLASS_DRIVER, powernow, NULL);
963
964 #ifdef _MODULE
965 #include "ioconf.c"
966 #endif
967
968 static int
powernow_modcmd(modcmd_t cmd,void * aux)969 powernow_modcmd(modcmd_t cmd, void *aux)
970 {
971 int error = 0;
972
973 switch (cmd) {
974 case MODULE_CMD_INIT:
975 #ifdef _MODULE
976 error = config_init_component(cfdriver_ioconf_powernow,
977 cfattach_ioconf_powernow, cfdata_ioconf_powernow);
978 #endif
979 return error;
980 case MODULE_CMD_FINI:
981 #ifdef _MODULE
982 error = config_fini_component(cfdriver_ioconf_powernow,
983 cfattach_ioconf_powernow, cfdata_ioconf_powernow);
984 #endif
985 return error;
986 default:
987 return ENOTTY;
988 }
989 }
990