ryzen_nb_smu
ryzen_nb_smu copied to clipboard
Different SMU message IDs for different platforms?
Hi, at the time of writing I found another tool called Zenstates. Referring to his readme, I can see that there are a few SMU message IDs that doesn't seem to match yours, notably:
- SetOverclockFreqAllCores (0x26)
- SetOverclockFreqPerCores (0x27)
- SetBoostLimitFrequency (0x29)
- SetBoostLimitFrequencyAllCores (0x2B)
as well as some others. The author didn't mention what CPU he is using when reverse engineering the SMU, but he was on SMU version 64.40.00. Testing his app already send my device (Nitro 5 2500U) to 400MHz hell so I won't step further without proper documentation. So this somehow made me to question:
- Do SMU message ID differs by platform? As you are testing your code on FP5 platform while the other one seems to be on AM4.
- Has AMD updated SMU version to scramble the message ID? Recall that AMD recommends to update to AGESA 1.0.04 even for older Zen/Zen+ which we know that there are no issues like RDRAND on Zen2
Sorry that I have to ask using the issues section as for some reason I couldn't contact you over Discord. I tried to ask for explanation from the Zenstates author but seems like he didn't reply
Sorry, my discord account was blocked. So you can nolonger reach me by discord. Email is preferred.
SMU Call ID do varied between different platforms. All Raven Ridge share the same interface as I know.
I haven't analyze recent SMU update, so I'm not sure if AMD had done that. Probably we can check acpi table to see if IDs have changed?
Currently my BIOS is 1.06 which Acer updated it. I did try to check SMU version by sending the command but unable to make a sense of it. Seems like the message response is in a reverse order as in the SMU command GetNameString
returns DMA
which supposed to be AMD. Probably different endian as I heard the SMU chip is STM chip.
btw the command that failed me is DisableSMUFeatures
and here's how I sent the command through the program: 0x6 39
. Seems like not enough arguments from the response message.
Currently my BIOS is 1.06 which Acer updated it. I did try to check SMU version by sending the command but unable to make a sense of it. Seems like the message response is in a reverse order as in the SMU command
GetNameString
returnsDMA
which supposed to be AMD. Probably different endian as I heard the SMU chip is STM chip.
SMU is integrated in processor chip. Probably a arm or LM32 risc core.
btw the command that failed me is
DisableSMUFeatures
and here's how I sent the command through the program:0x6 39
. Seems like not enough arguments from the response message. This is a news to me. But I can't do further investigation as I nolonger own an APU machine.
This is a news to me. But I can't do further investigation as I nolonger own an APU machine.
Do you have any lead on finding the source for at least Raven Ridge mobile AGESA documentation? AMD do provide family 15h AGESA documentation (although a bit scarce) but things are a bit different in family 17h where everything is manufacturer only. Asus Zenstates apparently uses SMU commands to change parameters but the information is useful for Summit Ridge/Pinnacle Ridge.
They all have different SMU commands. Some CPUs support more than one mailbox and commands for these mailboxes also differ. ZenStates does not support APUs currently, but I am working on it. The addresses I have are the old SMU commands for Matisse (and similar to those for SummitRidge). The SMU addresses for the APUs seem to be consistent, however I don't have a platform to test myself. Sadly, no public documentation. AFAIK "Disable SMU Features" got blocked by AMD since some SMU firmware version, but I don't know which one exactly.
@irusanov my ideapad 5 15 should arrive in 2 weeks max. I more than willing to help/work together with you to go on the hunt for the smu addresses.
Decided to chime in on this fun. The following information was reverse engineered from Ryzen Master so it's a tad easier than reversing a BIOS and has the benefit of guaranteeing support on all supported models that Ryzen Master also supports. So here we go, tagging @Flygoat and @irusanov as they've helped me out and might find some of this interesting:
List of supported CPU codenames that have SMU capabilities:
- Rome
- Colfax
- Renoir
- Picasso
- Matisse
- Threadripper
- Castle Peak
- Raven Ridge
- Raven Ridge 2
- Summit Ridge
- Pinnacle Ridge
And here are the SMU addresses for each of them, a total of three different sets of mailboxes:
switch (g_smu.codename) {
case CODENAME_MATISSE:
case CODENAME_CASTLEPEAK:
case CODENAME_ROME:
g_smu.addr_reg_rsp = 0x3B10570;
g_smu.addr_reg_args = 0x3B10A40;
g_smu.addr_reg_cmd = 0x3B10524;
break;
case CODENAME_PINNACLERIDGE:
case CODENAME_COLFAX:
case CODENAME_SUMMITRIDGE:
case CODENAME_THREADRIPPER:
g_smu.addr_reg_rsp = 0x3B10568;
g_smu.addr_reg_args = 0x3B10590;
g_smu.addr_reg_cmd = 0x3B1051C;
break;
case CODENAME_RAVENRIDGE:
case CODENAME_RAVENRIDGE2:
case CODENAME_PICASSO:
case CODENAME_RENOIR:
g_smu.addr_reg_rsp = 0x3B10A80;
g_smu.addr_reg_args = 0x3B10A88;
g_smu.addr_reg_cmd = 0x3B10A20;
break;
default:
return -5;
}
Now each CPU class either has completely different function/command IDs for the SMU or they simply do not exist at all. In addition, some commands take different arguments across CPU ranges.
Here's a few functions and their supported CPU models:
int smu_set_ppt(int ppt_watts) {
int args[6] = { ppt_watts * 1000, 0, 0, 0, 0, 0 };
int fn;
if (ppt_watts < 45 || ppt_watts > 511)
return SMU_Return_InvalidArgument;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
case CODENAME_MATISSE:
fn = 0x53;
break;
case CODENAME_COLFAX:
case CODENAME_PINNACLERIDGE:
fn = 0x58;
break;
case CODENAME_PICASSO:
fn = 0x2F;
break;
case CODENAME_RENOIR:
fn = 0x32;
break;
default:
return SMU_Return_Unsupported;
}
return smu_send_command(fn, args, 1);
}
int smu_set_tdc(int tdc_amps) {
int args[6] = { tdc_amps * 1000, 0, 0, 0, 0, 0 };
int fn;
if (tdc_amps < 30 || tdc_amps > 511)
return SMU_Return_InvalidArgument;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
case CODENAME_MATISSE:
fn = 0x54;
break;
case CODENAME_COLFAX:
case CODENAME_PINNACLERIDGE:
fn = 0x65;
break;
default:
return SMU_Return_Unsupported;
}
return smu_send_command(fn, args, 1);
}
int smu_set_edc(int edc_amps) {
int args[6] = { edc_amps * 1000, 0, 0, 0, 0, 0 };
int fn;
if (edc_amps < 30 || edc_amps > 511)
return SMU_Return_InvalidArgument;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
case CODENAME_MATISSE:
fn = 0x55;
break;
case CODENAME_COLFAX:
fn = 0x6C;
break;
case CODENAME_PINNACLERIDGE:
fn = 0x66;
break;
default:
return SMU_Return_Unsupported;
}
return smu_send_command(fn, args, 1);
}
int smu_set_pbo_scalar(int scalar) {
int args[6] = { 1000 - (scalar * 100), 0, 0, 0, 0, 0 };
int fn;
if (scalar < 1 || scalar > 10)
return SMU_Return_InvalidArgument;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
case CODENAME_MATISSE:
fn = 0x58;
break;
case CODENAME_PICASSO:
fn = 0x7C;
break;
case CODENAME_COLFAX:
fn = 0x6F;
break;
case CODENAME_PINNACLERIDGE:
fn = 0x6A;
break;
case CODENAME_RENOIR:
fn = 0x3F;
break;
default:
return SMU_Return_Unsupported;
}
return smu_send_command(fn, args, 1);
}
int smu_get_pbo_scalar() {
int args[6] = { 0x3F800000, 0, 0, 0, 0, 0 }, fn, ret;
switch (g_smu.codename) {
case CODENAME_PICASSO:
fn = 0x62;
break;
case CODENAME_CASTLEPEAK:
case CODENAME_MATISSE:
fn = 0x6C;
break;
case CODENAME_COLFAX:
fn = 0x70;
break;
case CODENAME_PINNACLERIDGE:
fn = 0x6f;
break;
case CODENAME_RAVENRIDGE2:
fn = 0x68;
break;
case CODENAME_RENOIR:
fn = 0xf;
break;
default:
return SMU_Return_Unsupported;
}
ret = smu_send_command(fn, args, 1);
if (ret != 0)
return ret;
return (int)*(float*)&args[0];
}
off_t smu_get_dram_base_address() {
int args[6] = { 0, 0, 0, 0, 0, 0 }, fn[3], ret;
// Matisse and Renoir have a PM table that is mapped to
// the base address that contains interesting stuff like
// the current voltage planes for CCD/IOD (VDDP, VDDG)
// as well as best core rankings, gated cores, how long
// cores were gated for, their average and peak voltage
// over time. Basically all the fun stuff Ryzen Master uses
// that no other application seems to be capable of showing.
//
// And you can bet the entire thing is absolutely undocumented.
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
case CODENAME_MATISSE:
fn[0] = 0x06;
goto BASE_ADDR_CLASS_1;
case CODENAME_RENOIR:
fn[0] = 0x66;
goto BASE_ADDR_CLASS_1;
case CODENAME_PINNACLERIDGE:
case CODENAME_COLFAX:
fn[0] = 0x0C;
goto BASE_ADDR_CLASS_2;
case CODENAME_PICASSO:
case CODENAME_RAVENRIDGE:
case CODENAME_RAVENRIDGE2:
// These can be done but provide two results.
// No idea what each translates to for now
// but each contains different sets of info.
default:
return SMU_Return_Unsupported;
}
BASE_ADDR_CLASS_1:
args[0] = args[1] = 1;
ret = smu_send_command(fn[0], args, 2);
if (ret != 0)
return ret;
return ((u64)args[1] << 32) | args[0];
BASE_ADDR_CLASS_2:
ret = smu_send_command(fn[0], args, 1);
if (ret != 0)
return ret;
return args[0];
}
int smu_enable_overclocking() {
int args[6] = { 0, 0, 0, 0, 0, 0 }, fn;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
fn = 0x5A;
break;
default:
return SMU_Return_Unsupported;
}
return smu_send_command(fn, args, 1);
}
int smu_disable_overclocking() {
int args[6] = { 0, 0, 0, 0, 0, 0 }, fn;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
fn = 0x5B;
break;
default:
return SMU_Return_Unsupported;
}
return smu_send_command(fn, args, 1);
}
int smu_disable_prochot() {
int args[6] = { 0, 0, 0, 0, 0, 0 }, fn;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
case CODENAME_MATISSE:
fn = 0x5A;
args[0] = 0x1000000;
break;
case CODENAME_PINNACLERIDGE:
case CODENAME_SUMMITRIDGE:
case CODENAME_THREADRIPPER:
case CODENAME_PICASSO:
case CODENAME_COLFAX:
fn = 0x63;
break;
case CODENAME_RAVENRIDGE2:
fn = 0x69;
break;
default:
return SMU_Return_Unsupported;
}
return smu_send_command(fn, args, 1);
}
int smu_get_version() {
int args[6] = { 1, 0, 0, 0, 0, 0 }, ret;
ret = smu_send_command(2, args, 1);
if (ret != 0)
return ret;
return args[0];
}
Bonus SMN stuff:
double smn_cpu_hw_vid() {
u32 addr;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
addr = 0x5A054;
break;
case CODENAME_MATISSE:
addr = 0x5A050;
break;
case CODENAME_RENOIR:
addr = 0x6F05C;
break;
case CODENAME_COLFAX:
case CODENAME_PICASSO:
case CODENAME_RAVENRIDGE:
case CODENAME_THREADRIPPER:
case CODENAME_RAVENRIDGE2:
case CODENAME_SUMMITRIDGE:
case CODENAME_PINNACLERIDGE:
addr = 0x5A04C;
break;
default:
return 0;
}
return 1.55 - ((smn_read_address(addr) >> 24) * 0.00625);
}
double smn_get_vddcr_soc() {
u32 addr;
switch (g_smu.codename) {
case CODENAME_CASTLEPEAK:
addr = 0x5A05C;
break;
case CODENAME_MATISSE:
addr = 0x5A058;
break;
case CODENAME_RENOIR:
addr = 0x6F064;
break;
case CODENAME_COLFAX:
case CODENAME_PICASSO:
case CODENAME_RAVENRIDGE:
case CODENAME_THREADRIPPER:
case CODENAME_RAVENRIDGE2:
case CODENAME_SUMMITRIDGE:
case CODENAME_PINNACLERIDGE:
addr = 0x5A054;
break;
default:
return 0;
}
return 1.55 - ((smn_read_address(addr) >> 24) * 0.00625);
}
int smn_cpu_prochot_enabled() {
u32 addr;
switch (g_smu.codename) {
case CODENAME_COLFAX:
case CODENAME_MATISSE:
case CODENAME_PICASSO:
case CODENAME_RAVENRIDGE:
case CODENAME_CASTLEPEAK:
case CODENAME_SUMMITRIDGE:
case CODENAME_THREADRIPPER:
case CODENAME_RAVENRIDGE2:
case CODENAME_PINNACLERIDGE:
addr = 0x59804;
break;
default:
return SMU_Return_Unsupported;
}
return smn_read_address(addr) & 1;
}
int cpu_get_fused_pbo_limits(int* tdp, int* ppt, int* tdc, int* edc, int* tjmax, int *minfreq, int* maxfreq) {
int args[6] = { 0, 0, 0, 0, 0, 0 }, base, ret, tmp;
switch (g_smu.codename) {
case CODENAME_MATISSE:
base = 0x5D2BB;
break;
case CODENAME_CASTLEPEAK:
base = 0x5D31B;
break;
case CODENAME_RAVENRIDGE2:
ret = smu_send_command(0x80, args, 1);
if (ret != 0)
return ret;
*edc = args[0] / 1000;
args[0] = 0;
ret = smu_send_command(0x84, args, 1);
if (ret != 0)
return ret;
*tdc = args[0] / 1000;
args[0] = 0;
ret = smu_send_command(0x82, args, 1);
if (ret != 0)
return ret;
*ppt = args[0] / 1000;
args[0] = 0;
ret = smu_send_command(0x83, args, 1);
if (ret != 0)
return ret;
*tdp = args[0] / 1000;
args[0] = 0;
ret = smu_send_command(0x85, args, 1);
if (ret != 0)
return ret;
*tjmax = args[0] / 1000;
*minfreq = 25 * (smn_read_address(0x5D494) & 0xff);
*maxfreq = 25 * (smn_read_address(0x5D497) >> 24);
return 0;
default:
return SMU_Return_Unsupported;
}
// Castle Peak / Matisse
*edc = (smn_read_address(base) >> 30) | 4 * (smn_read_address(base + 1) & 0x7f);
*tdc = (smn_read_address(base + 3) >> 16) & 0x1ff;
*ppt = (smn_read_address(base - 5) >> 17) & 0x1ff;
*tdp = (smn_read_address(base - 3) >> 3) & 0x1ff;
*tjmax = (smn_read_address(base - 6) >> 10) & 0x7f;
tmp = smn_read_address(base + 11);
*minfreq = 25 * ((tmp >> 25) & 0x7f);
*maxfreq = 25 * ((tmp >> 17) & 0xff);
return 0;
}
Thanks, here's what I've started to document, although I have more commands as well (found mostly experimentally).
https://docs.google.com/spreadsheets/d/15qC2GlLiF3gpW5d9pVMP4npJ14yDv_yP2zT6QGEWseg/edit?usp=sharing
Alright so I've spent a few more days working on reversing the PM tables the SMU calculates and for the specific models I've listed above, this is the sort of information you can receive:
- Current PBO Limit ( TjMax, PPT, TDC, EDC )
- Current Power Usage ( Temperature, PPT, TDC, EDC, Core Power, SoC Power )
- Voltages ( SoC, VDDP, VDDG, Average + Peak Core Voltage )
- Clock Rates ( "Real" CPU Core Clocks, Time Spent Asleep, FCLK, UCLK, MCLK )
I've written a Linux driver here that exposes access to the SMU that allows you to execute SMU commands (without needing to mess with mailboxes or have to worry about PCI register race conditions), read the SMN address space as well as access the entire PM table.
For a little preview of a bit of info the SMU provides:
Alright so I've spent a few more days working on reversing the PM tables the SMU calculates and for the specific models I've listed above, this is the sort of information you can receive:
* Current PBO Limit ( TjMax, PPT, TDC, EDC ) * Current Power Usage ( Temperature, PPT, TDC, EDC, Core Power, SoC Power ) * Voltages ( SoC, VDDP, VDDG, Average + Peak Core Voltage ) * Clock Rates ( "Real" CPU Core Clocks, Time Spent Asleep, FCLK, UCLK, MCLK )
I've written a Linux driver here that exposes access to the SMU that allows you to execute SMU commands (without needing to mess with mailboxes or have to worry about PCI register race conditions), read the SMN address space as well as access the entire PM table.
For a little preview of a bit of info the SMU provides:
Looks like that's what AMD uProf kernel driver did lol That's PSMU instead of MP1_SMU which we played with.
Looks like that's what AMD uProf kernel driver did lol That's PSMU instead of MP1_SMU which we played with.
Haven't spent any time reversing uProf, might be a worthy investment.
Anyways as far as I can tell, MP1_SMU and PSMU differ in the mailbox addresses they use, correct? The info above was simply taken from the "DRAM base" that a specific SMU code triggers to update with power and clock information, which Ryzen Master updates once a second.
The mailboxes this driver uses are exactly the same that Ryzen Master itself, which uses 3 different mailboxes across all platforms, each platform only ever using one mailbox address set, for tweaking, overclocking & monitoring.
The only functional difference I use is the PCI configuration space registers, which are from Ryzen Master as well. You used [0xB8, 0xBC], Linux's SMN reader uses [0x60, 0x64] and Ryzen Master uses [0xC4, 0xC8]. All these registers seem to function exactly the same, which I can only assume is due to having the need to execute multiple SMU commands at once.
Is there functional differences between them PSMU and MP1_SMU?
Looks like that's what AMD uProf kernel driver did lol That's PSMU instead of MP1_SMU which we played with.
Haven't spent any time reversing uProf, might be a worthy investment.
Anyways as far as I can tell, MP1_SMU and PSMU differ in the mailbox addresses they use, correct? The info above was simply taken from the "DRAM base" that a specific SMU code triggers to update with power and clock information, which Ryzen Master updates once a second.
The mailboxes this driver uses are exactly the same that Ryzen Master itself, which uses 3 different mailboxes across all platforms, each platform only ever using one mailbox address set, for tweaking, overclocking & monitoring.
The only functional difference I use is the PCI configuration space registers, which are from Ryzen Master as well. You used [0xB8, 0xBC], Linux's SMN reader uses [0x60, 0x64] and Ryzen Master uses [0xC4, 0xC8]. All these registers seem to function exactly the same, which I can only assume is due to having the need to execute multiple SMU commands at once.
Is there functional differences between them PSMU and MP1_SMU?
As far I can tell PSMU is mainly for drivers to gather perf information and MP1_SMU is mainly for BIOS to set options, they have different calling commands.
You can check the source of Linux kernel modules included in AMD uProf SDK, That's publicly available and looks like it's doing exactly what you did.
You may join discord server ryzenshine https://discord.gg/mzmWkqm, somebody is working on enhance these support.
I'm now trying to reverse engineering SMUv11 Dxe in BIOS and looks like PPTable is a golden key
Alright I'll have to make an account so I'll do that this weekend.
Extra note:
Indeed, I've looked at the source of uPerf and it does seem that it's pretty much what I do except that the performance monitoring tables for 3000 series don't exist in it, as well as in general, most of the tables being undocumented.
Thankfully I've managed to reverse quite a bit for the 3000 series, mainly to get the info that Ryzen Master presents, which was my goal.
Will definitely look into uPerf more when I have time, thanks for the tip.
Alright I'll have to make an account so I'll do that this weekend.
Extra note:
Indeed, I've looked at the source of uPerf and it does seem that it's pretty much what I do except that the performance monitoring tables for 3000 series don't exist in it, as well as in general, most of the tables being undocumented.
Thankfully I've managed to reverse quite a bit for the 3000 series, mainly to get the info that Ryzen Master presents, which was my goal.
Will definitely look into uPerf more when I have time, thanks for the tip.
This was what I had started implementing with the information from ryzen_adj with an initial ]AMDuProf.NET](https://github.com/GoelBiju/AMDuProf.NET) library. I know that more people would be willing to create better user interfaces for this and that's why I tried to move what ryzen_adj was doing to C# in order to facilitate it.
Is it possible this driver could even work with Carrizo/Bristol Ridge SMU? I knew The Stilt was planning something like that once upon a time.
Is it possible this driver could even work with Carrizo/Bristol Ridge SMU? I knew The Stilt was planning something like that once upon a time.
I want to say yes but I haven't looked at it. I think it would be fun to mess around with those. Push FX like power draw out of those cores 😄
https://community.hwbot.org/topic/155748-athlon-x4-845-crossblade-ranger-and-so-it-begins/#comments https://web.archive.org/web/20160318170558/http://www.overclock.net/t/1560230/jagatreview-hands-on-amd-fx-8800p-carrizo/410#post_24287228 https://web.archive.org/web/20151220083614/http://www.overclock.net:80/t/1573937/carrizo-fx-8800p-a10-8700p-info-and-optimizations/30#post_24440641 You might appreciate these then. EDIT: https://github.com/psyborg55/amdmsrtweaker-lnx
amdmsrtweaker method is not really recommended, it's based on bar edit boost state force trick found in previous AMD CPU models. i just used it at the time i didn't figured out yet about DPM solution: https://www.dell.com/community/Inspiron/Inspiron-5576-CPU-throttling/td-p/7495901
The SMU addresses for the APUs seem to be consistent
@irusanov how likely this would be valid for pre-ryzen series? e.g. carrizo, bristol ridge.. also need to figure out how to obtain smu_get_dram_base_address, is there any other way except reversing ryzen master? bristol ridge APU is not supported by any tuning software
Ryzen Master has no support for Carrizo/Bristol Ridge. I can add support for those CPUs in the debug tool and then experiment, but have no such processor, Might see if I can get a cheap one.
if you can provide compiled executable i can test it on my system. it's cpuid 0x15 model 0x65
Pre AM4 have a totally different SMU interface so no way. Maybe we can do sth with Bristol Ridge.
well i already tried to load @leogx9r driver but it hangs at some point as i haven't defined base dram address - no idea what to put there. i also saw below base dram in code TransferTableSmu2Dram - this seems to be defined in amdgpu driver, but haven't looked much further than that. would it make sense to add a bunch of prints to linux driver and recompile it? for mailbox regs i used those for ravenridge
In order to support them in the driver, you need to add codenames, model numbers, mailbox addresses and commands for the basic operations, e.g. you need the commands to get dram base address and to transfer the table to dram, so you can then read it. The PM table structure needs to be defined as well, if you want to get the SMU metrics.
If you have added the mailbox addresses, codename, family and model, then you want to try 0x1 and 0x2 commands first.
Haven't worked on the linux version of ZenStates for quite a long time, but can probably work something out.
just tried loading --smu-test-message from your ZenStates-Linux script with all known mailbox regs. all responses have been 0 i see you implemented mailbox scan in SMUDebugTool. how reliable that method is in discovering mailbox registers?
Is there functional differences between them PSMU and MP1_SMU?
As far I can tell PSMU is mainly for drivers to gather perf information and MP1_SMU is mainly for BIOS to set options, they have different calling commands.
there is a good explanation in 50742_15h_Models_60h-6Fh_BKDG:
The SMU uses two blocks, System Manage-ment Controller (SMC) and Platform Security Processor (PSP), in order to assist with many of these tasks. At the architectural level, PSP is known as MP0 and SMC is known as MP1.
@FlyGoat is completely right
@psyborg55 According to that document, it's using the D0F0, basically the same 0x0 PCI bus address as the rest of the cpus. address offset is 0xBC data offset is 0xB8 CP2MP (CPU to MP) is 0x1300007C.
It seems to be a simplified version of the one for 17h family. You can use rweverything for easier testing, but that requires windows.
page 233
The index/data pair registers, D0F0xB8 and D0F0xBC, are used to access the registers at D0F0xBC_x[FFFF_FFFF:0000_0000]. To access any of these registers, the address is first written into the index register, D0F0xB8, and then the data is read from or written to the data register, D0F0xBC.
PS: Family 16h extends that with arguments register and the protocol is similar to the one used for 17h and 19h page 155 from this document: https://www.amd.com/system/files/TechDocs/52740_16h_Models_30h-3Fh_BKDG.pdf
The problem is there are just 3 commands (service index) for the software interrupt. Don't know if other IDs are available, it would require the usual "poking" the SMU and observing the returned value and how the CPU reacts to each command. That one can be implemented, although doesn't seem to be very useful if we support just those 3 commands
BIOSSMC_MSG_LCLK_DPM_ENABLE. Enables LCLK DPM. BIOSSMC_MSG_VDDNB_REQUEST. Request VDDNB voltage. BIOSSMC_MSG_NBDPM_Enable. Enables NB P-state Adjustments.
PS: Family 16h extends that with arguments register and the protocol is similar to the one used for 17h and 19h page 155 from this document: https://www.amd.com/system/files/TechDocs/52740_16h_Models_30h-3Fh_BKDG.pdf
great! i immediately spotted that c210_0000 register and remembered i've seen it in a vbios disassembly:
0006: 370000 SET_ATI_PORT 0000 (INDIRECT_IO_MM)
0009: 0105000b2100200f MOVE reg[0b00] [XXXX] <- 0f200021
0011: 0105cc0b01000000 MOVE reg[0bcc] [XXXX] <- 00000001
0019: 5c05d30b0000e0ffe01b3200 MASK reg[0bd3] [XXXX] & ffe00000 | 00321be0
0025: 01050214a1030300 MOVE reg[1402] [XXXX] <- 000303a1
002d: 01050314a0030300 MOVE reg[1403] [XXXX] <- 000303a0
0035: 0105171510c11040 MOVE reg[1517] [XXXX] <- 4010c110
003d: 661e SET_DATA_BLOCK 1e (IntegratedSystemInfo)
003f: 0324413800 MOVE WS_REMIND/HI32 [...X] <- data[0038] [...X]
0044: 0925413c AND WS_REMIND/HI32 [...X] <- 3c
0048: 5c224718c341 MASK reg[1847] [...X] & c3 | WS_REMIND/HI32 [...X]
004e: 370000 SET_ATI_PORT 0000 (INDIRECT_IO_MM)
0051: 010580003c0010c2 MOVE reg[0080] [XXXX] <- c210003c
0059: 5164 DELAY_MicroSec 64
005b: 0105810080000000 MOVE reg[0081] [XXXX] <- 00000080
0063: 5164 DELAY_MicroSec 64
0065: 01058000000010c2 MOVE reg[0080] [XXXX] <- c2100000
006d: 5164 DELAY_MicroSec 64
006f: 0725810001 AND reg[0081] [...X] <- 01
0074: 5132 DELAY_MicroSec 32
0076: 0d258100c0 OR reg[0081] [...X] <- c0
007b: 5132 DELAY_MicroSec 32
007d: 6725810001 XOR reg[0081] [...X] <- 01
0082: 5b EOT
the code above is The Stilt's patch to disable one of the throttling features on AMD R7 integrated graphics, didn't try it yet but i'm quite sure it will work. nice thing if it will be possible to implement more changes directly into vbios.
i am on windows (triple-boot linux,win7,win10) so i can try anything given that i can figure out what exactly to do. that 0x1300007C i've noticed already but don't know where to put it in the code