void k8pnow_transition(struct k8pnow_cpu_state *cstate, int level) { uint64_t status; int cfid, cvid, fid = 0, vid = 0; int rvo; u_int val; /* * We dont do a k8pnow_read_pending_wait here, need to ensure that the * change pending bit isn't stuck, */ status = rdmsr(MSR_AMDK7_FIDVID_STATUS); if (PN8_STA_PENDING(status)) return; cfid = PN8_STA_CFID(status); cvid = PN8_STA_CVID(status); fid = cstate->state_table[level].fid; vid = cstate->state_table[level].vid; if (fid == cfid && vid == cvid) return; /* * Phase 1: Raise core voltage to requested VID if frequency is * going up. */ while (cvid > vid) { val = cvid - (1 << cstate->mvs); WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL); if (k8pnow_read_pending_wait(&status)) return; cvid = PN8_STA_CVID(status); COUNT_OFF_VST(cstate->vst); } /* ... then raise to voltage + RVO (if required) */ for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) { /* XXX It's not clear from spec if we have to do that * in 0.25 step or in MVS. Therefore do it as it's done * under Linux */ WRITE_FIDVID(cfid, cvid - 1, 1ULL); if (k8pnow_read_pending_wait(&status)) return; cvid = PN8_STA_CVID(status); COUNT_OFF_VST(cstate->vst); } /* Phase 2: change to requested core frequency */ if (cfid != fid) { u_int vco_fid, vco_cfid; vco_fid = FID_TO_VCO_FID(fid); vco_cfid = FID_TO_VCO_FID(cfid); while (abs(vco_fid - vco_cfid) > 2) { if (fid > cfid) { if (cfid > 6) val = cfid + 2; else val = FID_TO_VCO_FID(cfid) + 2; } else val = cfid - 2; WRITE_FIDVID(val, cvid, (uint64_t)cstate->pll * 1000 / 5); if (k8pnow_read_pending_wait(&status)) return; cfid = PN8_STA_CFID(status); COUNT_OFF_IRT(cstate->irt); vco_cfid = FID_TO_VCO_FID(cfid); } WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5); if (k8pnow_read_pending_wait(&status)) return; cfid = PN8_STA_CFID(status); COUNT_OFF_IRT(cstate->irt); } /* Phase 3: change to requested voltage */ if (cvid != vid) { WRITE_FIDVID(cfid, vid, 1ULL); if (k8pnow_read_pending_wait(&status)) return; cvid = PN8_STA_CVID(status); COUNT_OFF_VST(cstate->vst); } if (cfid == fid || cvid == vid) cpuspeed = cstate->state_table[level].freq; }
static int acpicpu_md_pstate_fidvid_set(struct acpicpu_pstate *ps) { const uint64_t ctrl = ps->ps_control; uint32_t cfid, cvid, fid, i, irt; uint32_t pll, vco_cfid, vco_fid; uint32_t val, vid, vst; int rv; rv = acpicpu_md_pstate_fidvid_read(&cfid, &cvid); if (rv != 0) return rv; fid = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_FID); vid = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_VID); irt = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_IRT); vst = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_VST); pll = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_PLL); vst = vst * 20; pll = pll * 1000 / 5; irt = 10 * __BIT(irt); /* * Phase 1. */ while (cvid > vid) { val = 1 << __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_MVS); val = (val > cvid) ? 0 : cvid - val; acpicpu_md_pstate_fidvid_write(cfid, val, 1, vst); rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid); if (rv != 0) return rv; } i = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_RVO); for (; i > 0 && cvid > 0; --i) { acpicpu_md_pstate_fidvid_write(cfid, cvid - 1, 1, vst); rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid); if (rv != 0) return rv; } /* * Phase 2. */ if (cfid != fid) { vco_fid = FID_TO_VCO_FID(fid); vco_cfid = FID_TO_VCO_FID(cfid); while (abs(vco_fid - vco_cfid) > 2) { if (fid <= cfid) val = cfid - 2; else { val = (cfid > 6) ? cfid + 2 : FID_TO_VCO_FID(cfid) + 2; } acpicpu_md_pstate_fidvid_write(val, cvid, pll, irt); rv = acpicpu_md_pstate_fidvid_read(&cfid, NULL); if (rv != 0) return rv; vco_cfid = FID_TO_VCO_FID(cfid); } acpicpu_md_pstate_fidvid_write(fid, cvid, pll, irt); rv = acpicpu_md_pstate_fidvid_read(&cfid, NULL); if (rv != 0) return rv; } /* * Phase 3. */ if (cvid != vid) { acpicpu_md_pstate_fidvid_write(cfid, vid, 1, vst); rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid); if (rv != 0) return rv; } return 0; }
static int pn8_setfidvid(struct pn_softc *sc, int fid, int vid) { uint64_t status; int cfid, cvid; int rvo; int rv; u_int val; rv = pn8_read_pending_wait(&status); if (rv) return (rv); cfid = PN8_STA_CFID(status); cvid = PN8_STA_CVID(status); if (fid == cfid && vid == cvid) return (0); /* * Phase 1: Raise core voltage to requested VID if frequency is * going up. */ while (cvid > vid) { val = cvid - (1 << sc->mvs); rv = pn8_write_fidvid(cfid, (val > 0) ? val : 0, 1ULL, &status); if (rv) { sc->errata |= PENDING_STUCK; return (rv); } cvid = PN8_STA_CVID(status); COUNT_OFF_VST(sc->vst); } /* ... then raise to voltage + RVO (if required) */ for (rvo = sc->rvo; rvo > 0 && cvid > 0; --rvo) { /* XXX It's not clear from spec if we have to do that * in 0.25 step or in MVS. Therefore do it as it's done * under Linux */ rv = pn8_write_fidvid(cfid, cvid - 1, 1ULL, &status); if (rv) { sc->errata |= PENDING_STUCK; return (rv); } cvid = PN8_STA_CVID(status); COUNT_OFF_VST(sc->vst); } /* Phase 2: change to requested core frequency */ if (cfid != fid) { u_int vco_fid, vco_cfid, fid_delta; vco_fid = FID_TO_VCO_FID(fid); vco_cfid = FID_TO_VCO_FID(cfid); while (abs(vco_fid - vco_cfid) > 2) { fid_delta = (vco_cfid & 1) ? 1 : 2; if (fid > cfid) { if (cfid > 7) val = cfid + fid_delta; else val = FID_TO_VCO_FID(cfid) + fid_delta; } else val = cfid - fid_delta; rv = pn8_write_fidvid(val, cvid, sc->pll * (uint64_t) sc->fsb, &status); if (rv) { sc->errata |= PENDING_STUCK; return (rv); } cfid = PN8_STA_CFID(status); COUNT_OFF_IRT(sc->irt); vco_cfid = FID_TO_VCO_FID(cfid); } rv = pn8_write_fidvid(fid, cvid, sc->pll * (uint64_t) sc->fsb, &status); if (rv) { sc->errata |= PENDING_STUCK; return (rv); } cfid = PN8_STA_CFID(status); COUNT_OFF_IRT(sc->irt); } /* Phase 3: change to requested voltage */ if (cvid != vid) { rv = pn8_write_fidvid(cfid, vid, 1ULL, &status); cvid = PN8_STA_CVID(status); COUNT_OFF_VST(sc->vst); } /* Check if transition failed. */ if (cfid != fid || cvid != vid) rv = ENXIO; return (rv); }
static int k8_powernow_setperf(unsigned int freq) { unsigned int i; uint64_t status; uint32_t val; int cfid , cvid, fid = 0, vid = 0; int rvo; struct k8pnow_cpu_state *cstate; /* * We dont do a k8pnow_read_pending_wait here, need to ensure that * the change pending bit isn't stuck, */ status = rdmsr(MSR_AMDK7_FIDVID_STATUS); if (PN8_STA_PENDING(status)) return 1; cfid = PN8_STA_CFID(status); cvid = PN8_STA_CVID(status); cstate = k8pnow_current_state; for (i = 0; i < cstate->n_states; i++) { if (cstate->state_table[i].freq >= freq) { fid = cstate->state_table[i].fid; vid = cstate->state_table[i].vid; break; } } if (fid == cfid && vid == cvid) { cpuspeed = freq; return 0; } /* * Phase 1: Raise core voltage to requested VID if frequency is going * up. */ while (cvid > vid) { val = cvid - (1 << cstate->mvs); WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL); READ_PENDING_WAIT(status); cvid = PN8_STA_CVID(status); COUNT_OFF_VST(cstate->vst); } /* ... then raise to voltage + RVO (if required) */ for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) { /* * XXX It's not clear from spec if we have to do that in 0.25 * step or in MVS. Therefore do it as it's done under Linux */ WRITE_FIDVID(cfid, cvid - 1, 1ULL); READ_PENDING_WAIT(status); cvid = PN8_STA_CVID(status); COUNT_OFF_VST(cstate->vst); } /* Phase 2: change to requested core frequency */ if (cfid != fid) { uint32_t vco_fid, vco_cfid; vco_fid = FID_TO_VCO_FID(fid); vco_cfid = FID_TO_VCO_FID(cfid); while (abs(vco_fid - vco_cfid) > 2) { if (fid > cfid) { if (cfid > 6) val = cfid + 2; else val = FID_TO_VCO_FID(cfid) + 2; } else val = cfid - 2; WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5); READ_PENDING_WAIT(status); cfid = PN8_STA_CFID(status); COUNT_OFF_IRT(cstate->irt); vco_cfid = FID_TO_VCO_FID(cfid); } WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5); READ_PENDING_WAIT(status); cfid = PN8_STA_CFID(status); COUNT_OFF_IRT(cstate->irt); } /* Phase 3: change to requested voltage */ if (cvid != vid) { WRITE_FIDVID(cfid, vid, 1ULL); READ_PENDING_WAIT(status); cvid = PN8_STA_CVID(status); COUNT_OFF_VST(cstate->vst); } if (cfid == fid || cvid == vid) cpuspeed = cstate->state_table[i].freq; return 0; }