Annotation of sys/arch/i386/i386/powernow-k7.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: powernow-k7.c,v 1.32 2007/07/27 03:03:37 gwk Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2004 Martin Végiard.
! 5: * Copyright (c) 2004-2005 Bruno Ducrot
! 6: * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp>
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: *
! 17: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 18: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 19: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 20: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
! 21: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 22: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 23: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 24: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 25: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 26: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 27: */
! 28:
! 29: /* AMD POWERNOW K7 driver */
! 30:
! 31: #include <sys/types.h>
! 32: #include <sys/param.h>
! 33: #include <sys/systm.h>
! 34: #include <sys/malloc.h>
! 35: #include <sys/sysctl.h>
! 36:
! 37: #include <machine/cpu.h>
! 38: #include <machine/cpufunc.h>
! 39: #include <machine/bus.h>
! 40:
! 41: #include <dev/isa/isareg.h>
! 42: #include <i386/isa/isa_machdep.h>
! 43:
! 44: #include "acpicpu.h"
! 45:
! 46: #if NACPICPU > 0
! 47: #include <dev/acpi/acpidev.h>
! 48: #include <dev/acpi/acpivar.h>
! 49: #endif
! 50:
! 51: #define BIOS_START 0xe0000
! 52: #define BIOS_LEN 0x20000
! 53: #define BIOS_STEP 16
! 54:
! 55: /*
! 56: * MSRs and bits used by PowerNow! technology
! 57: */
! 58: #define MSR_AMDK7_FIDVID_CTL 0xc0010041
! 59: #define MSR_AMDK7_FIDVID_STATUS 0xc0010042
! 60: #define AMD_PN_FID_VID 0x06
! 61: #define AMD_ERRATA_A0_CPUSIG 0x660
! 62:
! 63: #define PN7_FLAG_ERRATA_A0 0x01
! 64: #define PN7_FLAG_DESKTOP_VRM 0x02
! 65:
! 66: /* Bitfields used by K7 */
! 67: #define PN7_PSB_VERSION 0x12
! 68: #define PN7_CTR_FID(x) ((x) & 0x1f)
! 69: #define PN7_CTR_VID(x) (((x) & 0x1f) << 8)
! 70: #define PN7_CTR_FIDC 0x00010000
! 71: #define PN7_CTR_VIDC 0x00020000
! 72: #define PN7_CTR_FIDCHRATIO 0x00100000
! 73: #define PN7_CTR_SGTC(x) (((uint64_t)(x) & 0x000fffff) << 32)
! 74:
! 75: #define PN7_STA_CFID(x) ((x) & 0x1f)
! 76: #define PN7_STA_SFID(x) (((x) >> 8) & 0x1f)
! 77: #define PN7_STA_MFID(x) (((x) >> 16) & 0x1f)
! 78: #define PN7_STA_CVID(x) (((x) >> 32) & 0x1f)
! 79: #define PN7_STA_SVID(x) (((x) >> 40) & 0x1f)
! 80: #define PN7_STA_MVID(x) (((x) >> 48) & 0x1f)
! 81:
! 82: /*
! 83: * ACPI ctr_val status register to powernow k7 configuration
! 84: */
! 85: #define PN7_ACPI_CTRL_TO_FID(x) ((x) & 0x1f)
! 86: #define PN7_ACPI_CTRL_TO_VID(x) (((x) >> 5) & 0x1f)
! 87: #define PN7_ACPI_CTRL_TO_SGTC(x) (((x) >> 10) & 0xffff)
! 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: * Divide each value by 10 to get the processor multiplier.
! 95: * Taken from powernow-k7.c/Linux by Dave Jones
! 96: */
! 97: static int k7pnow_fid_to_mult[32] = {
! 98: 110, 115, 120, 125, 50, 55, 60, 65,
! 99: 70, 75, 80, 85, 90, 95, 100, 105,
! 100: 30, 190, 40, 200, 130, 135, 140, 210,
! 101: 150, 225, 160, 165, 170, 180, -1, -1
! 102: };
! 103:
! 104: #define POWERNOW_MAX_STATES 16
! 105:
! 106: struct k7pnow_state {
! 107: int freq;
! 108: int fid;
! 109: int vid;
! 110: };
! 111:
! 112: struct k7pnow_cpu_state {
! 113: unsigned int fsb;
! 114: unsigned int sgtc;
! 115: struct k7pnow_state state_table[POWERNOW_MAX_STATES];
! 116: unsigned int n_states;
! 117: int flags;
! 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 signature;
! 131: uint8_t fsb; /* Front Side Bus frequency (MHz) */
! 132: uint8_t fid; /* Max Frequency code */
! 133: uint8_t vid; /* Max Voltage code */
! 134: uint8_t n_states; /* Number of states */
! 135: };
! 136:
! 137: struct k7pnow_cpu_state *k7pnow_current_state;
! 138: extern int setperf_prio;
! 139:
! 140: int k7pnow_decode_pst(struct k7pnow_cpu_state *, uint8_t *, int);
! 141: int k7pnow_states(struct k7pnow_cpu_state *, uint32_t, unsigned int,
! 142: unsigned int);
! 143:
! 144: #if NACPICPU > 0
! 145: int k7pnow_acpi_init(struct k7pnow_cpu_state * cstate, uint64_t status);
! 146: int k7pnow_acpi_states(struct k7pnow_cpu_state * cstate,
! 147: struct acpicpu_pss *pss, int nstates, uint64_t status);
! 148: void k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss);
! 149: #endif
! 150:
! 151: void
! 152: k7_powernow_setperf(int level)
! 153: {
! 154: unsigned int i;
! 155: int cvid, cfid, vid = 0, fid = 0;
! 156: uint64_t status, ctl;
! 157: struct k7pnow_cpu_state * cstate;
! 158:
! 159: cstate = k7pnow_current_state;
! 160:
! 161: i = ((level * cstate->n_states) + 1) / 101;
! 162: if (i >= cstate->n_states)
! 163: i = cstate->n_states - 1;
! 164: fid = cstate->state_table[i].fid;
! 165: vid = cstate->state_table[i].vid;
! 166:
! 167: if (fid == 0 || vid == 0)
! 168: return;
! 169:
! 170: status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
! 171: cfid = PN7_STA_CFID(status);
! 172: cvid = PN7_STA_CVID(status);
! 173:
! 174: /*
! 175: * We're already at the requested level.
! 176: */
! 177: if (fid == cfid && vid == cvid)
! 178: return;
! 179:
! 180: ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO;
! 181:
! 182: ctl |= PN7_CTR_FID(fid);
! 183: ctl |= PN7_CTR_VID(vid);
! 184: ctl |= PN7_CTR_SGTC(cstate->sgtc);
! 185:
! 186: if (cstate->flags & PN7_FLAG_ERRATA_A0)
! 187: disable_intr();
! 188:
! 189: if (k7pnow_fid_to_mult[fid] < k7pnow_fid_to_mult[cfid]) {
! 190: wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
! 191: if (vid != cvid)
! 192: wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
! 193: } else {
! 194: wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC);
! 195: if (fid != cfid)
! 196: wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC);
! 197: }
! 198:
! 199: if (cstate->flags & PN7_FLAG_ERRATA_A0)
! 200: enable_intr();
! 201:
! 202: status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
! 203: cfid = PN7_STA_CFID(status);
! 204: cvid = PN7_STA_CVID(status);
! 205: if (cfid == fid || cvid == vid)
! 206: cpuspeed = cstate->state_table[i].freq;
! 207: }
! 208:
! 209: /*
! 210: * Given a set of pair of fid/vid, and number of performance states,
! 211: * compute state_table via an insertion sort.
! 212: */
! 213: int
! 214: k7pnow_decode_pst(struct k7pnow_cpu_state * cstate, uint8_t *p, int npst)
! 215: {
! 216: int i, j, n;
! 217: struct k7pnow_state state;
! 218:
! 219: for (n = 0, i = 0; i < npst; ++i) {
! 220: state.fid = *p++;
! 221: state.vid = *p++;
! 222: state.freq = k7pnow_fid_to_mult[state.fid]/10 * cstate->fsb;
! 223: if ((cstate->flags & PN7_FLAG_ERRATA_A0) &&
! 224: (k7pnow_fid_to_mult[state.fid] % 10) == 5)
! 225: continue;
! 226:
! 227: j = n;
! 228: while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
! 229: memcpy(&cstate->state_table[j],
! 230: &cstate->state_table[j - 1],
! 231: sizeof(struct k7pnow_state));
! 232: --j;
! 233: }
! 234: memcpy(&cstate->state_table[j], &state,
! 235: sizeof(struct k7pnow_state));
! 236: ++n;
! 237: }
! 238: /*
! 239: * Fix powernow_max_states, if errata_a0 give us less states
! 240: * than expected.
! 241: */
! 242: cstate->n_states = n;
! 243: return 1;
! 244: }
! 245:
! 246: int
! 247: k7pnow_states(struct k7pnow_cpu_state *cstate, uint32_t cpusig,
! 248: unsigned int fid, unsigned int vid)
! 249: {
! 250: int maxpst;
! 251: struct psb_s *psb;
! 252: struct pst_s *pst;
! 253: uint8_t *p;
! 254:
! 255: /*
! 256: * Look in the 0xe0000 - 0x100000 physical address
! 257: * range for the pst tables; 16 byte blocks
! 258: */
! 259: for (p = (u_int8_t *)ISA_HOLE_VADDR(BIOS_START);
! 260: p < (u_int8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p+=
! 261: BIOS_STEP) {
! 262: if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
! 263: psb = (struct psb_s *)p;
! 264: if (psb->version != PN7_PSB_VERSION)
! 265: return 0;
! 266:
! 267: cstate->sgtc = psb->ttime * cstate->fsb;
! 268: if (cstate->sgtc < 100 * cstate->fsb)
! 269: cstate->sgtc = 100 * cstate->fsb;
! 270: if (psb->flags & 1)
! 271: cstate->flags |= PN7_FLAG_DESKTOP_VRM;
! 272: p += sizeof(struct psb_s);
! 273:
! 274: for (maxpst = 0; maxpst < psb->n_pst; maxpst++) {
! 275: pst = (struct pst_s*) p;
! 276:
! 277: if (cpusig == pst->signature && fid == pst->fid
! 278: && vid == pst->vid) {
! 279:
! 280: if (abs(cstate->fsb - pst->fsb) > 5)
! 281: continue;
! 282: cstate->n_states = pst->n_states;
! 283: return (k7pnow_decode_pst(cstate,
! 284: p + sizeof(struct pst_s),
! 285: cstate->n_states));
! 286: }
! 287: p += sizeof(struct pst_s) +
! 288: (2 * pst->n_states);
! 289: }
! 290: }
! 291: }
! 292:
! 293: return 0;
! 294: }
! 295:
! 296: #if NACPICPU > 0
! 297:
! 298: int
! 299: k7pnow_acpi_states(struct k7pnow_cpu_state * cstate, struct acpicpu_pss *pss,
! 300: int nstates, uint64_t status)
! 301: {
! 302: struct k7pnow_state state;
! 303: int j, k, n;
! 304: uint32_t ctrl;
! 305:
! 306: k = -1;
! 307: for (n = 0; n < cstate->n_states; n++) {
! 308: if (status == pss[n].pss_status)
! 309: k = n;
! 310: ctrl = pss[n].pss_ctrl;
! 311: state.fid = PN7_ACPI_CTRL_TO_FID(ctrl);
! 312: state.vid = PN7_ACPI_CTRL_TO_VID(ctrl);
! 313:
! 314: state.freq = pss[n].pss_core_freq;
! 315: j = n;
! 316: while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
! 317: memcpy(&cstate->state_table[j],
! 318: &cstate->state_table[j - 1],
! 319: sizeof(struct k7pnow_state));
! 320: --j;
! 321: }
! 322: memcpy(&cstate->state_table[j], &state,
! 323: sizeof(struct k7pnow_state));
! 324: }
! 325: return k;
! 326: }
! 327:
! 328: void
! 329: k7pnow_acpi_pss_changed(struct acpicpu_pss *pss, int npss)
! 330: {
! 331: int curs;
! 332: struct k7pnow_cpu_state *cstate;
! 333: uint32_t ctrl;
! 334: uint64_t status;
! 335:
! 336: status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
! 337: cstate = k7pnow_current_state;
! 338:
! 339: curs = k7pnow_acpi_states(cstate, pss, npss, status);
! 340: ctrl = pss[curs].pss_ctrl;
! 341: cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
! 342: cstate->n_states = npss;
! 343: }
! 344:
! 345: int
! 346: k7pnow_acpi_init(struct k7pnow_cpu_state *cstate, uint64_t status)
! 347: {
! 348: int curs;
! 349: uint32_t ctrl;
! 350: struct acpicpu_pss *pss;
! 351: int mfid;
! 352:
! 353: cstate->n_states = acpicpu_fetch_pss(&pss);
! 354: if (cstate->n_states == 0)
! 355: return 0;
! 356:
! 357: /*
! 358: * XXX: Some BIOS supplied _PSS implementations have the wrong
! 359: * maximum frequency, if we encounter one of these punt and
! 360: * hope the legacy tables have correct values.
! 361: */
! 362: mfid = PN7_STA_MFID(status);
! 363: if (mfid != cstate->state_table[cstate->n_states - 1].fid) {
! 364: return 0;
! 365: }
! 366: curs = k7pnow_acpi_states(cstate, pss, cstate->n_states, status);
! 367:
! 368: acpicpu_set_notify(k7pnow_acpi_pss_changed);
! 369: ctrl = pss[curs].pss_ctrl;
! 370: cstate->sgtc = PN7_ACPI_CTRL_TO_SGTC(ctrl);
! 371:
! 372: return 1;
! 373: }
! 374:
! 375: #endif /* NACPICPU */
! 376:
! 377: void
! 378: k7_powernow_init(void)
! 379: {
! 380: u_int regs[4];
! 381: uint64_t status;
! 382: u_int maxfid, startvid, currentfid;
! 383: struct k7pnow_cpu_state *cstate;
! 384: struct k7pnow_state *state;
! 385: struct cpu_info *ci;
! 386: char *techname = NULL;
! 387: int i;
! 388:
! 389: if (setperf_prio > 1)
! 390: return;
! 391:
! 392: ci = curcpu();
! 393:
! 394: cpuid(0x80000000, regs);
! 395: if (regs[0] < 0x80000007)
! 396: return;
! 397:
! 398: cpuid(0x80000007, regs);
! 399: if (!(regs[3] & AMD_PN_FID_VID))
! 400: return;
! 401:
! 402: /* Extended CPUID signature value */
! 403: cpuid(0x80000001, regs);
! 404:
! 405: cstate = malloc(sizeof(struct k7pnow_cpu_state), M_DEVBUF, M_NOWAIT);
! 406: if (!cstate)
! 407: return;
! 408:
! 409: cstate->flags = cstate->n_states = 0;
! 410: if (ci->ci_signature == AMD_ERRATA_A0_CPUSIG)
! 411: cstate->flags |= PN7_FLAG_ERRATA_A0;
! 412:
! 413: status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
! 414: maxfid = PN7_STA_MFID(status);
! 415: startvid = PN7_STA_SVID(status);
! 416: currentfid = PN7_STA_CFID(status);
! 417:
! 418: cstate->fsb = cpuspeed / (k7pnow_fid_to_mult[currentfid]/10);
! 419:
! 420: #if NACPICPU > 0
! 421: /* If we have it try ACPI */
! 422: if (!k7pnow_acpi_init(cstate, status))
! 423: #endif
! 424: {
! 425: /* if the base CPUID signature fails to match try, the extended one */
! 426: if (!k7pnow_states(cstate, ci->ci_signature, maxfid, startvid))
! 427: k7pnow_states(cstate, regs[0], maxfid, startvid);
! 428: }
! 429:
! 430: if (cstate->n_states) {
! 431: if (cstate->flags & PN7_FLAG_DESKTOP_VRM)
! 432: techname = "Cool'n'Quiet K7";
! 433: else
! 434: techname = "PowerNow! K7";
! 435: printf("%s: %s %d MHz: speeds:",
! 436: ci->ci_dev.dv_xname, techname, cpuspeed);
! 437: for (i = cstate->n_states; i > 0; i--) {
! 438: state = &cstate->state_table[i-1];
! 439: printf(" %d", state->freq);
! 440: }
! 441: printf(" MHz\n");
! 442:
! 443: k7pnow_current_state = cstate;
! 444: cpu_setperf = k7_powernow_setperf;
! 445: setperf_prio = 1;
! 446: return;
! 447: }
! 448: free(cstate, M_DEVBUF);
! 449: }
CVSweb