Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC
This commit is contained in:
@@ -33,7 +33,7 @@
|
|||||||
#include "board.hpp"
|
#include "board.hpp"
|
||||||
#include "board_fuse.hpp"
|
#include "board_fuse.hpp"
|
||||||
#include "board_load.hpp"
|
#include "board_load.hpp"
|
||||||
#include "board_ram_oc_dvfs.hpp"
|
#include "board_volt.hpp"
|
||||||
#include "board_misc.hpp"
|
#include "board_misc.hpp"
|
||||||
|
|
||||||
namespace board {
|
namespace board {
|
||||||
@@ -114,7 +114,7 @@ namespace board {
|
|||||||
ASSERT_RESULT_OK(rc, "pmdmntInitialize");
|
ASSERT_RESULT_OK(rc, "pmdmntInitialize");
|
||||||
|
|
||||||
StartGpuLoad(nvCheck, fd);
|
StartGpuLoad(nvCheck, fd);
|
||||||
/* TODO: Add back fan. */
|
|
||||||
StartMiscThread(pwmCheck)
|
StartMiscThread(pwmCheck)
|
||||||
|
|
||||||
batteryInfoInitialize();
|
batteryInfoInitialize();
|
||||||
@@ -140,17 +140,7 @@ namespace board {
|
|||||||
DisplayRefresh_Initialize(&cfg);
|
DisplayRefresh_Initialize(&cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// rc = svcQueryMemoryMapping(&cldvfs, &cldvfs_temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE);
|
CacheDfllData();
|
||||||
// ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)");
|
|
||||||
|
|
||||||
// if (socType == SysClkSocType_Erista) {
|
|
||||||
// cachedEristaUvLowTune0 = *(u32*) (cldvfs + CL_DVFS_TUNE0_0);
|
|
||||||
// cachedEristaUvLowTune1 = *(u32*) (cldvfs + CL_DVFS_TUNE1_0);
|
|
||||||
// } else {
|
|
||||||
// SetHz(SysClkModule_CPU, 1785000000);
|
|
||||||
// cachedMarikoUvHighTune0 = *(u32*) (cldvfs + CL_DVFS_TUNE0_0);
|
|
||||||
// ResetToStockCpu();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Exit() {
|
void Exit() {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include "board_misc.hpp"
|
#include "board_misc.hpp"
|
||||||
|
#include <minIni.h>
|
||||||
|
|
||||||
namespace board {
|
namespace board {
|
||||||
|
|
||||||
@@ -130,4 +131,47 @@ namespace board {
|
|||||||
threadClose(&cpuCore2Thread);
|
threadClose(&cpuCore2Thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr u32 NVschedCtrlEnable = 0x00000601;
|
||||||
|
constexpr u32 NVschedCtrlDisable = 0x00000602;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetGpuSchedulingMode(GpuSchedulingMode mode, GpuSchedulingOverrideMethod method, Result nvCheckSched, u32 fd2) {
|
||||||
|
if (R_FAILED(nvCheckSched) && method == GpuSchedulingOverrideMethod_NvService) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 temp;
|
||||||
|
bool enabled = false;
|
||||||
|
switch (mode) {
|
||||||
|
case GpuSchedulingMode_DoNotOverride: break;
|
||||||
|
case GpuSchedulingMode_Disabled:
|
||||||
|
if (method == GpuSchedulingOverrideMethod_NvService) {
|
||||||
|
nvIoctl(fd2, NVschedCtrlDisable, &temp);
|
||||||
|
} else {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GpuSchedulingMode_Enabled:
|
||||||
|
if (method == GpuSchedulingOverrideMethod_NvService) {
|
||||||
|
nvIoctl(fd2, NVschedCtrlEnable, &temp);
|
||||||
|
} else {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_ENUM_VALID(GpuSchedulingMode, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == GpuSchedulingOverrideMethod_Ini) {
|
||||||
|
constexpr const char *IniPath = "sdmc:/atmosphere/config/system_settings.ini";
|
||||||
|
constexpr const char *Section = "am.gpu";
|
||||||
|
constexpr const char *Key = "gpu_scheduling_enabled";
|
||||||
|
|
||||||
|
const char *value = enabled ? "u8!0x1" : "u8!0x0";
|
||||||
|
|
||||||
|
ini_puts(Section, Key, value, IniPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,5 +32,6 @@ namespace board {
|
|||||||
void StartLoad(Result nvCheck, u32 fd);
|
void StartLoad(Result nvCheck, u32 fd);
|
||||||
void ExitLoad();
|
void ExitLoad();
|
||||||
u32 GetPartLoad();
|
u32 GetPartLoad();
|
||||||
|
void SetGpuSchedulingMode(GpuSchedulingMode mode, GpuSchedulingOverrideMethod method, Result nvCheckSched, u32 fd2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
* wrote this file. As long as you retain this notice you can do whatever you
|
|
||||||
* want with this stuff. If you meet any of us some day, and you think this
|
|
||||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
|
||||||
* --------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sysclk.h>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <nxExt.h>
|
|
||||||
|
|
||||||
namespace board {
|
|
||||||
|
|
||||||
u32 GetPowerMw(SysClkPowerSensor sensor) {
|
|
||||||
switch (sensor) {
|
|
||||||
case SysClkPowerSensor_Now:
|
|
||||||
return max17050PowerNow();
|
|
||||||
case SysClkPowerSensor_Avg:
|
|
||||||
return max17050PowerAvg();
|
|
||||||
default:
|
|
||||||
ASSERT_ENUM_VALID(SysClkPowerSensor, sensor);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
* wrote this file. As long as you retain this notice you can do whatever you
|
|
||||||
* want with this stuff. If you meet any of us some day, and you think this
|
|
||||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
|
||||||
* --------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <sysclk.h>
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
namespace board {
|
|
||||||
|
|
||||||
u32 GetPowerMw(SysClkPowerSensor sensor)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
* wrote this file. As long as you retain this notice you can do whatever you
|
|
||||||
* want with this stuff. If you meet any of us some day, and you think this
|
|
||||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
|
||||||
* --------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <switch.h>
|
|
||||||
#include <memmem.h>
|
|
||||||
#include "../file_utils.h"
|
|
||||||
#include <cstring>
|
|
||||||
#include "board_ram_oc_dvfs.hpp"
|
|
||||||
|
|
||||||
namespace board {
|
|
||||||
|
|
||||||
GpuVoltData voltData = {};
|
|
||||||
|
|
||||||
Handle GetPcvHandle() {
|
|
||||||
constexpr u64 PcvID = 0x10000000000001a;
|
|
||||||
u64 processIDList[80]{};
|
|
||||||
s32 processCount = 0;
|
|
||||||
Handle handle = INVALID_HANDLE;
|
|
||||||
|
|
||||||
DebugEventInfo debugEvent{};
|
|
||||||
|
|
||||||
/* Get all running processes. */
|
|
||||||
Result resultGetProcessList = svcGetProcessList(&processCount, processIDList, std::size(processIDList));
|
|
||||||
if (R_FAILED(resultGetProcessList)) {
|
|
||||||
return INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to find pcv. */
|
|
||||||
for (int i = 0; i < processCount; ++i) {
|
|
||||||
if (handle != INVALID_HANDLE) {
|
|
||||||
svcCloseHandle(handle);
|
|
||||||
handle = INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to debug process, if it fails, try next process. */
|
|
||||||
Result resultSvcDebugProcess = svcDebugActiveProcess(&handle, processIDList[i]);
|
|
||||||
if (R_FAILED(resultSvcDebugProcess)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to get a debug event. */
|
|
||||||
Result resultDebugEvent = svcGetDebugEvent(&debugEvent, handle);
|
|
||||||
if (R_SUCCEEDED(resultDebugEvent)) {
|
|
||||||
if (debugEvent.info.create_process.program_id == PcvID) {
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Failed to get handle. */
|
|
||||||
return INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CacheGpuVoltTable() {
|
|
||||||
UnkRegulator reg = {
|
|
||||||
.voltageMinUV = 600000,
|
|
||||||
.voltageStep = 12500,
|
|
||||||
.voltageMax = 1400000,
|
|
||||||
};
|
|
||||||
|
|
||||||
Handle handle = GetPcvHandle();
|
|
||||||
if (handle == INVALID_HANDLE) {
|
|
||||||
FileUtils::LogLine("[dvfs] Invalid handle!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryInfo memoryInfo = {};
|
|
||||||
u64 address = 0;
|
|
||||||
u32 pageInfo = 0;
|
|
||||||
constexpr u32 PageSize = 0x1000;
|
|
||||||
u8 buffer[PageSize];
|
|
||||||
|
|
||||||
/* Loop until failure. */
|
|
||||||
while (true) {
|
|
||||||
/* Find pcv heap. */
|
|
||||||
while (true) {
|
|
||||||
Result resultProcessMemory = svcQueryDebugProcessMemory(&memoryInfo, &pageInfo, handle, address);
|
|
||||||
address = memoryInfo.addr + memoryInfo.size;
|
|
||||||
|
|
||||||
if (R_FAILED(resultProcessMemory) || !address) {
|
|
||||||
svcCloseHandle(handle);
|
|
||||||
FileUtils::LogLine("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
|
|
||||||
handle = INVALID_HANDLE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast<char>(memoryInfo.type) == 0x4) {
|
|
||||||
/* Found valid memory. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u64 base = 0; base < memoryInfo.size; base += PageSize) {
|
|
||||||
u32 memorySize = std::min(memoryInfo.size, static_cast<u64>(PageSize));
|
|
||||||
if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, base + memoryInfo.addr, memorySize))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 *resultFindReg = static_cast<u8 *>(memmem_impl(buffer, sizeof(buffer), ®, sizeof(reg)));
|
|
||||||
u32 index = resultFindReg - buffer;
|
|
||||||
|
|
||||||
if (!resultFindReg) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assuming mariko. */
|
|
||||||
const u32 vmax = 800;
|
|
||||||
constexpr u32 VoltageTableOffset = 312;
|
|
||||||
if (!std::memcmp(&buffer[index + VoltageTableOffset], &vmax, sizeof(vmax))) {
|
|
||||||
std::memcpy(voltData.voltTable, &buffer[index + VoltageTableOffset], sizeof(voltData.voltTable));
|
|
||||||
voltData.voltTableAddress = base + memoryInfo.addr + VoltageTableOffset + index;
|
|
||||||
}
|
|
||||||
|
|
||||||
svcCloseHandle(handle);
|
|
||||||
handle = INVALID_HANDLE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
svcCloseHandle(handle);
|
|
||||||
handle = INVALID_HANDLE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PcvHijackGpuVolts(u32 vmin) {
|
|
||||||
u32 table[192];
|
|
||||||
static_assert(sizeof(table) == sizeof(voltData.voltTable));
|
|
||||||
std::memcpy(table, voltData.voltTable, sizeof(voltData.voltTable));
|
|
||||||
|
|
||||||
if (voltData.ramVmin == vmin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u32 i = 0; i < std::size(table); ++i) {
|
|
||||||
if (table[i] && table[i] <= vmin) {
|
|
||||||
table[i] = vmin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle handle = GetPcvHandle();
|
|
||||||
if (handle == INVALID_HANDLE) {
|
|
||||||
FileUtils::LogLine("Invalid handle!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result rc = svcWriteDebugProcessMemory(handle, table, voltData.dvfsAddress, sizeof(table));
|
|
||||||
|
|
||||||
if (R_SUCCEEDED(rc)) {
|
|
||||||
voltData.ramVmin = vmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
svcCloseHandle(handle);
|
|
||||||
FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetMinimumVmin(u32 freqMhz, u32 bracket) {
|
|
||||||
static const u32 ramTable[][22] = {
|
|
||||||
{ 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, },
|
|
||||||
{ 2300, 2366, 2433, 2466, 2533, 2566, 2633, 2700, 2733, 2800, 2833, 2900, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3266, },
|
|
||||||
{ 2433, 2466, 2533, 2600, 2666, 2733, 2766, 2800, 2833, 2866, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3300, 3333, 3366, },
|
|
||||||
{ 2500, 2533, 2600, 2633, 2666, 2733, 2800, 2866, 2900, 2966, 3033, 3100, 3166, 3200, 3233, 3266, 3300, 3333, 3366, 3400, 3400, 3400, },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const u32 gpuVoltArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, };
|
|
||||||
|
|
||||||
if (freqMhz <= 1600) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u32 i = 0; std::size(gpuDvfsArray) < 22; ++i) {
|
|
||||||
if (freqMhz <= ramTable[bracket][i]) {
|
|
||||||
return gpuVoltArray[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return gpuVoltArray[std::size(gpuVoltArray) - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* 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>
|
|
||||||
* wrote this file. As long as you retain this notice you can do whatever you
|
|
||||||
* want with this stuff. If you meet any of us some day, and you think this
|
|
||||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
|
||||||
* --------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
namespace board {
|
|
||||||
|
|
||||||
struct GpuVoltData {
|
|
||||||
u32 voltTable[6][32] = {};
|
|
||||||
u64 voltTableAddress;
|
|
||||||
u32 ramVmin;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* TODO: Find out what component this actually targets. */
|
|
||||||
struct UnkRegulator {
|
|
||||||
u32 voltageMinUV;
|
|
||||||
u32 voltageStep;
|
|
||||||
u32 voltageMax;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CacheGpuVoltTable();
|
|
||||||
void PcvHijackGpuVolts(u32 vmin);
|
|
||||||
u32 GetMinimumVmin(u32 freqMhz, u32 bracket);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -58,4 +58,17 @@ namespace board {
|
|||||||
return std::max(0, millis);
|
return std::max(0, millis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetPowerMw(SysClkPowerSensor sensor) {
|
||||||
|
switch (sensor) {
|
||||||
|
case SysClkPowerSensor_Now:
|
||||||
|
return max17050PowerNow();
|
||||||
|
case SysClkPowerSensor_Avg:
|
||||||
|
return max17050PowerAvg();
|
||||||
|
default:
|
||||||
|
ASSERT_ENUM_VALID(SysClkPowerSensor, sensor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,5 +31,6 @@
|
|||||||
namespace board {
|
namespace board {
|
||||||
|
|
||||||
u32 GetTemperatureMilli(SysClkThermalSensor sensor);
|
u32 GetTemperatureMilli(SysClkThermalSensor sensor);
|
||||||
|
u32 GetPowerMw(SysClkPowerSensor sensor);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -26,10 +26,160 @@
|
|||||||
|
|
||||||
#include <switch.h>
|
#include <switch.h>
|
||||||
#include <sysclk.h>
|
#include <sysclk.h>
|
||||||
|
#include <memmem.h>
|
||||||
|
#include <registers.h>
|
||||||
|
#include <cstring>
|
||||||
#include "board.hpp"
|
#include "board.hpp"
|
||||||
|
#include "board_freq.hpp"
|
||||||
|
#include "board_volt.hpp"
|
||||||
|
#include "../file_utils.h"
|
||||||
|
|
||||||
namespace board {
|
namespace board {
|
||||||
|
|
||||||
|
GpuVoltData voltData = {};
|
||||||
|
u64 cldvfs;
|
||||||
|
CpuDfllData cachedTune;
|
||||||
|
|
||||||
|
/* ... This really needs some cleanup... */
|
||||||
|
namespace {
|
||||||
|
struct EristaCpuUvEntry {
|
||||||
|
u32 tune0;
|
||||||
|
u32 tune1;
|
||||||
|
} EristaCpuUvEntry;
|
||||||
|
|
||||||
|
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 CacheDfllData() {
|
||||||
|
u64 temp;
|
||||||
|
rc = svcQueryMemoryMapping(&cldvfs, &temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE);
|
||||||
|
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)");
|
||||||
|
|
||||||
|
if (GetSocType() == SysClkSocType_Erista) {
|
||||||
|
cachedTune.tune0Low = *static_cast<u32 *>(cldvfs + CL_DVFS_TUNE0_0);
|
||||||
|
cachedTune.tune1Low = *static_cast<u32 *>(cldvfs + CL_DVFS_TUNE1_0);
|
||||||
|
} else {
|
||||||
|
SetHz(SysClkModule_CPU, 1785000000);
|
||||||
|
cachedTune.tune0High = *static_cast<u32 *>(cldvfs + CL_DVFS_TUNE0_0);
|
||||||
|
ResetToStockCpu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: clean up this code. */
|
||||||
|
void SetDfllTunings(u32 levelLow, u32 levelHigh, u32 tbreakPoint) {
|
||||||
|
u32* tune0_ptr = static_cast<u32 *>(cldvfs + CL_DVFS_TUNE0_0);
|
||||||
|
u32* tune1_ptr = static_cast<u32 *>(cldvfs + CL_DVFS_TUNE1_0);
|
||||||
|
if (GetSocType() == SysClkSocType_Mariko) {
|
||||||
|
if (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 (GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
|
||||||
|
*tune0_ptr = 0xCFFF;
|
||||||
|
*tune1_ptr = 0xFF072201;
|
||||||
|
return;
|
||||||
|
} else if (GetHz(SysClkModule_CPU) >= tbreakPoint || (!levelHigh)) {
|
||||||
|
*tune0_ptr = cachedTune.tune0High; // per console?
|
||||||
|
*tune1_ptr = 0xFFF7FF3F;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
|
||||||
|
*tune0_ptr = cachedTune.tune0Low; // I think each erista has a different tune0/tune1?
|
||||||
|
*tune1_ptr = cachedTune.tune1Low;
|
||||||
|
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 CalculateTbreak(u32 table) {
|
||||||
|
if (GetSocType() == SysClkSocType_Erista) {
|
||||||
|
return 1581000000;
|
||||||
|
} else {
|
||||||
|
switch (table) {
|
||||||
|
case 1 ... 2:
|
||||||
|
case 4:
|
||||||
|
return 1581000000;
|
||||||
|
case 3:
|
||||||
|
return 1683000000;
|
||||||
|
default:
|
||||||
|
return 1581000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switch Power domains (max77620):
|
* Switch Power domains (max77620):
|
||||||
* Name | Usage | uV step | uV min | uV default | uV max | Init
|
* Name | Usage | uV step | uV min | uV default | uV max | Init
|
||||||
@@ -135,4 +285,169 @@ namespace board {
|
|||||||
return out > 0 ? out : 0;
|
return out > 0 ? out : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle GetPcvHandle() {
|
||||||
|
constexpr u64 PcvID = 0x10000000000001a;
|
||||||
|
u64 processIDList[80]{};
|
||||||
|
s32 processCount = 0;
|
||||||
|
Handle handle = INVALID_HANDLE;
|
||||||
|
|
||||||
|
DebugEventInfo debugEvent{};
|
||||||
|
|
||||||
|
/* Get all running processes. */
|
||||||
|
Result resultGetProcessList = svcGetProcessList(&processCount, processIDList, std::size(processIDList));
|
||||||
|
if (R_FAILED(resultGetProcessList)) {
|
||||||
|
return INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to find pcv. */
|
||||||
|
for (int i = 0; i < processCount; ++i) {
|
||||||
|
if (handle != INVALID_HANDLE) {
|
||||||
|
svcCloseHandle(handle);
|
||||||
|
handle = INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to debug process, if it fails, try next process. */
|
||||||
|
Result resultSvcDebugProcess = svcDebugActiveProcess(&handle, processIDList[i]);
|
||||||
|
if (R_FAILED(resultSvcDebugProcess)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to get a debug event. */
|
||||||
|
Result resultDebugEvent = svcGetDebugEvent(&debugEvent, handle);
|
||||||
|
if (R_SUCCEEDED(resultDebugEvent)) {
|
||||||
|
if (debugEvent.info.create_process.program_id == PcvID) {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Failed to get handle. */
|
||||||
|
return INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CacheGpuVoltTable() {
|
||||||
|
UnkRegulator reg = {
|
||||||
|
.voltageMinUV = 600000,
|
||||||
|
.voltageStep = 12500,
|
||||||
|
.voltageMax = 1400000,
|
||||||
|
};
|
||||||
|
|
||||||
|
Handle handle = GetPcvHandle();
|
||||||
|
if (handle == INVALID_HANDLE) {
|
||||||
|
FileUtils::LogLine("[dvfs] Invalid handle!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryInfo memoryInfo = {};
|
||||||
|
u64 address = 0;
|
||||||
|
u32 pageInfo = 0;
|
||||||
|
constexpr u32 PageSize = 0x1000;
|
||||||
|
u8 buffer[PageSize];
|
||||||
|
|
||||||
|
/* Loop until failure. */
|
||||||
|
while (true) {
|
||||||
|
/* Find pcv heap. */
|
||||||
|
while (true) {
|
||||||
|
Result resultProcessMemory = svcQueryDebugProcessMemory(&memoryInfo, &pageInfo, handle, address);
|
||||||
|
address = memoryInfo.addr + memoryInfo.size;
|
||||||
|
|
||||||
|
if (R_FAILED(resultProcessMemory) || !address) {
|
||||||
|
svcCloseHandle(handle);
|
||||||
|
FileUtils::LogLine("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
|
||||||
|
handle = INVALID_HANDLE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast<char>(memoryInfo.type) == 0x4) {
|
||||||
|
/* Found valid memory. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u64 base = 0; base < memoryInfo.size; base += PageSize) {
|
||||||
|
u32 memorySize = std::min(memoryInfo.size, static_cast<u64>(PageSize));
|
||||||
|
if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, base + memoryInfo.addr, memorySize))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *resultFindReg = static_cast<u8 *>(memmem_impl(buffer, sizeof(buffer), ®, sizeof(reg)));
|
||||||
|
u32 index = resultFindReg - buffer;
|
||||||
|
|
||||||
|
if (!resultFindReg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assuming mariko. */
|
||||||
|
const u32 vmax = 800;
|
||||||
|
constexpr u32 VoltageTableOffset = 312;
|
||||||
|
if (!std::memcmp(&buffer[index + VoltageTableOffset], &vmax, sizeof(vmax))) {
|
||||||
|
std::memcpy(voltData.voltTable, &buffer[index + VoltageTableOffset], sizeof(voltData.voltTable));
|
||||||
|
voltData.voltTableAddress = base + memoryInfo.addr + VoltageTableOffset + index;
|
||||||
|
}
|
||||||
|
|
||||||
|
svcCloseHandle(handle);
|
||||||
|
handle = INVALID_HANDLE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svcCloseHandle(handle);
|
||||||
|
handle = INVALID_HANDLE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PcvHijackGpuVolts(u32 vmin) {
|
||||||
|
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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < std::size(table); ++i) {
|
||||||
|
if (table[i] && table[i] <= vmin) {
|
||||||
|
table[i] = vmin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle handle = GetPcvHandle();
|
||||||
|
if (handle == INVALID_HANDLE) {
|
||||||
|
FileUtils::LogLine("Invalid handle!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result rc = svcWriteDebugProcessMemory(handle, table, voltData.dvfsAddress, sizeof(table));
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
voltData.ramVmin = vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
svcCloseHandle(handle);
|
||||||
|
FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket) {
|
||||||
|
static const u32 ramTable[][22] = {
|
||||||
|
{ 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, },
|
||||||
|
{ 2300, 2366, 2433, 2466, 2533, 2566, 2633, 2700, 2733, 2800, 2833, 2900, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3266, },
|
||||||
|
{ 2433, 2466, 2533, 2600, 2666, 2733, 2766, 2800, 2833, 2866, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3300, 3333, 3366, },
|
||||||
|
{ 2500, 2533, 2600, 2633, 2666, 2733, 2800, 2866, 2900, 2966, 3033, 3100, 3166, 3200, 3233, 3266, 3300, 3333, 3366, 3400, 3400, 3400, },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u32 gpuVoltArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, };
|
||||||
|
|
||||||
|
if (freqMhz <= 1600) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < std::size(gpuDvfsArray); ++i) {
|
||||||
|
if (freqMhz <= ramTable[bracket][i]) {
|
||||||
|
return gpuVoltArray[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpuVoltArray[std::size(gpuVoltArray) - 1];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,34 @@
|
|||||||
|
|
||||||
namespace board {
|
namespace board {
|
||||||
|
|
||||||
|
struct GpuVoltData {
|
||||||
|
u32 voltTable[6][32] = {};
|
||||||
|
u64 voltTableAddress;
|
||||||
|
u32 ramVmin;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO: Find out what component this actually targets. */
|
||||||
|
struct UnkRegulator {
|
||||||
|
u32 voltageMinUV;
|
||||||
|
u32 voltageStep;
|
||||||
|
u32 voltageMax;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CpuDfllData {
|
||||||
|
u32 tune0Low;
|
||||||
|
u32 tune0High;
|
||||||
|
u32 tune1Low;
|
||||||
|
u32 tune1High;
|
||||||
|
// u32 tune_high_min_millivolts;
|
||||||
|
// u32 tune_high_margin_millivolts;
|
||||||
|
// u64 dvco_calibration_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
void CacheDfllData();
|
||||||
|
u32 CalculateTbreak();
|
||||||
u32 GetVoltage(HocClkVoltage voltage);
|
u32 GetVoltage(HocClkVoltage voltage);
|
||||||
|
void CacheGpuVoltTable();
|
||||||
|
void PcvHijackGpuVolts(u32 vmin);
|
||||||
|
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -566,7 +566,7 @@ void ClockManager::VRRThread(void* arg) {
|
|||||||
if(++tick > 50) {
|
if(++tick > 50) {
|
||||||
Board::SetHz(HorizonOCModule_Display, maxDisplay);
|
Board::SetHz(HorizonOCModule_Display, maxDisplay);
|
||||||
tick = 0;
|
tick = 0;
|
||||||
svcSleepThread(25'000'000);
|
svcSleepThread(50'000'000);
|
||||||
}
|
}
|
||||||
|
|
||||||
svcSleepThread(POLL_NS);
|
svcSleepThread(POLL_NS);
|
||||||
|
|||||||
Reference in New Issue
Block a user