# HG changeset patch # User Robert Ransom # Date 1578731009 18000 # Node ID 6fd1dc88184ef0db3b95278fc9e6fe0a987d22f3 # Parent 64f92cdc003b532a34e90fda087c7a59977058b2 Add support for setting any CPU frequency level to amdpstate driver Not finished, but now the 'powersave' command can be undone. Needs to be extended to print out the frequency and other values of the available P-states, and to check and print various other values in the processor's PCI configuration spaces. Reading the effective CPU frequency will take more work, including two additional assembly routines. diff -r 64f92cdc003b -r 6fd1dc88184e sys/src/9/pc/cpufreq.c --- a/sys/src/9/pc/cpufreq.c Thu Jan 09 01:33:32 2020 -0500 +++ b/sys/src/9/pc/cpufreq.c Sat Jan 11 03:23:29 2020 -0500 @@ -6,12 +6,41 @@ #include "io.h" #include "../port/error.h" +/* Relevant hardware documentation for AMD "hardware P-state control": + + AMD document 25481 - CPUID Specification + Fn0000_0006 - EffFreq bit (read effective frequency) - TODO + Fn8000_0007 - HwPstate, VID, and FID bits + + AMD document 31116 - AMD Family 10h BKDG + section 2.4.2.1.3 Core P-state Control (p. 54) + MSR reference MSRC001_0061 to _0063 (p. 427,428) + Note that MSRC001_0070 and _0071 (software low-level COF/VID control) + used on family 0Fh are still present; however, the processor's + "core performance boost" feature will clash with the older + mechanism unless CPB is disabled first. + + AMD document 56255 - AMD Family 17h OSRR (cut-down version of BKDG) + confirms that this P-state mechanism is still in use; + specifies P-state bitfields as 3 bits wide; + MSRC001_0070 and _0071 are no longer documented as COF/VID control, + but not yet used for P-states. + + AMD document 24593 Rev. 3.32 October 2019 - arch. manual vol 2 System Programming + specifies P-state bitfields as 4 bits wide; + MSRC001_0070 and _0071 would fall within the range used by firmware + to provide supported P-state values to the processor. + +*/ + enum { CMpowersave, + CMpstate, }; static Cmdtab amdpstatectlmsg[] = { CMpowersave, "powersave", 1, + CMpstate, "pstate", 2, }; static int @@ -26,44 +55,64 @@ return 0; } -static void +static int amdpstateset(ulong pst) { vlong cmdmsr; long i; Mach *w; + int failed = 0; w = up->wired; cmdmsr = pst & 15; for(i = 0; i < conf.nmach; ++i){ procwired(up, i); sched(); - if(wrmsr(0xC0010062, cmdmsr) < 0) + if(wrmsr(0xC0010062, cmdmsr) < 0){ print("error setting P-state %uld on mach %ld\n", pst, i); + failed = -1; + } } up->wired = w; sched(); + + return failed; } static void amdpstatectl(Cmdbuf *cb) { Cmdtab *ct; + char *p; vlong limmsr; - ulong PstateMaxVal; + ulong pst; ct = lookupcmd(cb, amdpstatectlmsg, nelem(amdpstatectlmsg)); switch(ct->index){ case CMpowersave: if(rdmsr(0xC0010061, &limmsr) < 0){ - cmderror(cb, "MSRC001_0061 unsupported?!"); + cmderror(cb, "MSRC001_0061 (PState limits) unreadable"); return; } - PstateMaxVal = (limmsr >> 4) & 15; - print("PStateMaxVal: %uld\n", PstateMaxVal); + pst = (limmsr >> 4) & 15; + print("PStateMaxVal: %uld\n", pst); - amdpstateset(PstateMaxVal); + if(amdpstateset(pst) < 0) + cmderror(cb, "error setting min-power P-state"); + return; + case CMpstate: + p = cb->f[1]; + if((*p == 'P') || (*p == 'p')) + ++p; + pst = strtoul(p, &p, 10); + if(*p){ + cmderror(cb, "invalid P-state format"); + return; + } + + if(amdpstateset(pst) < 0) + cmderror(cb, "error setting specified P-state"); return; } @@ -71,7 +120,65 @@ } static long -amdpstatectlwrite(Chan *c, void *a, long n, vlong off) +amdpstatectlread(Chan *, void *a, long n, vlong off) +{ + char *p, *s, *e; + ulong offset = off; + int hwcrvalid, limmsrvalid; + ulong cpuidregs[4]; + vlong hwcr, limmsr; + int cpuidCPB; + int CpbDis, PstateMaxVal, CurPstateLimit; + + cpuid(0x80000007, cpuidregs); + hwcrvalid = (rdmsr(0xC0010015, &hwcr) >= 0); + limmsrvalid = (rdmsr(0xC0010061, &limmsr) >= 0); + + cpuidCPB = ((cpuidregs[3] & (1 << 9)) != 0); + + s = smalloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + p = s; e = s+READSTR; + p = seprint(p, e, "type %s\n", "amdpstate"); + p = seprint(p, e, "boostavail %s\n", + (cpuidCPB) ? "yes" : "no"); + /* FIXME check bits in PCI config space too; requires ECAM */ + + if(hwcrvalid){ + CpbDis = ((ulong)hwcr & (1 << 25)); + p = seprint(p, e, "boost %s\n", + CpbDis ? "disabled" : "enabled"); + }else{ + p = seprint(p, e, "ERROR MSRC001_0015 (HWCR) unreadable\n"); + } + + if(limmsrvalid){ + PstateMaxVal = (limmsr >> 4) & 15; + CurPstateLimit = limmsr & 15; + p = seprint(p, e, "pstaterange %d %d\n", PstateMaxVal, CurPstateLimit); + }else{ + PstateMaxVal = -1; + CurPstateLimit = 0; + p = seprint(p, e, "ERROR MSRC001_0061 (PState limits) unreadable\n"); + } + + /* TODO dump more data */ + USED(PstateMaxVal); + USED(CurPstateLimit); + + USED(p); + n = readstr(offset, a, n, s); + poperror(); + free(s); + + return n; +} + +static long +amdpstatectlwrite(Chan *, void *a, long n, vlong off) { Cmdbuf *cb; @@ -93,5 +200,5 @@ cpufreqlink(void) { if(amdpstatecpufreqok()) - addarchfile("cpufreqctl", 0664, nil, amdpstatectlwrite); + addarchfile("cpufreqctl", 0664, amdpstatectlread, amdpstatectlwrite); }