add gpu voltage request

This commit is contained in:
Lightos1
2026-07-04 13:44:45 +02:00
parent d7df43e962
commit cb0ffbcd29
11 changed files with 130 additions and 27 deletions

View File

@@ -12,9 +12,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
@@ -50,6 +50,7 @@ Result hocclkIpcSetConfigValues(HocClkConfigValueList* configValues);
Result hocclkIpcGetFreqList(HocClkModule module, u32* list, u32 maxCount, u32* outCount);
Result hocClkIpcSetKipData();
Result hocClkIpcGetKipData();
Result hocClkIpcRequestGpuVoltage(u32 voltage);
static inline Result hocclkIpcRemoveOverride(HocClkModule module)
{

View File

@@ -70,6 +70,7 @@ typedef enum {
HocClkConfigValue_AulaDisplayColorPreset,
HocClkConfigValue_MarikoMiddleFreqs,
HocClkConfigValue_LiveGpuVoltage,
HocClkConfigValue_AutoRAMCPUOverclock,
HocClkConfigValue_AutoRamCpuCpuOCFreq,
@@ -315,6 +316,9 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case HocClkConfigValue_MarikoMiddleFreqs:
return pretty ? "Mariko Middle Clocks" : "mariko_middle_freqs";
case HocClkConfigValue_LiveGpuVoltage:
return pretty ? "GPU Voltage Request" : "gpu_voltage_request";
case HocClkConfigValue_AutoRAMCPUOverclock:
return pretty ? "Auto High RAM CPU OC" : "auto_high_ram_cpu_oc";
case HocClkConfigValue_AutoRamCpuCpuOCFreq:
@@ -568,6 +572,7 @@ static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
case HocClkConfigValue_GPUSchedulingMethod:
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
case HocClkConfigValue_MarikoMiddleFreqs:
case HocClkConfigValue_LiveGpuVoltage:
return 0ULL;
case HocClkConfigValue_RamDisplayUnit:
return (uint64_t)RamDisplayUnit_MHz;
@@ -625,6 +630,7 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case HocClkConfigValue_LiveCpuUv:
case HocClkConfigValue_GPUSchedulingMethod:
case HocClkConfigValue_MarikoMiddleFreqs:
case HocClkConfigValue_LiveGpuVoltage:
case HocClkConfigValue_AutoRAMCPUOverclock:
return (input & 0x1) == input;

View File

@@ -12,9 +12,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
@@ -50,9 +50,9 @@ enum HocClkIpcCmd
HocClkIpcCmd_GetFreqList = 11,
HocClkIpcCmd_SetKipData = 12,
HocClkIpcCmd_GetKipData = 13,
HocClkIpcCmd_RequestGpuVoltage = 14,
};
typedef struct
{
uint64_t tid;

View File

@@ -12,9 +12,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
@@ -166,4 +166,8 @@ Result hocClkIpcGetKipData()
{
u32 temp = 0;
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetKipData, temp);
}
}
Result hocClkIpcRequestGpuVoltage(u32 voltage) {
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_RequestGpuVoltage, voltage);
}

View File

@@ -542,6 +542,15 @@ std::vector<std::string> ConfigInfoStrings(HocClkConfigValue val, bool isMariko,
"- Enabled: Enables GPU scheduling, 96.5% GPU max load",
"Default: Do not override"
};
case HocClkConfigValue_LiveGpuVoltage:
return {
"Adjust GPU voltage on the fly.",
"Warning: unstable settings allowed.",
"Exception: while RAM OC is active, voltage cannot drop below stability minimum.",
"RAM frequency changes overwrite requested voltage.",
"Voltage applies globally across all frequencies.",
"\"Reset\" restores default or RAM-stable minimum."
};
default:
return {};
}

View File

@@ -662,6 +662,9 @@ class ExperimentalSettingsSubMenuGui : public MiscGui {
addConfigToggle(HocClkConfigValue_MarikoMiddleFreqs, nullptr, true);
addConfigToggle(HocClkConfigValue_LiveCpuUv, nullptr);
}
addConfigToggle(HocClkConfigValue_LiveGpuVoltage, "Live GPU Volt Request", false);
std::vector<NamedValue> gpuSchedMethodValues = {
NamedValue("INI", GpuSchedulingOverrideMethod_Ini),
NamedValue("NV Service", GpuSchedulingOverrideMethod_NvService),
@@ -2126,6 +2129,41 @@ class GpuSubmenuGui : public MiscGui {
addConfigButton(HocClkConfigValue_DVFSOffset, "GPU DVFS Offset", ValueRange(0, 12, 1, "", 0), "GPU DVFS Offset", &thresholdsDisabled, {},
dvfsOffset, false);
if (this->configList->values[HocClkConfigValue_LiveGpuVoltage]) {
std::vector<NamedValue> gpuVoltRequestValues = {
NamedValue("Reset", 0),
};
for (u32 mv = IsMariko() ? 420 : 700; mv <= 1050; mv += 5) {
gpuVoltRequestValues.push_back(NamedValue(std::to_string(mv) + "mV", mv));
}
tsl::elm::ListItem *gpuVoltRequestBtn = new tsl::elm::ListItem("GPU Volt Request");
gpuVoltRequestBtn->setTextColor(tsl::Color(120, 235, 255, 255));
gpuVoltRequestBtn->setValue("Request");
gpuVoltRequestBtn->setClickListener([gpuVoltRequestValues](u64 keys) {
if ((keys & HidNpadButton_A) == 0)
return false;
tsl::changeTo<ValueChoiceGui>(
0, ValueRange(0, 0, 1, "", 1), "GPU Volt Request",
[](u32 value) {
Result rc = hocClkIpcRequestGpuVoltage(value);
if (R_FAILED(rc)) {
FatalGui::openWithResultCode("hocClkIpcRequestGpuVoltage", rc);
return false;
}
return true;
},
ValueThresholds(), false, std::map<u32, std::string>{}, gpuVoltRequestValues, false, false);
return true;
});
this->listElement->addItem(gpuVoltRequestBtn);
}
tsl::elm::ListItem *customTableSubmenu = new tsl::elm::ListItem("GPU Voltage Table");
customTableSubmenu->setClickListener([](u64 keys) {
if (keys & HidNpadButton_A) {

View File

@@ -335,9 +335,9 @@ namespace board {
}
/* 800mV on Mariko, 950mV on Erista. */
u32 vmax = GetSocType() == HocClkSocType_Mariko ? 800 : 950;
u32 bootVoltage = GetSocType() == HocClkSocType_Mariko ? 800 : 950;
constexpr u32 GpuVoltageTableOffset = 312;
if (!std::memcmp(&buffer[index + GpuVoltageTableOffset], &vmax, sizeof(vmax))) {
if (!std::memcmp(&buffer[index + GpuVoltageTableOffset], &bootVoltage, sizeof(bootVoltage))) {
std::memcpy(voltData.voltTable, &buffer[index + GpuVoltageTableOffset], sizeof(voltData.voltTable));
voltData.voltTableAddress = base + memoryInfo.addr + GpuVoltageTableOffset + index;
}
@@ -365,18 +365,34 @@ namespace board {
return;
}
void PcvHijackGpuVolts(u32 vmin) {
void PcvHijackGpuVolts(u32 vmin, bool forceOverwrite) {
u32 table[192];
static_assert(sizeof(table) == sizeof(voltData.voltTable), "Invalid gpu voltage table size!");
std::memcpy(table, voltData.voltTable, sizeof(voltData.voltTable));
if (voltData.ramVmin == vmin) {
/* forceOverwrite needs to be handled regardless. */
if (voltData.ramVmin == vmin && !forceOverwrite) {
return;
}
for (u32 i = 0; i < std::size(table); ++i) {
if (table[i] && table[i] <= vmin) {
table[i] = vmin;
/* Skip adjusting table if vmin is zero (voltage reset), unless forced. */
if (vmin != 0 || forceOverwrite) {
/* Only apply forced voltage override if it's safe to do so. */
if (forceOverwrite && vmin < voltData.ramVmin) {
vmin = voltData.ramVmin;
}
if (vmin != 0) {
for (u32 i = 0; i < std::size(table); ++i) {
if (!table[i]) {
continue;
}
const bool raiseVoltageRamOc = table[i] <= vmin && !forceOverwrite;
if (raiseVoltageRamOc || forceOverwrite) {
table[i] = vmin;
}
}
}
}
@@ -388,7 +404,7 @@ namespace board {
Result rc = svcWriteDebugProcessMemory(handle, table, voltData.voltTableAddress, sizeof(table));
if (R_SUCCEEDED(rc)) {
if (R_SUCCEEDED(rc) && !forceOverwrite) {
voltData.ramVmin = vmin;
}

View File

@@ -48,7 +48,7 @@ namespace board {
u32 CalculateTbreak(u32 table);
u32 GetVoltage(HocClkVoltage voltage);
void CacheGpuVoltTable();
void PcvHijackGpuVolts(u32 vmin);
void PcvHijackGpuVolts(u32 vmin, bool forceOverwrite);
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket);
} // namespace board

View File

@@ -235,6 +235,15 @@ namespace ipcService {
return 0;
}
break;
case HocClkIpcCmd_RequestGpuVoltage:
if (r->data.size >= sizeof(u32)) {
u32 voltage;
memcpy(&voltage, r->data.ptr, sizeof(u32));
clockManager::ApplyGpuVoltageRequest(voltage);
return 0;
}
break;
}
return HOCCLK_ERROR(Generic);

View File

@@ -332,6 +332,28 @@ namespace clockManager {
}
}
u32 ClampGpuVoltage(u32 voltage) {
constexpr u32 MaxGpuVoltage = 960;
return std::min(voltage, MaxGpuVoltage);
}
u32 GetCurrentNearestGpuFrequency() {
u32 gpuHz = board::GetHz(HocClkModule_GPU);
u32 maxHz = GetMaxAllowedHz(HocClkModule_GPU, gContext.profile);
return GetNearestHz(HocClkModule_GPU, gpuHz, maxHz);
}
void ApplyGpuVoltageRequest(u32 voltage) {
fileUtils::LogLine("Unclamped voltage request: %u", voltage);
voltage = ClampGpuVoltage(voltage);
fileUtils::LogLine("Actual voltage: %u", voltage);
board::PcvHijackGpuVolts(voltage, true);
/* Update the voltage using the currently nearest valid gpu frequency. */
board::SetHz(HocClkModule_GPU, GetCurrentNearestGpuFrequency());
}
void ApplyGpuDvfs(u32 targetHz) {
s32 dvfsOffset = config::GetConfigValue(HocClkConfigValue_DVFSOffset);
dvfsOffset = std::max(dvfsOffset, -80);
@@ -342,19 +364,14 @@ namespace clockManager {
}
/* Prevent console from combusting if for some reason bad shit happens :P */
vmin = std::min(vmin, 1000u);
/* Get nearest gpu clock; we need this in a second to update the voltage. */
u32 gpuHz = board::GetHz(HocClkModule_GPU);
u32 maxHz = GetMaxAllowedHz(HocClkModule_GPU, gContext.profile);
u32 nearestGpuHz = GetNearestHz(HocClkModule_GPU, gpuHz, maxHz);
vmin = ClampGpuVoltage(vmin);
/* Hijack gpu volt table. */
board::PcvHijackGpuVolts(vmin);
board::PcvHijackGpuVolts(vmin, false);
/* Update gpu frequency to actually use the voltage. */
if (targetHz) {
board::SetHz(HocClkModule_GPU, nearestGpuHz);
board::SetHz(HocClkModule_GPU, GetCurrentNearestGpuFrequency());
} else {
/* If the target frequency is zero, we reset the frequency to ensure it gets updated even without any frequency override. */
board::ResetToStockGpu();
@@ -363,7 +380,7 @@ namespace clockManager {
void DVFSReset() {
if (config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
board::PcvHijackGpuVolts(0); // Reset to vMin
board::PcvHijackGpuVolts(0, false); // Reset to vMin
u32 targetHz = gContext.overrideFreqs[HocClkModule_GPU];
if (!targetHz) {
@@ -515,6 +532,7 @@ namespace clockManager {
if (module == HocClkModule_MEM && targetHz > oldHz && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
ApplyGpuDvfs(targetHz);
}
board::SetHz((HocClkModule)module, nearestHz);
gContext.freqs[module] = nearestHz;
@@ -560,7 +578,7 @@ namespace clockManager {
if (hasChanged) {
board::ResetToStock();
if (config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
board::PcvHijackGpuVolts(0);
board::PcvHijackGpuVolts(0, false);
board::ResetToStockGpu();
}
WaitForNextTick();

View File

@@ -63,6 +63,8 @@ namespace clockManager {
void GetFreqList(HocClkModule module, std::uint32_t *list, std::uint32_t maxCount, std::uint32_t *outCount);
void ApplyGpuVoltageRequest(u32 voltage);
void Tick();
void WaitForNextTick();
} // namespace clockManager