add on the fly gpu volt table
This commit is contained in:
@@ -281,6 +281,16 @@ namespace board {
|
||||
return INVALID_HANDLE;
|
||||
}
|
||||
|
||||
bool ValidateHandle(Handle handle) {
|
||||
if (handle == INVALID_HANDLE) {
|
||||
fileUtils::LogLine("Invalid handle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CacheGpuVoltTable() {
|
||||
// Likely CPU regulator?
|
||||
UnkRegulator reg = {
|
||||
@@ -290,8 +300,7 @@ namespace board {
|
||||
};
|
||||
|
||||
Handle handle = GetPcvHandle();
|
||||
if (handle == INVALID_HANDLE) {
|
||||
fileUtils::LogLine("[dvfs] Invalid handle!");
|
||||
if (!ValidateHandle(handle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,13 +347,13 @@ namespace board {
|
||||
u32 bootVoltage = GetSocType() == HocClkSocType_Mariko ? 800 : 950;
|
||||
constexpr u32 GpuVoltageTableOffset = 312;
|
||||
if (!std::memcmp(&buffer[index + GpuVoltageTableOffset], &bootVoltage, sizeof(bootVoltage))) {
|
||||
std::memcpy(voltData.voltTable, &buffer[index + GpuVoltageTableOffset], sizeof(voltData.voltTable));
|
||||
std::memcpy(voltData.voltTableStock, &buffer[index + GpuVoltageTableOffset], sizeof(voltData.voltTableStock));
|
||||
std::memcpy(voltData.voltTableLive, voltData.voltTableStock, sizeof(voltData.voltTableStock));
|
||||
voltData.voltTableAddress = base + memoryInfo.addr + GpuVoltageTableOffset + index;
|
||||
}
|
||||
|
||||
constexpr u32 CpuVoltageTableOffset = 0xB8;
|
||||
std::memcpy(cpuVoltTable, &buffer[index + CpuVoltageTableOffset], sizeof(cpuVoltTable)); // TODO: verify the CPU table
|
||||
|
||||
svcCloseHandle(handle);
|
||||
handle = INVALID_HANDLE;
|
||||
|
||||
@@ -353,8 +362,8 @@ namespace board {
|
||||
fileUtils::LogLine("[dvfs] cpu volt %d: %u mV", i, cpuVoltTable[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)std::size(voltData.voltTable); ++i) {
|
||||
fileUtils::LogLine("[dvfs] gpu volt %d: %u mV", i, voltData.voltTable[0][i]);
|
||||
for (int i = 0; i < (int)std::size(voltData.voltTableStock); ++i) {
|
||||
fileUtils::LogLine("[dvfs] gpu volt %d: %u mV", i, voltData.voltTableStock[0][i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -365,53 +374,88 @@ namespace board {
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
/* forceOverwrite needs to be handled regardless. */
|
||||
if (voltData.ramVmin == vmin && !forceOverwrite) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Result ReadGpuVoltTable(u32 *table) {
|
||||
// Handle handle = GetPcvHandle();
|
||||
// if (!ValidateHandle(handle)) {
|
||||
// return 1;
|
||||
// }
|
||||
//
|
||||
// Result rc = svcReadDebugProcessMemory(table, handle, voltData.voltTableAddress, sizeof(voltData.voltTableStock));
|
||||
// svcCloseHandle(handle);
|
||||
// return rc;
|
||||
// }
|
||||
|
||||
Result WriteGpuVoltTable(u32 *table) {
|
||||
Handle handle = GetPcvHandle();
|
||||
if (handle == INVALID_HANDLE) {
|
||||
fileUtils::LogLine("Invalid handle!");
|
||||
if (!ValidateHandle(handle)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Result rc = svcWriteDebugProcessMemory(handle, table, voltData.voltTableAddress, sizeof(voltData.voltTableStock));
|
||||
svcCloseHandle(handle);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void PcvHijackGpuVolts(u32 vmin) {
|
||||
u32 table[GpuVoltTableSize];
|
||||
std::memcpy(table, voltData.voltTableLive, sizeof(voltData.voltTableLive));
|
||||
|
||||
if (voltData.ramVmin == vmin) {
|
||||
return;
|
||||
}
|
||||
|
||||
Result rc = svcWriteDebugProcessMemory(handle, table, voltData.voltTableAddress, sizeof(table));
|
||||
|
||||
if (R_SUCCEEDED(rc) && !forceOverwrite) {
|
||||
voltData.ramVmin = vmin;
|
||||
for (u32 i = 0; i < std::size(table); ++i) {
|
||||
if (table[i] && table[i] <= vmin) {
|
||||
table[i] = vmin;
|
||||
}
|
||||
}
|
||||
|
||||
svcCloseHandle(handle);
|
||||
ASSERT_RESULT_OK(WriteGpuVoltTable(table), "WriteGpuVoltTable");
|
||||
voltData.ramVmin = vmin;
|
||||
fileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
|
||||
}
|
||||
|
||||
u32 GetGpuFreqIndex(u32 hz) {
|
||||
u32 list[HOCCLK_FREQ_LIST_MAX];
|
||||
u32 count;
|
||||
|
||||
GetFreqList(HocClkModule_GPU, list, HOCCLK_FREQ_LIST_MAX, &count);
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
if (list[i] == hz) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return HOCCLK_FREQ_LIST_MAX;
|
||||
}
|
||||
|
||||
void PcvHijackGpuFrequency(u32 voltage, u32 hz) {
|
||||
u32 freqIndex = GetGpuFreqIndex(hz);
|
||||
if (freqIndex == HOCCLK_FREQ_LIST_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool voltageReset = (voltage == 0);
|
||||
|
||||
for (u32 i = 0; i < GpuVoltTableTempCount; ++i) {
|
||||
if (voltageReset) {
|
||||
voltage = voltData.voltTableStock[i][freqIndex];
|
||||
}
|
||||
|
||||
voltData.voltTableLive[i][freqIndex] = voltage;
|
||||
}
|
||||
|
||||
if (voltageReset) {
|
||||
voltage = voltData.voltTableStock[GpuVoltTableTempCount - 2 /* 70C */][freqIndex];
|
||||
}
|
||||
|
||||
if (voltage >= voltData.ramVmin) {
|
||||
/* Only update the table if it's safe to do so. */
|
||||
ASSERT_RESULT_OK(WriteGpuVoltTable(reinterpret_cast<u32 *>(voltData.voltTableLive)), "WriteGpuVoltTable");
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket) {
|
||||
u32 baseVolt = 800;
|
||||
if (GetSocType() == HocClkSocType_Mariko) {
|
||||
|
||||
@@ -22,11 +22,19 @@
|
||||
|
||||
namespace board {
|
||||
|
||||
constexpr u32 GpuVoltTableTempCount = 6;
|
||||
constexpr u32 GpuVoltFreqTempCount = 32;
|
||||
constexpr size_t GpuVoltTableSize = GpuVoltTableTempCount * GpuVoltFreqTempCount;
|
||||
|
||||
struct GpuVoltData {
|
||||
u32 voltTable[6][32] = {};
|
||||
u32 voltTableStock[GpuVoltTableTempCount][GpuVoltFreqTempCount] = {};
|
||||
u32 voltTableRamOc[GpuVoltTableTempCount][GpuVoltFreqTempCount] = {};
|
||||
u32 voltTableLive[GpuVoltTableTempCount][GpuVoltFreqTempCount] = {};
|
||||
|
||||
u64 voltTableAddress;
|
||||
u32 ramVmin;
|
||||
};
|
||||
|
||||
struct UnkRegulator {
|
||||
u32 voltageMin;
|
||||
u32 voltageStep;
|
||||
@@ -48,7 +56,8 @@ namespace board {
|
||||
u32 CalculateTbreak(u32 table);
|
||||
u32 GetVoltage(HocClkVoltage voltage);
|
||||
void CacheGpuVoltTable();
|
||||
void PcvHijackGpuVolts(u32 vmin, bool forceOverwrite);
|
||||
void PcvHijackGpuVolts(u32 vmin);
|
||||
void PcvHijackGpuFrequency(u32 voltage, u32 hz);
|
||||
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket);
|
||||
|
||||
} // namespace board
|
||||
|
||||
@@ -141,6 +141,11 @@ namespace ipcService {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result RequestGpuVoltage(HocClkIpc_RequestGpuVoltage_Args *args) {
|
||||
clockManager::ApplyGpuFreqVoltRequest(args->voltage, args->hz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result ServiceHandlerFunc(void *arg, const IpcServerRequest *r, u8 *out_data, size_t *out_dataSize) {
|
||||
(void)arg;
|
||||
switch (r->data.cmdId) {
|
||||
@@ -237,11 +242,8 @@ namespace ipcService {
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_RequestGpuVoltage:
|
||||
if (r->data.size >= sizeof(u32)) {
|
||||
u32 voltage;
|
||||
memcpy(&voltage, r->data.ptr, sizeof(u32));
|
||||
clockManager::ApplyGpuVoltageRequest(voltage);
|
||||
return 0;
|
||||
if (r->data.size >= sizeof(HocClkIpc_RequestGpuVoltage_Args)) {
|
||||
return RequestGpuVoltage((HocClkIpc_RequestGpuVoltage_Args *)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -333,8 +333,8 @@ namespace clockManager {
|
||||
}
|
||||
|
||||
u32 ClampGpuVoltage(u32 voltage) {
|
||||
constexpr u32 MaxGpuVoltage = 960;
|
||||
return std::min(voltage, MaxGpuVoltage);
|
||||
static const u32 maxGpuVoltage = board::GetSocType() == HocClkSocType_Mariko ? 960 : 995;
|
||||
return std::min(voltage, maxGpuVoltage);
|
||||
}
|
||||
|
||||
u32 GetCurrentNearestGpuFrequency() {
|
||||
@@ -343,15 +343,17 @@ namespace clockManager {
|
||||
return GetNearestHz(HocClkModule_GPU, gpuHz, maxHz);
|
||||
}
|
||||
|
||||
void ApplyGpuVoltageRequest(u32 voltage) {
|
||||
fileUtils::LogLine("Unclamped voltage request: %u", voltage);
|
||||
void ApplyGpuFreqVoltRequest(u32 voltage, u32 hz) {
|
||||
voltage = ClampGpuVoltage(voltage);
|
||||
fileUtils::LogLine("Actual voltage: %u", voltage);
|
||||
|
||||
board::PcvHijackGpuVolts(voltage, true);
|
||||
u32 currentFreq = GetCurrentNearestGpuFrequency();
|
||||
/* If not freq was provided, use the current freq. */
|
||||
if (hz == 0) {
|
||||
hz = currentFreq;
|
||||
}
|
||||
|
||||
board::PcvHijackGpuFrequency(voltage, hz);
|
||||
/* Update the voltage using the currently nearest valid gpu frequency. */
|
||||
board::SetHz(HocClkModule_GPU, GetCurrentNearestGpuFrequency());
|
||||
board::SetHz(HocClkModule_GPU, currentFreq);
|
||||
}
|
||||
|
||||
void ApplyGpuDvfs(u32 targetHz) {
|
||||
@@ -367,7 +369,7 @@ namespace clockManager {
|
||||
vmin = ClampGpuVoltage(vmin);
|
||||
|
||||
/* Hijack gpu volt table. */
|
||||
board::PcvHijackGpuVolts(vmin, false);
|
||||
board::PcvHijackGpuVolts(vmin);
|
||||
|
||||
/* Update gpu frequency to actually use the voltage. */
|
||||
if (targetHz) {
|
||||
@@ -380,7 +382,7 @@ namespace clockManager {
|
||||
|
||||
void DVFSReset() {
|
||||
if (config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
||||
board::PcvHijackGpuVolts(0, false); // Reset to vMin
|
||||
board::PcvHijackGpuVolts(0); // Reset to vMin
|
||||
|
||||
u32 targetHz = gContext.overrideFreqs[HocClkModule_GPU];
|
||||
if (!targetHz) {
|
||||
@@ -578,7 +580,7 @@ namespace clockManager {
|
||||
if (hasChanged) {
|
||||
board::ResetToStock();
|
||||
if (config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
||||
board::PcvHijackGpuVolts(0, false);
|
||||
board::PcvHijackGpuVolts(0);
|
||||
board::ResetToStockGpu();
|
||||
}
|
||||
WaitForNextTick();
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace clockManager {
|
||||
|
||||
void GetFreqList(HocClkModule module, std::uint32_t *list, std::uint32_t maxCount, std::uint32_t *outCount);
|
||||
|
||||
void ApplyGpuVoltageRequest(u32 voltage);
|
||||
void ApplyGpuFreqVoltRequest(u32 voltage, u32 hz);
|
||||
|
||||
void Tick();
|
||||
void WaitForNextTick();
|
||||
|
||||
Reference in New Issue
Block a user