hocclk: actually implement cpu dvfs

This commit is contained in:
souldbminersmwc committed 2026-10-04 20:52:27 -04:00
1 parent befe98fa4b
commit a8f221aed8
7 files changed
+148 -26

No files matched your search

@@ -25,6 +25,7 @@
#include <switch.h>
#include "../file/file_utils.hpp"
#include "../file/config.hpp"
#include "../hos/rgltr.h"
#include "../i2c/i2cDrv.h"
#include "board.hpp"
@@ -593,6 +594,7 @@ namespace board {
return baseVolt;
}
/* Mariko only! */
constexpr u32 PmicVmin = 250;
constexpr u32 PmicStep = 5;
@@ -602,6 +604,12 @@ namespace board {
}
void ApplyCpuDvfs(u32 ramFreqMhz) {
/* Don't reapply the table if it has been*/
static u32 lastRamFreqMhz = 0;
if(ramFreqMhz == lastRamFreqMhz) {
return;
}
/* Get the minimum DRAM OC Cpu Vmin */
u32 vmin = GetMinimumCpuVmin(ramFreqMhz, GetCpuSpeedoBracket());
@@ -615,7 +623,6 @@ namespace board {
if(vlt < vmin) {
vlt = vmin;
}
/* Don't know if modifying and passing argument causes undefined behavior? */
u32 tempVlt = vlt;
vlt = ConvertVoltToLut(tempVlt);
@@ -624,12 +631,18 @@ namespace board {
/* Pet the watchdog to avoid it hanging the system during this operation */
i2c::wdt::Pet();
/* DSB to prevent a hang during setting the new LUT */
/* DSB to prevent a hang during setting the new LUT. ST will not work, SY is needed */
__asm__("dsb SY");
/* Copy the edited LUT into it's appropriate location */
memcpy((void*)(cldvfs + CL_DVFS_OUTPUT_LUT_0), NewCpuVoltTable, sizeof(NewCpuVoltTable));
file::utils::LogLine("[cpudvfs]: Set voltage to %dmV for %dMHz", vmin, ramFreqMhz);
/* Pet the watchdog to avoid it hanging the system during this operation */
i2c::wdt::Pet();
svcSleepThread(100'000'000); // 100ms
lastRamFreqMhz = ramFreqMhz;
}
} // namespace board
@@ -20,6 +20,7 @@
#include "../file/config.hpp"
#include <lockable_mutex.h>
#include <mutex>
#include "../file/file_utils.hpp"
/* PMIC Registers */
#define MAX77620_REG_CNFGGLBL2 ((u8)0x01)
@@ -68,6 +69,8 @@ namespace i2c::wdt {
return;
}
file::utils::LogLine("[Watchdog] armed");
Result rc;
u8 twd;
@@ -108,6 +111,7 @@ namespace i2c::wdt {
if (!isWdtEnabled) {
return;
}
file::utils::LogLine("[Watchdog] disarmed");
Result rc = update_bits(MAX77620_REG_CNFGGLBL2, MAX77620_WDTEN, (u8)0x00);
ASSERT_RESULT_OK(rc, "update_bits");
@@ -427,7 +427,7 @@ namespace mgr {
}
void ApplyGpuDvfs(u32 targetHz) {
s32 dvfsOffset = file::config::GetConfigValue(HocClkConfigValue_DVFSOffset);
s32 dvfsOffset = file::config::GetConfigValue(HocClkConfigValue_GPUDVFSOffset);
dvfsOffset = std::max(dvfsOffset, -80);
u32 vmin = board::GetMinimumGpuVmin(targetHz / 1000000, board::GetGpuSpeedoBracket());
@@ -450,8 +450,39 @@ namespace mgr {
}
}
void ApplyCpuDvfs(u32 targetHz) {
if(file::config::GetConfigValue(HocClkConfigValue_CPUDVFSMode) == CPUDVFSMode_Modify) {
board::ApplyCpuDvfs(targetHz / 1'000'000); // convert to mhz
u32 ltargetHz = gContext.overrideFreqs[HocClkModule_CPU];
if (!ltargetHz) {
ltargetHz = file::config::GetAutoClockHz(gContext.applicationId, HocClkModule_CPU, gContext.profile, false);
if (!ltargetHz)
ltargetHz = file::config::GetAutoClockHz(HOCCLK_GLOBAL_PROFILE_TID, HocClkModule_CPU, gContext.profile, false);
}
u32 maxHz = GetMaxAllowedHz(HocClkModule_CPU, gContext.profile);
u32 nearestHz = ltargetHz ? GetNearestHz(HocClkModule_CPU, ltargetHz, maxHz) : 0;
board::SetHz(HocClkModule_CPU, 1'785'000'000);
svcSleepThread(5'000'000);
if(nearestHz != 0) {
board::SetHz(HocClkModule_CPU, nearestHz);
} else {
board::ResetToStockCpu();
}
}
}
void ResetCpuDvfs() {
if(file::config::GetConfigValue(HocClkConfigValue_CPUDVFSMode) == CPUDVFSMode_Modify) {
board::ApplyCpuDvfs(0);
board::SetHz(HocClkModule_CPU, 1'785'000'000);
svcSleepThread(5'000'000);
board::ResetToStockCpu();
}
}
void DVFSReset() {
if (file::config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
if (file::config::GetConfigValue(HocClkConfigValue_GPUDVFSMode) == DVFSMode_Hijack) {
board::PcvHijackGpuVolts(0); // Reset to vMin
u32 targetHz = GetNearestOverrideHz(HocClkModule_GPU);
@@ -462,7 +493,7 @@ namespace mgr {
}
}
void HandleFreqReset(HocClkModule module, bool isBoost, bool didHijackPcv) {
void HandleFreqReset(HocClkModule module, bool isBoost, bool didHijackPcv, bool didApplyCpuDvfs) {
switch (module) {
case HocClkModule_CPU:
if (!(isBoost || (file::config::GetConfigValue(HocClkConfigValue_OverwriteBoostMode) && isBoost)))
@@ -485,6 +516,10 @@ namespace mgr {
DVFSReset();
didHijackPcv = true;
}
if(!didApplyCpuDvfs) {
ResetCpuDvfs();
didApplyCpuDvfs = true;
}
break;
case HocClkModule_Display:
if (file::config::GetConfigValue(HocClkConfigValue_OverwriteRefreshRate)) {
@@ -506,9 +541,10 @@ namespace mgr {
u32 nearestFreq = GetCurrentNearestFrequency(HocClkModule_MEM);
if (targetRamHz != nearestFreq) {
if (file::config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
if (file::config::GetConfigValue(HocClkConfigValue_GPUDVFSMode) == DVFSMode_Hijack) {
ApplyGpuDvfs(targetRamHz);
}
ApplyCpuDvfs(targetRamHz);
board::SetHz(HocClkModule_MEM, targetRamHz);
}
@@ -520,7 +556,7 @@ namespace mgr {
std::uint32_t nearestHz = 0;
static bool prepareBoostExit = false;
bool didHijackPcv = false;
bool didHijackPcv = false, didApplyCpuDvfs = false;
bool skipCpuDueToBoost = isBoost && !file::config::GetConfigValue(HocClkConfigValue_OverwriteBoostMode);
if (skipCpuDueToBoost) {
board::SetHz(HocClkModule_CPU, board::GetHz(HocClkModule_CPU));
@@ -570,7 +606,7 @@ namespace mgr {
gContext.stable.freqs[HocClkModule_Display] = targetHz;
gContext.stable.realFreqs[HocClkModule_Display] = targetHz;
} else {
HandleFreqReset(HocClkModule_Display, isBoost, didHijackPcv);
HandleFreqReset(HocClkModule_Display, isBoost, didHijackPcv, didApplyCpuDvfs);
}
}
@@ -605,9 +641,13 @@ namespace mgr {
targetHz / 100000 - targetHz / 1000000 * 10);
// The logic MUST be done in this order otherwise you WILL get crashes
if (module == HocClkModule_MEM && targetHz > oldHz && file::config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
if (module == HocClkModule_MEM && targetHz > oldHz && file::config::GetConfigValue(HocClkConfigValue_GPUDVFSMode) == DVFSMode_Hijack) {
ApplyGpuDvfs(targetHz);
}
if (module == HocClkModule_MEM && targetHz > oldHz && board::GetSocType() == HocClkSocType_Mariko) {
ApplyCpuDvfs(targetHz);
}
board::SetHz((HocClkModule)module, nearestHz);
gContext.freqs[module] = nearestHz;
@@ -615,15 +655,22 @@ namespace mgr {
gContext.stable.freqs[module] = nearestHz;
}
if (module == HocClkModule_MEM && targetHz < oldHz && file::config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
if (module == HocClkModule_MEM && targetHz < oldHz && file::config::GetConfigValue(HocClkConfigValue_GPUDVFSMode) == DVFSMode_Hijack) {
ApplyGpuDvfs(targetHz);
}
if (module == HocClkModule_MEM && targetHz < oldHz && board::GetSocType() == HocClkSocType_Mariko) {
ApplyCpuDvfs(targetHz);
}
if (module == HocClkModule_MEM && file::config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack)
if (module == HocClkModule_MEM && file::config::GetConfigValue(HocClkConfigValue_GPUDVFSMode) == DVFSMode_Hijack)
didHijackPcv = false;
if(module == HocClkModule_MEM && board::GetSocType() == HocClkSocType_Mariko && file::config::GetConfigValue(HocClkConfigValue_CPUDVFSMode) == CPUDVFSMode_Modify) {
didApplyCpuDvfs = true;
}
}
} else {
HandleFreqReset((HocClkModule)module, isBoost, didHijackPcv);
HandleFreqReset((HocClkModule)module, isBoost, didHijackPcv, didApplyCpuDvfs);
}
}
}
@@ -652,7 +699,7 @@ namespace mgr {
// restore clocks to stock values on app or profile change
if (hasChanged) {
board::ResetToStock();
if (file::config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
if (file::config::GetConfigValue(HocClkConfigValue_GPUDVFSMode) == DVFSMode_Hijack) {
board::PcvHijackGpuVolts(0);
board::ResetToStockGpu();
}