Annotation of sys/arch/amd64/amd64/powernow-k8.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: powernow-k8.c,v 1.19 2007/05/31 17:49:15 gwk Exp $ */
2: /*
3: * Copyright (c) 2004 Martin Végiard.
4: * Copyright (c) 2004-2005 Bruno Ducrot
5: * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26: */
27: /* AMD POWERNOW K8 driver */
28:
29: #include <sys/types.h>
30: #include <sys/param.h>
31: #include <sys/systm.h>
32: #include <sys/malloc.h>
33: #include <sys/sysctl.h>
34:
35: #include <dev/isa/isareg.h>
36: #include <amd64/include/isa_machdep.h>
37:
38: #include <machine/cpu.h>
39: #include <machine/cpufunc.h>
40: #include <machine/bus.h>
41:
42: #include "acpicpu.h"
43:
44: #if NACPICPU > 0
45: #include <dev/acpi/acpidev.h>
46: #include <dev/acpi/acpivar.h>
47: #endif
48:
49: #define BIOS_START 0xe0000
50: #define BIOS_LEN 0x20000
51:
52: extern int setperf_prio;
53:
54: /*
55: * MSRs and bits used by PowerNow technology
56: */
57: #define MSR_AMDK7_FIDVID_CTL 0xc0010041
58: #define MSR_AMDK7_FIDVID_STATUS 0xc0010042
59:
60: /* Bitfields used by K8 */
61:
62: #define PN8_CTR_FID(x) ((x) & 0x3f)
63: #define PN8_CTR_VID(x) (((x) & 0x1f) << 8)
64: #define PN8_CTR_PENDING(x) (((x) & 1) << 32)
65:
66: #define PN8_STA_CFID(x) ((x) & 0x3f)
67: #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f)
68: #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f)
69: #define PN8_STA_PENDING(x) (((x) >> 31) & 0x01)
70: #define PN8_STA_CVID(x) (((x) >> 32) & 0x1f)
71: #define PN8_STA_SVID(x) (((x) >> 40) & 0x1f)
72: #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f)
73:
74: /* Reserved1 to PowerNow K8 configuration */
75: #define PN8_PSB_TO_RVO(x) ((x) & 0x03)
76: #define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03)
77: #define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03)
78: #define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03)
79:
80: /* ACPI ctr_val status register to PowerNow K8 configuration */
81: #define PN8_ACPI_CTRL_TO_FID(x) ((x) & 0x3f)
82: #define PN8_ACPI_CTRL_TO_VID(x) (((x) >> 6) & 0x1f)
83: #define PN8_ACPI_CTRL_TO_VST(x) (((x) >> 11) & 0x1f)
84: #define PN8_ACPI_CTRL_TO_MVS(x) (((x) >> 18) & 0x03)
85: #define PN8_ACPI_CTRL_TO_PLL(x) (((x) >> 20) & 0x7f)
86: #define PN8_ACPI_CTRL_TO_RVO(x) (((x) >> 28) & 0x03)
87: #define PN8_ACPI_CTRL_TO_IRT(x) (((x) >> 30) & 0x03)
88:
89: #define WRITE_FIDVID(fid, vid, ctrl) \
90: wrmsr(MSR_AMDK7_FIDVID_CTL, \
91: (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
92:
93:
94: #define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt)))
95: #define COUNT_OFF_VST(vst) DELAY(20 * (vst))
96:
97: #define FID_TO_VCO_FID(fid) \
98: (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
99:
100: #define POWERNOW_MAX_STATES 16
101:
102: struct k8pnow_state {
103: int freq;
104: uint8_t fid;
105: uint8_t vid;
106: };
107:
108: struct k8pnow_cpu_state {
109: struct k8pnow_state state_table[POWERNOW_MAX_STATES];
110: unsigned int n_states;
111: unsigned int sgtc;
112: unsigned int vst;
113: unsigned int mvs;
114: unsigned int pll;
115: unsigned int rvo;
116: unsigned int irt;
117: int low;
118: };
119:
120: struct psb_s {
121: char signature[10]; /* AMDK7PNOW! */
122: uint8_t version;
123: uint8_t flags;
124: uint16_t ttime; /* Min Settling time */
125: uint8_t reserved;
126: uint8_t n_pst;
127: };
128:
129: struct pst_s {
130: uint32_t cpuid;
131: uint8_t pll;
132: uint8_t fid;
133: uint8_t vid;
134: uint8_t n_states;
135: };
136:
137: struct k8pnow_cpu_state *k8pnow_current_state;
138:
139: int k8pnow_read_pending_wait(uint64_t *);
140: int k8pnow_decode_pst(struct k8pnow_cpu_state *, uint8_t *);
141: int k8pnow_states(struct k8pnow_cpu_state *, uint32_t, unsigned int,
142: unsigned int);
143:
144: #if NACPICPU > 0
145: int k8pnow_acpi_init(struct k8pnow_cpu_state *, uint64_t);
146: void k8pnow_acpi_pss_changed(struct acpicpu_pss *, int);
147: int k8pnow_acpi_states(struct k8pnow_cpu_state *, struct acpicpu_pss *, int,
148: uint64_t);
149: #endif
150:
151: int
152: k8pnow_read_pending_wait(uint64_t *status)
153: {
154: unsigned int i = 100000;
155:
156: while (i--) {
157: *status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
158: if (!PN8_STA_PENDING(*status))
159: return 0;
160:
161: }
162: printf("k8pnow_read_pending_wait: change pending stuck.\n");
163: return 1;
164: }
165:
166: void
167: k8_powernow_setperf(int level)
168: {
169: unsigned int i;
170: uint64_t status;
171: int cfid, cvid, fid = 0, vid = 0;
172: int rvo;
173: u_int val;
174: struct k8pnow_cpu_state *cstate;
175:
176: /*
177: * We dont do a k8pnow_read_pending_wait here, need to ensure that the
178: * change pending bit isn't stuck,
179: */
180: status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
181: if (PN8_STA_PENDING(status))
182: return;
183: cfid = PN8_STA_CFID(status);
184: cvid = PN8_STA_CVID(status);
185:
186: cstate = k8pnow_current_state;
187:
188: i = ((level * cstate->n_states) + 1) / 101;
189: if (i >= cstate->n_states)
190: i = cstate->n_states - 1;
191:
192: fid = cstate->state_table[i].fid;
193: vid = cstate->state_table[i].vid;
194:
195: if (fid == cfid && vid == cvid)
196: return;
197:
198: /*
199: * Phase 1: Raise core voltage to requested VID if frequency is
200: * going up.
201: */
202: while (cvid > vid) {
203: val = cvid - (1 << cstate->mvs);
204: WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
205: if (k8pnow_read_pending_wait(&status))
206: return;
207: cvid = PN8_STA_CVID(status);
208: COUNT_OFF_VST(cstate->vst);
209: }
210:
211: /* ... then raise to voltage + RVO (if required) */
212: for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
213: /* XXX It's not clear from spec if we have to do that
214: * in 0.25 step or in MVS. Therefore do it as it's done
215: * under Linux */
216: WRITE_FIDVID(cfid, cvid - 1, 1ULL);
217: if (k8pnow_read_pending_wait(&status))
218: return;
219: cvid = PN8_STA_CVID(status);
220: COUNT_OFF_VST(cstate->vst);
221: }
222:
223: /* Phase 2: change to requested core frequency */
224: if (cfid != fid) {
225: u_int vco_fid, vco_cfid;
226:
227: vco_fid = FID_TO_VCO_FID(fid);
228: vco_cfid = FID_TO_VCO_FID(cfid);
229:
230: while (abs(vco_fid - vco_cfid) > 2) {
231: if (fid > cfid) {
232: if (cfid > 6)
233: val = cfid + 2;
234: else
235: val = FID_TO_VCO_FID(cfid) + 2;
236: } else
237: val = cfid - 2;
238: WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5);
239:
240: if (k8pnow_read_pending_wait(&status))
241: return;
242: cfid = PN8_STA_CFID(status);
243: COUNT_OFF_IRT(cstate->irt);
244:
245: vco_cfid = FID_TO_VCO_FID(cfid);
246: }
247:
248: WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
249: if (k8pnow_read_pending_wait(&status))
250: return;
251: cfid = PN8_STA_CFID(status);
252: COUNT_OFF_IRT(cstate->irt);
253: }
254:
255: /* Phase 3: change to requested voltage */
256: if (cvid != vid) {
257: WRITE_FIDVID(cfid, vid, 1ULL);
258: if (k8pnow_read_pending_wait(&status))
259: return;
260: cvid = PN8_STA_CVID(status);
261: COUNT_OFF_VST(cstate->vst);
262: }
263:
264: if (cfid == fid || cvid == vid)
265: cpuspeed = cstate->state_table[i].freq;
266: }
267:
268: /*
269: * Given a set of pair of fid/vid, and number of performance states,
270: * compute state_table via an insertion sort.
271: */
272: int
273: k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t *p)
274: {
275: int i, j, n;
276: struct k8pnow_state state;
277: for (n = 0, i = 0; i < cstate->n_states; i++) {
278: state.fid = *p++;
279: state.vid = *p++;
280:
281: /*
282: * The minimum supported frequency per the data sheet is 800MHz
283: * The maximum supported frequency is 5000MHz.
284: */
285: state.freq = 800 + state.fid * 100;
286: j = n;
287: while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
288: memcpy(&cstate->state_table[j],
289: &cstate->state_table[j - 1],
290: sizeof(struct k8pnow_state));
291: --j;
292: }
293: memcpy(&cstate->state_table[j], &state,
294: sizeof(struct k8pnow_state));
295: n++;
296: }
297: return 1;
298: }
299:
300: #if NACPICPU > 0
301:
302: int
303: k8pnow_acpi_states(struct k8pnow_cpu_state * cstate, struct acpicpu_pss * pss,
304: int nstates, uint64_t status)
305: {
306: struct k8pnow_state state;
307: int j, k, n;
308: uint32_t ctrl;
309:
310: k = -1;
311:
312: for (n = 0; n < cstate->n_states; n++) {
313: if (status == pss[n].pss_status)
314: k = n;
315: ctrl = pss[n].pss_ctrl;
316: state.fid = PN8_ACPI_CTRL_TO_FID(ctrl);
317: state.vid = PN8_ACPI_CTRL_TO_VID(ctrl);
318:
319: state.freq = pss[n].pss_core_freq;
320: j = n;
321: while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
322: memcpy(&cstate->state_table[j],
323: &cstate->state_table[j - 1],
324: sizeof(struct k8pnow_state));
325: --j;
326: }
327: memcpy(&cstate->state_table[j], &state,
328: sizeof(struct k8pnow_state));
329: }
330:
331: return k;
332: }
333:
334: void
335: k8pnow_acpi_pss_changed(struct acpicpu_pss * pss, int npss)
336: {
337: int curs;
338: struct k8pnow_cpu_state * cstate;
339: uint32_t ctrl;
340: uint64_t status;
341:
342: status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
343: cstate = k8pnow_current_state;
344:
345: curs = k8pnow_acpi_states(cstate, pss, npss, status);
346: ctrl = pss[curs].pss_ctrl;
347: cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
348: cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
349: cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
350: cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
351: cstate->low = 0;
352: cstate->n_states = npss;
353: }
354:
355: int
356: k8pnow_acpi_init(struct k8pnow_cpu_state * cstate, uint64_t status)
357: {
358: int curs;
359: uint32_t ctrl;
360: struct acpicpu_pss *pss;
361:
362: cstate->n_states = acpicpu_fetch_pss(&pss);
363: if (cstate->n_states == 0)
364: return 0;
365: acpicpu_set_notify(k8pnow_acpi_pss_changed);
366:
367: curs = k8pnow_acpi_states(cstate, pss, cstate->n_states, status);
368: ctrl = pss[curs].pss_ctrl;
369:
370: cstate->vst = PN8_ACPI_CTRL_TO_VST(ctrl);
371: cstate->mvs = PN8_ACPI_CTRL_TO_MVS(ctrl);
372: cstate->pll = PN8_ACPI_CTRL_TO_PLL(ctrl);
373: cstate->irt = PN8_ACPI_CTRL_TO_IRT(ctrl);
374: cstate->low = 0;
375:
376: return 1;
377: }
378:
379: #endif /* NACPICPU */
380:
381: int
382: k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
383: unsigned int fid, unsigned int vid)
384: {
385: struct psb_s *psb;
386: struct pst_s *pst;
387: uint8_t *p;
388: int i;
389:
390: for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
391: p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p += 16) {
392: if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
393: psb = (struct psb_s *)p;
394: if (psb->version != 0x14)
395: return 0;
396:
397: cstate->vst = psb->ttime;
398: cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
399: cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
400: cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
401: cstate->low = PN8_PSB_TO_BATT(psb->reserved);
402: p+= sizeof(struct psb_s);
403:
404: for(i = 0; i < psb->n_pst; ++i) {
405: pst = (struct pst_s *) p;
406:
407: cstate->pll = pst->pll;
408: cstate->n_states = pst->n_states;
409: if (cpusig == pst->cpuid &&
410: pst->fid == fid && pst->vid == vid) {
411: return (k8pnow_decode_pst(cstate,
412: p+= sizeof (struct pst_s)));
413: }
414: p += sizeof(struct pst_s) + 2
415: * cstate->n_states;
416: }
417: }
418: }
419:
420: return 0;
421:
422: }
423:
424: void
425: k8_powernow_init(struct cpu_info *ci)
426: {
427: uint64_t status;
428: u_int maxfid, maxvid, i;
429: u_int32_t extcpuid, dummy;
430: struct k8pnow_cpu_state *cstate;
431: struct k8pnow_state *state;
432: char * techname = NULL;
433:
434: if (setperf_prio > 1)
435: return;
436:
437: cstate = malloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_NOWAIT);
438: if (!cstate)
439: return;
440:
441: cstate->n_states = 0;
442: status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
443: maxfid = PN8_STA_MFID(status);
444: maxvid = PN8_STA_MVID(status);
445:
446: /*
447: * If start FID is different to max FID, then it is a
448: * mobile processor. If not, it is a low powered desktop
449: * processor.
450: */
451: if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
452: techname = "PowerNow! K8";
453: else
454: techname = "Cool'n'Quiet K8";
455:
456: #if NACPICPU > 0
457: /* If we have acpi check acpi first */
458: if (!k8pnow_acpi_init(cstate, status))
459: #endif
460: {
461: if (!k8pnow_states(cstate, ci->ci_signature, maxfid, maxvid)) {
462: /* Extended CPUID signature value */
463: CPUID(0x80000001, extcpuid, dummy, dummy, dummy);
464: k8pnow_states(cstate, extcpuid, maxfid, maxvid);
465: }
466: }
467: if (cstate->n_states) {
468: printf("%s: %s %d MHz: speeds:",
469: ci->ci_dev->dv_xname, techname, cpuspeed);
470: for (i = cstate->n_states; i > 0; i--) {
471: state = &cstate->state_table[i-1];
472: printf(" %d", state->freq);
473: }
474: printf(" MHz\n");
475: k8pnow_current_state = cstate;
476: cpu_setperf = k8_powernow_setperf;
477: setperf_prio = 1;
478: return;
479: }
480: free(cstate, M_DEVBUF);
481: }
CVSweb