sysclk: add on-the-fly CPU undervolt

This commit is contained in:
souldbminersmwc
2026-02-16 18:27:26 -05:00
parent 4a1772df77
commit 190353dc11
11 changed files with 215 additions and 9 deletions

View File

@@ -12,9 +12,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#pragma once
@@ -310,6 +309,9 @@
#define EMC_PMACRO_CMD_CTRL_1_0 0x784
#define EMC_PMACRO_CMD_CTRL_2_0 0x788
#define MC_REGISTER_BASE 0x70019000
#define MC_REGISTER_REGION_SIZE 0x1000
#define MC_INTSTATUS_0 0x000
#define MC_INTMASK_0 0x004
#define MC_ERR_STATUS_0 0x008
@@ -489,4 +491,40 @@
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xBEC
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS_0 0xC00
#define MC_SECURITY_CARVEOUT2_BOM_0 0xC5C
#define MC_SECURITY_CARVEOUT3_BOM_0 0xCAC
#define MC_SECURITY_CARVEOUT3_BOM_0 0xCAC
#define CLDVFS_REGION_BASE 0x70110000
#define CLDVFS_REGION_SIZE 0x1000
#define CL_DVFS_CTRL_0 0x0
#define CL_DVFS_CONFIG_0 0x4
#define CL_DVFS_PARAMS_0 0x8
#define CL_DVFS_TUNE0_0 0xC
#define CL_DVFS_TUNE1_0 0x10
#define CL_DVFS_FREQ_REQ_0 0x14
#define CL_DVFS_SCALE_RAMP_0 0x18
#define CL_DVFS_DROOP_CTRL_0 0x1C
#define CL_DVFS_OUTPUT_CFG_0 0x20
#define CL_DVFS_OUTPUT_FORCE_0 0x24
#define CL_DVFS_MONITOR_CTRL_0 0x28
#define CL_DVFS_MONITOR_DATA_0 0x2C
#define CL_DVFS_I2C_CFG_0 0x40
#define CL_DVFS_I2C_VDD_REG_ADDR_0 0x44
#define CL_DVFS_I2C_STS_0 0x48
#define CL_DVFS_INTR_STS_0 0x5C
#define CL_DVFS_INTR_EN_0 0x60
#define DVFS_DFLL_THROTTLE_CTRL_0 0x64
#define DVFS_DFLL_THROTTLE_LIGHT_0 0x68
#define DVFS_DFLL_THROTTLE_MEDIUM_0 0x6C
#define DVFS_DFLL_THROTTLE_HEAVY_0 0x70
#define DVFS_CC4_HVC_0 0x74
#define CL_DVFS_MONITOR_DATA_0 0x2C
#define CL_DVFS_I2C_CFG_0 0x40
#define CL_DVFS_I2C_VDD_REG_ADDR_0 0x44
#define CL_DVFS_I2C_STS_0 0x48
#define CL_DVFS_INTR_STS_0 0x5C
#define CL_DVFS_INTR_EN_0 0x60
#define DVFS_DFLL_THROTTLE_CTRL_0 0x64
#define DVFS_DFLL_THROTTLE_LIGHT_0 0x68
#define DVFS_DFLL_THROTTLE_MEDIUM_0 0x6C
#define DVFS_DFLL_THROTTLE_HEAVY_0 0x70
#define CL_DVFS_I2C_CLK_DIVISOR_REGISTER_0 0x16C

View File

@@ -58,7 +58,7 @@ typedef enum {
HorizonOCConfigValue_DVFSMode,
HorizonOCConfigValue_DVFSOffset,
HorizonOCConfigValue_LiveCpuUv,
HorizonOCConfigValue_EnableExperimentalSettings,
HorizonOCConfigValue_GPUScheduling,
@@ -235,6 +235,9 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
case HorizonOCConfigValue_GPUScheduling:
return pretty ? "GPU Scheduling" : "gpu_scheduling";
case HorizonOCConfigValue_LiveCpuUv:
return pretty ? "Live CPU Undervolt" : "live_cpu_uv";
case HorizonOCConfigValue_EnableExperimentalSettings:
return pretty ? "Enable Experimental Settings" : "enable_experimental_settings";
@@ -416,6 +419,7 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
case HorizonOCConfigValue_OverwriteRefreshRate:
case HorizonOCConfigValue_EnableUnsafeDisplayFreqs:
case HorizonOCConfigValue_GPUScheduling:
case HorizonOCConfigValue_LiveCpuUv:
return 0ULL;
case HocClkConfigValue_EristaMaxCpuClock:
return 1785ULL;
@@ -463,6 +467,7 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case HorizonOCConfigValue_EnableUnsafeDisplayFreqs:
case HocClkConfigValue_IsFirstLoad:
case HorizonOCConfigValue_EnableExperimentalSettings:
case HorizonOCConfigValue_LiveCpuUv:
return (input & 0x1) == input;
case KipConfigValue_custRev:

View File

@@ -409,7 +409,8 @@ void MiscGui::listUI()
// };
if(this->configList->values[HorizonOCConfigValue_EnableExperimentalSettings]) {
this->listElement->addItem(new tsl::elm::CategoryHeader("Experimental"));
addConfigToggle(HorizonOCConfigValue_LiveCpuUv, nullptr);
std::vector<NamedValue> gpuSchedValues = {
NamedValue("Do not override", GpuSchedulingMode_DoNotOverride),
NamedValue("Enabled", GpuSchedulingMode_Enabled, "96.5% limit"),

View File

@@ -57,6 +57,15 @@
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x70110000",
"size": "0x1000",
"is_ro": false,
"is_io": true
}
},
{
"type": "syscalls",
"value": {

View File

@@ -105,6 +105,8 @@ u16 cpuSpeedo0, cpuSpeedo2, socSpeedo0; // CPU, GPU, SOC
u32 speedoBracket;
u16 cpuIDDQ, gpuIDDQ, socIDDQ;
u8 g_dramID = 0;
u64 cldvfs, cldvfs_temp;
u32 cachedEristaUvLowTune0 = 0, cachedEristaUvLowTune1 = 0, cachedMarikoUvHighTune0 = 0;
static const u32 ramBrackets[][22] = {
{ 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, },
@@ -290,6 +292,16 @@ void Board::Initialize()
}
FetchHardwareInfos();
rc = svcQueryMemoryMapping(&cldvfs, &cldvfs_temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE);
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)");
if(Board::GetSocType() == SysClkSocType_Erista) {
cachedEristaUvLowTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
cachedEristaUvLowTune1 = *(u32*)(cldvfs + CL_DVFS_TUNE1_0);
} else {
Board::SetHz(SysClkModule_CPU, 1785000000);
cachedMarikoUvHighTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
Board::ResetToStockCpu();
}
}
void Board::fuseReadSpeedos() {
@@ -1134,9 +1146,6 @@ void Board::PcvHijackDvfs(u32 vmin) {
FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
}
#define MC_REGISTER_BASE 0x70019000
#define MC_REGISTER_REGION_SIZE 0x1000
bool Board::IsDram8GB() {
SecmonArgs args = {};
args.X[0] = 0xF0000002;
@@ -1173,4 +1182,125 @@ void Board::SetDisplayRefreshDockedState(bool docked) {
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
DisplayRefresh_SetDockedState(docked);
}
}
typedef struct EristaCpuUvEntry {
u32 tune0;
u32 tune1;
} EristaCpuUvEntry;
typedef struct MarikoCpuUvEntry {
u32 tune0_low;
u32 tune0_high;
u32 tune1_low;
u32 tune1_high;
} MarikoCpuUvEntry;
EristaCpuUvEntry eristaCpuUvTable[5] = {
{0xffff, 0x27007ff},
{0xefff, 0x27407ff},
{0xdfff, 0x27807ff},
{0xdfdf, 0x27a07ff},
{0xcfdf, 0x37007ff},
};
MarikoCpuUvEntry marikoCpuUvLow[12] = {
{0xffa0, 0xffff, 0x21107ff, 0},
{0x0, 0xffdf, 0x21107ff, 0x27207ff},
{0xffdf, 0xffdf, 0x21107ff, 0x27307ff},
{0xffff, 0xffdf, 0x21107ff, 0x27407ff},
{0x0, 0xffdf, 0x21607ff, 0x27707ff},
{0x0, 0xffdf, 0x21607ff, 0x27807ff},
{0x0, 0xdfff, 0x21607ff, 0x27b07ff},
{0xdfff, 0xdfff, 0x21707ff, 0x27b07ff},
{0xdfff, 0xdfff, 0x21707ff, 0x27c07ff},
{0xdfff, 0xdfff, 0x21707ff, 0x27d07ff},
{0xdfff, 0xdfff, 0x21707ff, 0x27e07ff},
{0xdfff, 0xdfff, 0x21707ff, 0x27f07ff},
};
MarikoCpuUvEntry marikoCpuUvHigh[12] = {
{0x0, 0xffff, 0, 0},
{0x0, 0xffdf, 0, 0x27207ff},
{0x0, 0xffdf, 0, 0x27307ff},
{0x0, 0xffdf, 0, 0x27407ff},
{0x0, 0xffdf, 0, 0x27707ff},
{0x0, 0xffdf, 0, 0x27807ff},
{0x0, 0xdfff, 0, 0x27b07ff},
{0x0, 0xdfff, 0, 0x27c07ff},
{0x0, 0xdfff, 0, 0x27d07ff},
{0x0, 0xdfff, 0, 0x27e07ff},
{0x0, 0xdfff, 0, 0x27f07ff},
{0x0, 0xdfff, 0, 0x27f07ff},
};
void Board::SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint) {
u32* tune0_ptr = (u32*)(cldvfs + CL_DVFS_TUNE0_0);
u32* tune1_ptr = (u32*)(cldvfs + CL_DVFS_TUNE1_0);
if(Board::GetSocType() == SysClkSocType_Mariko) {
if(Board::GetHz(SysClkModule_CPU) < tbreakPoint && (levelLow || levelHigh)) {
if(levelLow) {
*tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low;
*tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low;
}
return;
} else {
if(levelLow) {
*tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low;
*tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low;
}
if(levelHigh) {
*tune0_ptr = marikoCpuUvHigh[levelHigh-1].tune0_high;
*tune1_ptr = marikoCpuUvHigh[levelHigh-1].tune1_high;
}
return;
}
if(Board::GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
*tune0_ptr = 0xCFFF;
*tune1_ptr = 0xFF072201;
return;
} else if (Board::GetHz(SysClkModule_CPU) >= tbreakPoint || (!levelHigh)) {
*tune0_ptr = cachedMarikoUvHighTune0; // per console?
*tune1_ptr = 0xFFF7FF3F;
return;
}
} else {
if(Board::GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
*tune0_ptr = cachedEristaUvLowTune0; // I think each erista has a different tune0/tune1?
*tune1_ptr = cachedEristaUvLowTune1;
return;
} else {
if(levelLow) {
*tune0_ptr = eristaCpuUvTable[levelLow-1].tune0;
*tune1_ptr = eristaCpuUvTable[levelLow-1].tune1;
} else {
*tune0_ptr = 0x0;
*tune1_ptr = 0x0;
}
}
}
}
/*
enum TableConfig: u32 {
DEFAULT_TABLE = 1,
TBREAK_1581 = 2,
TBREAK_1683 = 3,
EXTREME_TABLE = 4,
};
*/
u32 Board::CalculateTbreak(u32 table) {
if(Board::GetSocType() == SysClkSocType_Erista)
return 1581000000;
else {
switch(table) {
case 1 ... 2:
case 4:
return 1581000000;
case 3:
return 1683000000;
default:
return 1581000000;
}
}
}

View File

@@ -66,6 +66,8 @@ class Board
static bool IsDram8GB();
static void SetGpuSchedulingMode(GpuSchedulingMode mode);
static void SetDisplayRefreshDockedState(bool docked);
static void SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint);
static u32 CalculateTbreak(u32 table);
protected:
static void FetchHardwareInfos();
static PcvModule GetPcvModule(SysClkModule sysclkModule);

View File

@@ -550,9 +550,15 @@ void ClockManager::Tick()
this->context->voltages[HocClkVoltage_GPU] = vmin * 1000;
}
Board::SetHz((SysClkModule)module, nearestHz);
this->context->freqs[module] = nearestHz;
if(module == SysClkModule_CPU && this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv))
{
if(Board::GetSocType() == SysClkSocType_Erista)
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
else
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf)));
}
}
if(module == SysClkModule_MEM && Board::GetSocType() == SysClkSocType_Mariko && targetHz < oldHz && this->config->GetConfigValue(HorizonOCConfigValue_DVFSMode) == DVFSMode_Hijack) {
@@ -583,6 +589,14 @@ void ClockManager::Tick()
void ClockManager::ResetToStockClocks() {
Board::ResetToStockCpu();
if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv))
{
if(Board::GetSocType() == SysClkSocType_Erista)
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
else
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf)));
}
Board::ResetToStockGpu();
}
@@ -656,6 +670,13 @@ bool ClockManager::RefreshContext()
case SysClkModule_CPU:
if(!(apmExtIsBoostMode(mode) || (this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode) && apmExtIsBoostMode(mode))))
Board::ResetToStockCpu();
if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv)) {
if(Board::GetSocType() == SysClkSocType_Erista)
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
else
Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf)));
}
break;
case SysClkModule_GPU:
Board::ResetToStockGpu();

Binary file not shown.

Binary file not shown.

Binary file not shown.