static int pn_decode_pst(device_t dev) { int maxpst; struct pn_softc *sc; u_int cpuid, maxfid, startvid; u_long sig; struct psb_header *psb; uint8_t *p; u_int regs[4]; uint64_t status; sc = device_get_softc(dev); do_cpuid(0x80000001, regs); cpuid = regs[0]; if ((cpuid & 0xfff) == 0x760) sc->errata |= A0_ERRATA; status = rdmsr(MSR_AMDK7_FIDVID_STATUS); switch (sc->pn_type) { case PN7_TYPE: maxfid = PN7_STA_MFID(status); startvid = PN7_STA_SVID(status); break; case PN8_TYPE: maxfid = PN8_STA_MFID(status); /* * we should actually use a variable named 'maxvid' if K8, * but why introducing a new variable for that? */ startvid = PN8_STA_MVID(status); break; default: return (ENODEV); } if (bootverbose) { device_printf(dev, "STATUS: 0x%jx\n", status); device_printf(dev, "STATUS: maxfid: 0x%02x\n", maxfid); device_printf(dev, "STATUS: %s: 0x%02x\n", sc->pn_type == PN7_TYPE ? "startvid" : "maxvid", startvid); } sig = bios_sigsearch(PSB_START, PSB_SIG, PSB_LEN, PSB_STEP, PSB_OFF); if (sig) { struct pst_header *pst; psb = (struct psb_header*)(uintptr_t)BIOS_PADDRTOVADDR(sig); switch (psb->version) { default: return (ENODEV); case 0x14: /* * We can't be picky about numpst since at least * some systems have a value of 1 and some have 2. * We trust that cpuid_is_k7() will be better at * catching that we're on a K8 anyway. */ if (sc->pn_type != PN8_TYPE) return (EINVAL); sc->vst = psb->settlingtime; sc->rvo = PN8_PSB_TO_RVO(psb->res1), sc->irt = PN8_PSB_TO_IRT(psb->res1), sc->mvs = PN8_PSB_TO_MVS(psb->res1), sc->low = PN8_PSB_TO_BATT(psb->res1); if (bootverbose) { device_printf(dev, "PSB: VST: %d\n", psb->settlingtime); device_printf(dev, "PSB: RVO %x IRT %d " "MVS %d BATT %d\n", sc->rvo, sc->irt, sc->mvs, sc->low); } break; case 0x12: if (sc->pn_type != PN7_TYPE) return (EINVAL); sc->sgtc = psb->settlingtime * sc->fsb; if (sc->sgtc < 100 * sc->fsb) sc->sgtc = 100 * sc->fsb; break; } p = ((uint8_t *) psb) + sizeof(struct psb_header); pst = (struct pst_header*) p; maxpst = 200; do { struct pst_header *pst = (struct pst_header*) p; if (cpuid == pst->cpuid && maxfid == pst->maxfid && startvid == pst->startvid) { sc->powernow_max_states = pst->numpstates; switch (sc->pn_type) { case PN7_TYPE: if (abs(sc->fsb - pst->fsb) > 5) continue; break; case PN8_TYPE: break; } return (decode_pst(sc, p + sizeof(struct pst_header), sc->powernow_max_states)); } p += sizeof(struct pst_header) + (2 * pst->numpstates); } while (cpuid_is_k7(pst->cpuid) && maxpst--); device_printf(dev, "no match for extended cpuid %.3x\n", cpuid); } return (ENODEV); }
void k7_powernow_init(void) { u_int regs[4]; uint64_t status; u_int maxfid, startvid, currentfid; struct k7pnow_cpu_state *cstate; struct k7pnow_state *state; struct cpu_info *ci; char *techname = NULL; int i; if (setperf_prio > 1) return; ci = curcpu(); cpuid(0x80000000, regs); if (regs[0] < 0x80000007) return; cpuid(0x80000007, regs); if (!(regs[3] & AMD_PN_FID_VID)) return; /* Extended CPUID signature value */ cpuid(0x80000001, regs); cstate = malloc(sizeof(struct k7pnow_cpu_state), M_DEVBUF, M_NOWAIT); if (!cstate) return; cstate->flags = cstate->n_states = 0; if (ci->ci_signature == AMD_ERRATA_A0_CPUSIG) cstate->flags |= PN7_FLAG_ERRATA_A0; status = rdmsr(MSR_AMDK7_FIDVID_STATUS); maxfid = PN7_STA_MFID(status); startvid = PN7_STA_SVID(status); currentfid = PN7_STA_CFID(status); cstate->fsb = cpuspeed / (k7pnow_fid_to_mult[currentfid]/10); if (!k7pnow_states(cstate, ci->ci_signature, maxfid, startvid)) if (!k7pnow_states(cstate, regs[0], maxfid, startvid)) { #if NACPICPU > 0 /* If we have it try ACPI */ k7pnow_acpi_init(cstate, status); #endif } if (cstate->n_states) { if (cstate->flags & PN7_FLAG_DESKTOP_VRM) techname = "Cool'n'Quiet K7"; else techname = "PowerNow! K7"; printf("%s: %s %d MHz: speeds:", ci->ci_dev.dv_xname, techname, cpuspeed); for (i = cstate->n_states; i > 0; i--) { state = &cstate->state_table[i-1]; printf(" %d", state->freq); } printf(" MHz\n"); k7pnow_current_state = cstate; cpu_setperf = k7_powernow_setperf; setperf_prio = 1; return; } free(cstate, M_DEVBUF, sizeof(*cstate)); }