Updated: Sys-clk-OC, Loader, System_settings
- Sys-clk-OC
- Major cleanup in clock_manager, preparing to add basic Erista support.
- Added an experimental CPU & GPU frequency governor.
- Known issue:
- Occasional stuttering is expected: GPU load% metric PMU_GET_GPU_LOAD does not reflect real utilization precisely. Use another metric, some interpolation algo or add min frequency option for improvement.
- Loader
- Addressed an issue for Erista variants: Boot with unmodified Fusee or Hekate, nn::pcv::EmcDvfsPeriodicCompHandler will fail with rc 0x8C5. Fixed by removing 40.8 MHz while retaining 1600.0 MHz MEM table -- however, this means user has to use modified sys-clk.
This commit is contained in:
@@ -59,27 +59,32 @@ ClockManager::ClockManager()
|
||||
this->oc = new SysClkOcExtra;
|
||||
this->oc->systemCoreBoostCPU = false;
|
||||
this->oc->allowUnsafeFreq = false;
|
||||
this->oc->syncReverseNXMode = false;
|
||||
this->oc->governor = false;
|
||||
this->oc->realProfile = SysClkProfile_Handheld;
|
||||
this->oc->reverseNXToolMode = ReverseNX_NotFound;
|
||||
this->oc->reverseNXRTMode = ReverseNX_NotFound;
|
||||
this->oc->maxMEMFreq = 0;
|
||||
this->oc->boostCPUFreq = 0;
|
||||
|
||||
this->rnxSync = new ReverseNXSync;
|
||||
this->governor = new Governor;
|
||||
}
|
||||
|
||||
ClockManager::~ClockManager()
|
||||
{
|
||||
delete this->config;
|
||||
delete this->context;
|
||||
delete this->governor;
|
||||
delete this->rnxSync;
|
||||
delete this->oc;
|
||||
delete this->context;
|
||||
delete this->config;
|
||||
}
|
||||
|
||||
bool ClockManager::IsCpuBoostMode()
|
||||
{
|
||||
std::uint32_t confId = this->context->perfConfId;
|
||||
bool isCpuBoostMode = (confId == 0x92220009 || confId == 0x9222000A);
|
||||
if (isCpuBoostMode && !this->oc->boostCPUFreq)
|
||||
bool isCpuBoostMode = apmExtIsBoostMode(confId, false);
|
||||
if (isCpuBoostMode && !this->oc->boostCPUFreq) {
|
||||
this->oc->boostCPUFreq = std::max(this->context->freqs[SysClkModule_CPU], 1785'000'000U);
|
||||
this->governor->SetCPUBoostHz(this->oc->boostCPUFreq);
|
||||
}
|
||||
return isCpuBoostMode;
|
||||
}
|
||||
|
||||
@@ -107,7 +112,8 @@ uint32_t ClockManager::GetHz(SysClkModule module)
|
||||
hz = this->config->GetAutoClockHz(SYSCLK_GLOBAL_PROFILE_TID, module, this->context->profile);
|
||||
|
||||
/* Return pre-set hz */
|
||||
if (!hz && this->oc->syncReverseNXMode && GetReverseNXMode())
|
||||
ReverseNXMode mode;
|
||||
if (!hz && (mode = this->rnxSync->GetMode()))
|
||||
{
|
||||
switch (module)
|
||||
{
|
||||
@@ -115,12 +121,12 @@ uint32_t ClockManager::GetHz(SysClkModule module)
|
||||
hz = 1020'000'000;
|
||||
break;
|
||||
case SysClkModule_GPU:
|
||||
hz = (GetReverseNXMode() == ReverseNX_Docked ||
|
||||
hz = (mode == ReverseNX_Docked ||
|
||||
this->oc->realProfile == SysClkProfile_Docked) ?
|
||||
768'000'000 : 460'800'000;
|
||||
break;
|
||||
case SysClkModule_MEM:
|
||||
hz = (GetReverseNXMode() == ReverseNX_Docked ||
|
||||
hz = (mode == ReverseNX_Docked ||
|
||||
this->oc->realProfile == SysClkProfile_Docked) ?
|
||||
MAX_MEM_CLOCK : 1600'000'000;
|
||||
break;
|
||||
@@ -172,17 +178,23 @@ void ClockManager::Tick()
|
||||
{
|
||||
uint32_t hz = GetHz((SysClkModule)module);
|
||||
|
||||
if (this->oc->governor) {
|
||||
this->governor->SetMaxHz(hz, (SysClkModule)module);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hz && hz != this->context->freqs[module])
|
||||
{
|
||||
// Skip setting CPU or GPU clocks in CpuBoostMode if CPU <= boostCPUFreq or GPU >= 76.8MHz
|
||||
if (IsCpuBoostMode() && ((module == SysClkModule_CPU && hz <= this->oc->boostCPUFreq) || module == SysClkModule_GPU))
|
||||
{
|
||||
continue;
|
||||
bool skipBoost = IsCpuBoostMode() && ((module == SysClkModule_CPU && hz <= this->oc->boostCPUFreq) || module == SysClkModule_GPU);
|
||||
if (!skipBoost) {
|
||||
FileUtils::LogLine("[mgr] %s clock set : %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
Clocks::SetHz((SysClkModule)module, hz);
|
||||
uint32_t hz_now = Clocks::GetCurrentHz((SysClkModule)module);
|
||||
if (hz != hz_now)
|
||||
FileUtils::LogLine("[mgr] Cannot set %s clock to %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
this->context->freqs[module] = hz_now;
|
||||
}
|
||||
|
||||
FileUtils::LogLine("[mgr] %s clock set : %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
Clocks::SetHz((SysClkModule)module, hz);
|
||||
this->context->freqs[module] = hz;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,162 +204,48 @@ void ClockManager::WaitForNextTick()
|
||||
{
|
||||
/* Self-check system core (#3) usage via idleticks at intervals (Not enabled at higher CPU freq or without charger) */
|
||||
uint64_t tickWaitTimeMs = this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs);
|
||||
uint64_t tickWaitTimeNs = tickWaitTimeMs * 1000000ULL;
|
||||
|
||||
bool isAutoBoostEnabled = this->GetConfig()->GetConfigValue(SysClkConfigValue_AutoCPUBoost);
|
||||
if ( isAutoBoostEnabled
|
||||
&& this->oc->realProfile != SysClkProfile_Handheld
|
||||
&& this->context->enabled
|
||||
&& this->context->freqs[SysClkModule_CPU] <= this->oc->boostCPUFreq)
|
||||
{
|
||||
uint64_t systemCoreIdleTickPrev = 0, systemCoreIdleTickNext = 0;
|
||||
svcGetInfo(&systemCoreIdleTickPrev, InfoType_IdleTickCount, INVALID_HANDLE, 3);
|
||||
svcSleepThread(tickWaitTimeNs);
|
||||
svcGetInfo(&systemCoreIdleTickNext, InfoType_IdleTickCount, INVALID_HANDLE, 3);
|
||||
if (this->oc->governor) {
|
||||
svcSleepThread(tickWaitTimeMs * 1000'000ULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Convert idletick to free% */
|
||||
/* If CPU core usage is 0%, then idletick = 19'200'000 per sec */
|
||||
uint64_t systemCoreIdleTick = systemCoreIdleTickNext - systemCoreIdleTickPrev;
|
||||
uint64_t freeIdleTick = 19'200 * tickWaitTimeMs;
|
||||
uint8_t freePerc = systemCoreIdleTick / (freeIdleTick / 100);
|
||||
bool boostOK =
|
||||
this->GetConfig()->GetConfigValue(SysClkConfigValue_AutoCPUBoost) &&
|
||||
this->context->enabled &&
|
||||
this->oc->realProfile != SysClkProfile_Handheld &&
|
||||
this->context->freqs[SysClkModule_CPU] <= this->oc->boostCPUFreq;
|
||||
|
||||
constexpr uint8_t systemCoreBoostFreeThreshold = 5;
|
||||
if (boostOK) {
|
||||
uint64_t core3Util = CpuCoreUtil(3, tickWaitTimeMs).Get();
|
||||
bool lastBoost = this->oc->systemCoreBoostCPU;
|
||||
constexpr uint8_t BOOST_THRESHOLD = 95;
|
||||
this->oc->systemCoreBoostCPU = (core3Util >= BOOST_THRESHOLD);
|
||||
|
||||
bool systemCoreBoostCPUPrevState = this->oc->systemCoreBoostCPU;
|
||||
this->oc->systemCoreBoostCPU = (freePerc <= systemCoreBoostFreeThreshold);
|
||||
|
||||
if (systemCoreBoostCPUPrevState && !this->oc->systemCoreBoostCPU)
|
||||
{
|
||||
if (lastBoost && !this->oc->systemCoreBoostCPU)
|
||||
Clocks::SetHz(SysClkModule_CPU, GetHz(SysClkModule_CPU));
|
||||
}
|
||||
else if (!systemCoreBoostCPUPrevState && this->oc->systemCoreBoostCPU)
|
||||
{
|
||||
|
||||
if (!lastBoost && this->oc->systemCoreBoostCPU)
|
||||
Clocks::SetHz(SysClkModule_CPU, this->oc->boostCPUFreq);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this->oc->systemCoreBoostCPU) {
|
||||
this->oc->systemCoreBoostCPU = false;
|
||||
Clocks::SetHz(SysClkModule_CPU, GetHz(SysClkModule_CPU));
|
||||
}
|
||||
svcSleepThread(tickWaitTimeNs);
|
||||
}
|
||||
}
|
||||
|
||||
SysClkProfile ClockManager::ReverseNXProfileHandler()
|
||||
{
|
||||
switch (GetReverseNXMode())
|
||||
{
|
||||
case ReverseNX_Docked:
|
||||
return SysClkProfile_Docked;
|
||||
case ReverseNX_Handheld:
|
||||
return (this->oc->realProfile == SysClkProfile_Docked) ?
|
||||
SysClkProfile_HandheldChargingOfficial : this->oc->realProfile;
|
||||
default:
|
||||
return this->oc->realProfile;
|
||||
}
|
||||
}
|
||||
|
||||
ReverseNXMode ClockManager::ReverseNXFileHandler(const char* filePath)
|
||||
{
|
||||
FILE *readFile;
|
||||
readFile = fopen(filePath, "rb");
|
||||
|
||||
if (!readFile)
|
||||
return ReverseNX_NotFound;
|
||||
|
||||
uint64_t magicDocked = 0xD65F03C0320003E0;
|
||||
uint64_t magicHandheld = 0xD65F03C052A00000;
|
||||
|
||||
uint64_t readBuffer = 0;
|
||||
fread(&readBuffer, 1, sizeof(readBuffer), readFile);
|
||||
fclose(readFile);
|
||||
|
||||
if (R_SUCCEEDED(memcmp(&readBuffer, &magicDocked, sizeof(readBuffer))))
|
||||
return ReverseNX_Docked;
|
||||
|
||||
if (R_SUCCEEDED(memcmp(&readBuffer, &magicHandheld, sizeof(readBuffer))))
|
||||
return ReverseNX_Handheld;
|
||||
|
||||
return ReverseNX_NotFound;
|
||||
}
|
||||
|
||||
ReverseNXMode ClockManager::GetReverseNXToolMode()
|
||||
{
|
||||
bool shouldCheckReverseNXTool = FileUtils::ExistReverseNXTool();
|
||||
if (!shouldCheckReverseNXTool)
|
||||
return ReverseNX_NotFound;
|
||||
|
||||
ReverseNXMode getMode = ReverseNX_NotFound;
|
||||
if (this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
{
|
||||
const char asmFileName[] = "_ZN2nn2oe18GetPerformanceModeEv.asm64"; // Checking one asm64 file is enough
|
||||
char asmFilePath[128];
|
||||
|
||||
/* Check per-game patch */
|
||||
snprintf(asmFilePath, sizeof(asmFilePath), "/SaltySD/patches/%016lX/%s", this->context->applicationId, asmFileName);
|
||||
getMode = ReverseNXFileHandler(asmFilePath);
|
||||
|
||||
if (!getMode)
|
||||
{
|
||||
/* Check global patch */
|
||||
snprintf(asmFilePath, sizeof(asmFilePath), "/SaltySD/patches/%s", asmFileName);
|
||||
getMode = ReverseNXFileHandler(asmFilePath);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return getMode;
|
||||
}
|
||||
|
||||
ReverseNXMode ClockManager::GetReverseNXMode()
|
||||
{
|
||||
if (this->oc->reverseNXRTMode)
|
||||
return this->oc->reverseNXRTMode;
|
||||
return this->oc->reverseNXToolMode;
|
||||
}
|
||||
|
||||
void ClockManager::ChargingHandler()
|
||||
{
|
||||
smInitialize();
|
||||
psmInitialize();
|
||||
ChargeInfo* chargeInfoField = new ChargeInfo;
|
||||
Service* session = psmGetServiceSession();
|
||||
serviceDispatchOut(session, GetBatteryChargeInfoFields, *(chargeInfoField));
|
||||
|
||||
bool fastChargingState = chargeInfoField->ChargeCurrentLimit > 768;
|
||||
bool fastChargingConfig = !(this->GetConfig()->GetConfigValue(SysClkConfigValue_DisableFastCharging));
|
||||
if (fastChargingState != fastChargingConfig)
|
||||
serviceDispatch(session, fastChargingConfig ? EnableFastBatteryCharging : DisableFastBatteryCharging);
|
||||
|
||||
bool isChargerConnected = (chargeInfoField->ChargerType != ChargerType_None);
|
||||
if (isChargerConnected)
|
||||
{
|
||||
u32 chargeNow = 0;
|
||||
if (R_SUCCEEDED(psmGetBatteryChargePercentage(&chargeNow)))
|
||||
{
|
||||
bool isCharging = ((chargeInfoField->unk_x14 >> 8) & 1);
|
||||
u32 chargeLimit = this->GetConfig()->GetConfigValue(SysClkConfigValue_ChargingLimitPercentage);
|
||||
if (isCharging && chargeLimit < chargeNow) {
|
||||
serviceDispatch(session, DisableBatteryCharging);
|
||||
}
|
||||
if (!isCharging && chargeLimit > chargeNow) {
|
||||
serviceDispatch(session, EnableBatteryCharging);
|
||||
}
|
||||
}
|
||||
if (this->oc->systemCoreBoostCPU) {
|
||||
this->oc->systemCoreBoostCPU = false;
|
||||
Clocks::SetHz(SysClkModule_CPU, GetHz(SysClkModule_CPU));
|
||||
}
|
||||
|
||||
delete chargeInfoField;
|
||||
psmExit();
|
||||
smExit();
|
||||
svcSleepThread(tickWaitTimeMs * 1000'000ULL);
|
||||
}
|
||||
|
||||
bool ClockManager::RefreshContext()
|
||||
{
|
||||
ChargingHandler();
|
||||
bool fastChargingEnabled = !(this->GetConfig()->GetConfigValue(SysClkConfigValue_DisableFastCharging));
|
||||
uint32_t chargingLimit = this->GetConfig()->GetConfigValue(SysClkConfigValue_ChargingLimitPercentage);
|
||||
PsmExt::ChargingHandler(fastChargingEnabled, chargingLimit);
|
||||
|
||||
bool hasChanged = this->config->Refresh();
|
||||
this->oc->syncReverseNXMode = this->GetConfig()->GetConfigValue(SysClkConfigValue_SyncReverseNXMode);
|
||||
this->rnxSync->ToggleSync(this->GetConfig()->GetConfigValue(SysClkConfigValue_SyncReverseNXMode));
|
||||
this->oc->allowUnsafeFreq = this->GetConfig()->GetConfigValue(SysClkConfigValue_AllowUnsafeFrequencies);
|
||||
|
||||
bool enabled = this->GetConfig()->Enabled();
|
||||
@@ -358,6 +256,18 @@ bool ClockManager::RefreshContext()
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
bool governor = this->GetConfig()->GetConfigValue(SysClkConfigValue_GovernorExperimental);
|
||||
if (governor != this->oc->governor)
|
||||
{
|
||||
this->oc->governor = governor;
|
||||
FileUtils::LogLine("[mgr] Governor status: %s", governor ? "enabled" : "disabled");
|
||||
if (governor)
|
||||
this->governor->Start();
|
||||
else
|
||||
this->governor->Stop();
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId();
|
||||
if (applicationId != this->context->applicationId)
|
||||
{
|
||||
@@ -367,8 +277,7 @@ bool ClockManager::RefreshContext()
|
||||
|
||||
/* Clear ReverseNX state */
|
||||
this->GetConfig()->SetReverseNXRTMode(ReverseNX_NotFound);
|
||||
this->oc->reverseNXRTMode = ReverseNX_NotFound;
|
||||
this->oc->reverseNXToolMode = GetReverseNXToolMode();
|
||||
this->rnxSync->Reset(applicationId);
|
||||
}
|
||||
|
||||
SysClkProfile profile = Clocks::GetCurrentProfile();
|
||||
@@ -388,17 +297,17 @@ bool ClockManager::RefreshContext()
|
||||
if (this->context->perfConfId != confId)
|
||||
{
|
||||
this->context->perfConfId = confId;
|
||||
this->governor->SetPerfConf(confId);
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
this->oc->reverseNXRTMode = this->GetConfig()->GetReverseNXRTMode();
|
||||
SysClkProfile currentProfile = this->context->profile;
|
||||
SysClkProfile expectedProfile = this->oc->syncReverseNXMode ?
|
||||
ReverseNXProfileHandler() : this->oc->realProfile;
|
||||
this->context->profile = expectedProfile;
|
||||
if (currentProfile != expectedProfile)
|
||||
this->rnxSync->SetRTMode(this->GetConfig()->GetReverseNXRTMode());
|
||||
SysClkProfile current = this->context->profile;
|
||||
SysClkProfile expected = this->rnxSync->GetProfile(this->oc->realProfile);
|
||||
this->context->profile = expected;
|
||||
if (current != expected)
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
@@ -414,9 +323,11 @@ bool ClockManager::RefreshContext()
|
||||
|
||||
if (hz != 0 && hz != this->context->freqs[module])
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s clock change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
this->context->freqs[module] = hz;
|
||||
hasChanged = true;
|
||||
if (!this->oc->governor) {
|
||||
FileUtils::LogLine("[mgr] %s clock change: %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
hz = this->GetConfig()->GetOverrideHz((SysClkModule)module);
|
||||
@@ -424,7 +335,7 @@ bool ClockManager::RefreshContext()
|
||||
{
|
||||
if(hz)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s override change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
FileUtils::LogLine("[mgr] %s override change: %u.%u MHz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "clocks.h"
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
|
||||
#include "oc_extra.h"
|
||||
|
||||
class ClockManager
|
||||
{
|
||||
public:
|
||||
@@ -32,64 +34,6 @@ class ClockManager
|
||||
SysClkContext GetCurrentContext();
|
||||
Config* GetConfig();
|
||||
|
||||
typedef enum {
|
||||
PDCtrler_NewPDO = 1, //Received new Power Data Object
|
||||
PDCtrler_NoPD = 2, //No Power Delivery source is detected
|
||||
PDCtrler_AcceptedRDO = 3 //Received and accepted Request Data Object
|
||||
} ChargeInfoPDCtrler; //BM92T series
|
||||
|
||||
typedef enum {
|
||||
PowerRole_Sink = 1,
|
||||
PowerRole_Source = 2
|
||||
} ChargeInfoPowerRole;
|
||||
|
||||
typedef enum {
|
||||
ChargerType_None = 0,
|
||||
ChargerType_PD = 1,
|
||||
ChargerType_TypeC_1500mA = 2,
|
||||
ChargerType_TypeC_3000mA = 3,
|
||||
ChargerType_DCP = 4,
|
||||
ChargerType_CDP = 5,
|
||||
ChargerType_SDP = 6,
|
||||
ChargerType_Apple_500mA = 7,
|
||||
ChargerType_Apple_1000mA = 8,
|
||||
ChargerType_Apple_2000mA = 9
|
||||
} ChargeInfoChargerType;
|
||||
|
||||
typedef enum {
|
||||
Flags_NoHub = BIT(0), //If hub is disconnected
|
||||
Flags_Rail = BIT(8), //At least one Joy-con is charging from rail
|
||||
Flags_SPDSRC = BIT(12), //OTG
|
||||
Flags_ACC = BIT(16) //Accessory
|
||||
} ChargeInfoFlags;
|
||||
|
||||
typedef struct {
|
||||
int32_t InputCurrentLimit; //Input (Sink) current limit in mA
|
||||
int32_t VBUSCurrentLimit; //Output (Source/VBUS/OTG) current limit in mA
|
||||
int32_t ChargeCurrentLimit; //Battery charging current limit in mA (512mA when Docked, 768mA when BatteryTemperature < 17.0 C)
|
||||
int32_t ChargeVoltageLimit; //Battery charging voltage limit in mV (3952mV when BatteryTemperature >= 51.0 C)
|
||||
int32_t unk_x10; //Possibly an emum, getting the same value as PowerRole in all tested cases
|
||||
int32_t unk_x14; //Possibly flags
|
||||
ChargeInfoPDCtrler PDCtrlerState; //Power Delivery Controller State
|
||||
int32_t BatteryTemperature; //Battery temperature in milli C
|
||||
int32_t RawBatteryCharge; //Raw battery charged capacity per cent-mille (i.e. 100% = 100000 pcm)
|
||||
int32_t VoltageAvg; //Voltage avg in mV (more in Notes)
|
||||
int32_t BatteryAge; //Battery age (capacity full / capacity design) per cent-mille (i.e. 100% = 100000 pcm)
|
||||
ChargeInfoPowerRole PowerRole;
|
||||
ChargeInfoChargerType ChargerType;
|
||||
int32_t ChargerVoltageLimit; //Charger and external device voltage limit in mV
|
||||
int32_t ChargerCurrentLimit; //Charger and external device current limit in mA
|
||||
ChargeInfoFlags Flags; //Unknown flags
|
||||
} ChargeInfo;
|
||||
|
||||
typedef enum {
|
||||
EnableBatteryCharging = 2,
|
||||
DisableBatteryCharging = 3,
|
||||
EnableFastBatteryCharging = 10,
|
||||
DisableFastBatteryCharging = 11,
|
||||
GetBatteryChargeInfoFields = 17,
|
||||
} IPsmServerCmd;
|
||||
|
||||
protected:
|
||||
ClockManager();
|
||||
virtual ~ClockManager();
|
||||
@@ -105,16 +49,10 @@ class ClockManager
|
||||
std::uint64_t lastCsvWriteNs;
|
||||
|
||||
SysClkOcExtra *oc;
|
||||
ReverseNXSync *rnxSync;
|
||||
Governor *governor;
|
||||
|
||||
bool IsCpuBoostMode();
|
||||
|
||||
uint32_t GetHz(SysClkModule);
|
||||
|
||||
SysClkProfile ReverseNXProfileHandler();
|
||||
ReverseNXMode ReverseNXFileHandler(const char*);
|
||||
ReverseNXMode GetReverseNXToolMode();
|
||||
ReverseNXMode GetReverseNXMode();
|
||||
|
||||
void ChargingHandler();
|
||||
|
||||
};
|
||||
|
||||
@@ -158,29 +158,49 @@ PcvModuleId Clocks::GetPcvModuleId(SysClkModule sysclkModule)
|
||||
return pcvModuleId;
|
||||
}
|
||||
|
||||
SysClkApmConfiguration* Clocks::GetEmbeddedApmConfig(uint32_t confId)
|
||||
{
|
||||
SysClkApmConfiguration* apmConfiguration = NULL;
|
||||
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++)
|
||||
{
|
||||
if(sysclk_g_apm_configurations[i].id == confId)
|
||||
{
|
||||
apmConfiguration = &sysclk_g_apm_configurations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!apmConfiguration)
|
||||
{
|
||||
ERROR_THROW("Unknown apm configuration: %x", confId);
|
||||
}
|
||||
return apmConfiguration;
|
||||
}
|
||||
|
||||
uint32_t Clocks::GetStockClock(SysClkApmConfiguration* apm, SysClkModule module)
|
||||
{
|
||||
switch (module) {
|
||||
case SysClkModule_CPU:
|
||||
return apm->cpu_hz;
|
||||
case SysClkModule_GPU:
|
||||
return apm->gpu_hz;
|
||||
case SysClkModule_MEM:
|
||||
return apm->mem_hz;
|
||||
default:
|
||||
ERROR_THROW("Unknown SysClkModule: %x", module);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Clocks::ResetToStock(unsigned int module)
|
||||
{
|
||||
Result rc = 0;
|
||||
if(hosversionAtLeast(9,0,0))
|
||||
{
|
||||
std::uint32_t confId = 0;
|
||||
rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
|
||||
SysClkApmConfiguration* apmConfiguration = NULL;
|
||||
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++)
|
||||
{
|
||||
if(sysclk_g_apm_configurations[i].id == confId)
|
||||
{
|
||||
apmConfiguration = &sysclk_g_apm_configurations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!apmConfiguration)
|
||||
{
|
||||
ERROR_THROW("Unknown apm configuration: %x", confId);
|
||||
}
|
||||
SysClkApmConfiguration* apmConfiguration = GetEmbeddedApmConfig(confId);
|
||||
|
||||
if (module == SysClkModule_EnumMax || module == SysClkModule_CPU)
|
||||
{
|
||||
@@ -197,6 +217,7 @@ void Clocks::ResetToStock(unsigned int module)
|
||||
}
|
||||
else
|
||||
{
|
||||
Result rc = 0;
|
||||
std::uint32_t mode = 0;
|
||||
rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
@@ -20,6 +20,8 @@ class Clocks
|
||||
public:
|
||||
static void Exit();
|
||||
static void Initialize();
|
||||
static SysClkApmConfiguration* GetEmbeddedApmConfig(uint32_t confId);
|
||||
static uint32_t GetStockClock(SysClkApmConfiguration* apm, SysClkModule module);
|
||||
static void ResetToStock(unsigned int module = SysClkModule_EnumMax);
|
||||
static SysClkProfile GetCurrentProfile();
|
||||
static std::uint32_t GetCurrentHz(SysClkModule module);
|
||||
|
||||
@@ -183,12 +183,12 @@ bool Config::SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bo
|
||||
uint8_t numProfiles = 0;
|
||||
|
||||
// String pointer array passed to ini
|
||||
char* iniKeys[SysClkProfile_EnumMax * SysClkModule_EnumMax + 1];
|
||||
char* iniValues[SysClkProfile_EnumMax * SysClkModule_EnumMax + 1];
|
||||
char* iniKeys[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) + 1];
|
||||
char* iniValues[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) + 1];
|
||||
|
||||
// Char arrays to build strings
|
||||
char keysStr[SysClkProfile_EnumMax * SysClkModule_EnumMax * 0x40];
|
||||
char valuesStr[SysClkProfile_EnumMax * SysClkModule_EnumMax * 0x10];
|
||||
char keysStr[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) * 0x40];
|
||||
char valuesStr[static_cast<int>(SysClkProfile_EnumMax) * static_cast<int>(SysClkModule_EnumMax) * 0x10];
|
||||
char section[17] = {0};
|
||||
|
||||
// Iteration pointers
|
||||
|
||||
@@ -18,7 +18,6 @@ static LockableMutex g_log_mutex;
|
||||
static LockableMutex g_csv_mutex;
|
||||
static std::atomic_bool g_has_initialized = false;
|
||||
static bool g_log_enabled = false;
|
||||
static bool g_reversenx_tool_exist = false;
|
||||
static std::uint64_t g_last_flag_check = 0;
|
||||
|
||||
extern "C" void __libnx_init_time(void);
|
||||
@@ -131,22 +130,6 @@ void FileUtils::RefreshFlags(bool force)
|
||||
g_last_flag_check = now;
|
||||
}
|
||||
|
||||
void FileUtils::InitCheckFlags()
|
||||
{
|
||||
FILE *file;
|
||||
file = fopen(FILE_SALTYNX_PATH, "r");
|
||||
if (file)
|
||||
{
|
||||
g_reversenx_tool_exist = true;
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
bool FileUtils::ExistReverseNXTool()
|
||||
{
|
||||
return g_reversenx_tool_exist;
|
||||
}
|
||||
|
||||
void FileUtils::InitializeAsync()
|
||||
{
|
||||
Thread initThread = {0};
|
||||
@@ -179,7 +162,6 @@ Result FileUtils::Initialize()
|
||||
if (R_SUCCEEDED(rc))
|
||||
{
|
||||
FileUtils::RefreshFlags(true);
|
||||
FileUtils::InitCheckFlags();
|
||||
g_has_initialized = true;
|
||||
FileUtils::LogLine("=== " TARGET " " TARGET_VERSION " ===");
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#define FILE_CONTEXT_CSV_PATH FILE_CONFIG_DIR "/context.csv"
|
||||
#define FILE_LOG_FLAG_PATH FILE_CONFIG_DIR "/log.flag"
|
||||
#define FILE_LOG_FILE_PATH FILE_CONFIG_DIR "/log.txt"
|
||||
#define FILE_SALTYNX_PATH "/atmosphere/contents/0000000000534C56/flags/boot2.flag" // Just check for SaltyNX boot flag
|
||||
|
||||
class FileUtils
|
||||
{
|
||||
@@ -37,5 +36,4 @@ class FileUtils
|
||||
static void WriteContextToCsv(const SysClkContext* context);
|
||||
protected:
|
||||
static void RefreshFlags(bool force);
|
||||
static void InitCheckFlags();
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "process_management.h"
|
||||
#include "clock_manager.h"
|
||||
#include "ipc_service.h"
|
||||
#include "oc_extra.h"
|
||||
|
||||
#define INNER_HEAP_SIZE 0x30000
|
||||
|
||||
@@ -27,13 +28,18 @@ extern "C"
|
||||
{
|
||||
extern std::uint32_t __start__;
|
||||
|
||||
std::uint32_t __nx_applet_type = AppletType_None;
|
||||
//set applet type to use nvdrv* service
|
||||
std::uint32_t __nx_applet_type = AppletType_SystemApplication;
|
||||
TimeServiceType __nx_time_service_type = TimeServiceType_System;
|
||||
std::uint32_t __nx_fs_num_sessions = 1;
|
||||
|
||||
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
|
||||
char nx_inner_heap[INNER_HEAP_SIZE];
|
||||
|
||||
// set transfermem size to 32kib as [Fizeau](https://github.com/averne/Fizeau/)
|
||||
// or LibnxError_OutOfMemory
|
||||
u32 __nx_nv_transfermem_size = 0x8000;
|
||||
|
||||
void __libnx_initheap(void)
|
||||
{
|
||||
void *addr = nx_inner_heap;
|
||||
|
||||
368
Source/sys-clk-OC/sysmodule/src/oc_extra.cpp
Normal file
368
Source/sys-clk-OC/sysmodule/src/oc_extra.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
#include "oc_extra.h"
|
||||
|
||||
SysClkProfile ReverseNXSync::GetProfile(SysClkProfile real) {
|
||||
switch (this->GetMode()) {
|
||||
case ReverseNX_Docked:
|
||||
return SysClkProfile_Docked;
|
||||
case ReverseNX_Handheld:
|
||||
if (real == SysClkProfile_Docked)
|
||||
return SysClkProfile_HandheldChargingOfficial;
|
||||
default:
|
||||
return real;
|
||||
}
|
||||
}
|
||||
|
||||
ReverseNXMode ReverseNXSync::GetMode() {
|
||||
if (!this->m_sync_enabled)
|
||||
return ReverseNX_NotFound;
|
||||
if (this->m_rt_mode)
|
||||
return this->m_rt_mode;
|
||||
return this->m_tool_mode;
|
||||
}
|
||||
|
||||
bool ReverseNXSync::CheckToolEnabled() {
|
||||
FILE *fp = fopen("/atmosphere/contents/0000000000534C56/flags/boot2.flag", "r");
|
||||
if (fp) {
|
||||
this->m_tool_enabled = true;
|
||||
fclose(fp);
|
||||
} else {
|
||||
this->m_tool_enabled = false;
|
||||
}
|
||||
return this->m_tool_enabled;
|
||||
}
|
||||
|
||||
ReverseNXMode ReverseNXSync::GetToolModeFromPatch(const char* patch_path) {
|
||||
constexpr uint32_t DOCKED_MAGIC = 0x320003E0;
|
||||
constexpr uint32_t HANDHELD_MAGIC = 0x52A00000;
|
||||
FILE *fp = fopen(patch_path, "rb");
|
||||
if (fp) {
|
||||
uint32_t buf = 0;
|
||||
fread(&buf, sizeof(buf), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (buf == DOCKED_MAGIC)
|
||||
return ReverseNX_Docked;
|
||||
if (buf == HANDHELD_MAGIC)
|
||||
return ReverseNX_Handheld;
|
||||
}
|
||||
|
||||
return ReverseNX_NotFound;
|
||||
}
|
||||
|
||||
ReverseNXMode ReverseNXSync::RecheckToolMode() {
|
||||
ReverseNXMode mode = ReverseNX_NotFound;
|
||||
if (this->m_tool_enabled) {
|
||||
const char* fileName = "_ZN2nn2oe16GetOperationModeEv.asm64"; // or _ZN2nn2oe18GetPerformanceModeEv.asm64
|
||||
const char* filePath = new char[72];
|
||||
/* Check per-game patch */
|
||||
snprintf((char*)filePath, 72, "/SaltySD/patches/%016lX/%s", this->m_app_id, fileName);
|
||||
mode = this->GetToolModeFromPatch(filePath);
|
||||
if (!mode) {
|
||||
/* Check global patch */
|
||||
snprintf((char*)filePath, 72, "/SaltySD/patches/%s", fileName);
|
||||
mode = this->GetToolModeFromPatch(filePath);
|
||||
}
|
||||
delete[] filePath;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
void PsmExt::ChargingHandler(bool fastChargingEnabled, uint32_t chargingLimit) {
|
||||
PsmChargeInfo* info = new PsmChargeInfo;
|
||||
Service* session = psmGetServiceSession();
|
||||
serviceDispatchOut(session, Psm_GetBatteryChargeInfoFields, *info);
|
||||
|
||||
if (PsmIsFastChargingEnabled(info) != fastChargingEnabled)
|
||||
serviceDispatch(session, fastChargingEnabled ? Psm_EnableFastBatteryCharging : Psm_DisableFastBatteryCharging);
|
||||
|
||||
if (PsmIsChargerConnected(info)) {
|
||||
u32 chargeNow = 0;
|
||||
if (R_SUCCEEDED(psmGetBatteryChargePercentage(&chargeNow))) {
|
||||
bool isCharging = PsmIsCharging(info);
|
||||
if (isCharging && chargingLimit < chargeNow)
|
||||
serviceDispatch(session, Psm_DisableBatteryCharging);
|
||||
if (!isCharging && chargingLimit > chargeNow)
|
||||
serviceDispatch(session, Psm_EnableBatteryCharging);
|
||||
}
|
||||
}
|
||||
|
||||
delete info;
|
||||
}
|
||||
|
||||
void Governor::Start() {
|
||||
m_stop_threads = false;
|
||||
svcSleepThread(8 * TICK_TIME_MAIN_NS);
|
||||
Result rc = 0;
|
||||
|
||||
for (int core = 0; core < CORE_NUMS; core++) {
|
||||
if (m_t_cpuworker[core].handle)
|
||||
continue;
|
||||
s_CoreContext* s = InitCoreContext(&m_cpu_core_ctx[core], this, core);
|
||||
rc = threadCreate(&m_t_cpuworker[core], &CheckCpuUtilWorker, (void*)s, NULL, 0x1000, 0x20, core);
|
||||
if (rc) {
|
||||
ERROR_THROW("Cannot create thread m_t_cpuworker[%d]: %u", core, rc);
|
||||
return;
|
||||
}
|
||||
rc = threadStart(&m_t_cpuworker[core]);
|
||||
if (rc) {
|
||||
ERROR_THROW("Cannot start thread m_t_cpuworker[%d]: %u", core, rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
rc = threadCreate(&m_t_main, &Main, (void*)this, NULL, 0x1000, 0x3F, 3);
|
||||
if (rc) {
|
||||
ERROR_THROW("Cannot create thread m_t_main: %u", rc);
|
||||
return;
|
||||
}
|
||||
rc = threadStart(&m_t_main);
|
||||
if (rc) {
|
||||
ERROR_THROW("Cannot start thread m_t_main: %u", rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Governor::Stop() {
|
||||
m_stop_threads = true;
|
||||
svcSleepThread(8 * TICK_TIME_MAIN_NS);
|
||||
|
||||
threadWaitForExit(&m_t_main);
|
||||
threadClose(&m_t_main);
|
||||
|
||||
for (int core = 0; core < CORE_NUMS; core++) {
|
||||
threadWaitForExit(&m_t_cpuworker[core]);
|
||||
threadClose(&m_t_cpuworker[core]);
|
||||
}
|
||||
}
|
||||
|
||||
void Governor::SetMaxHz(uint32_t max_hz, SysClkModule module) {
|
||||
if (!max_hz) // Fallback to apm configuration
|
||||
max_hz = Clocks::GetStockClock(m_apm_conf, (SysClkModule)module);
|
||||
|
||||
switch (module) {
|
||||
case SysClkModule_CPU:
|
||||
m_cpu_freq.idx_max_hz = FindIndex(&m_cpu_freq, max_hz);
|
||||
break;
|
||||
case SysClkModule_GPU:
|
||||
m_gpu_freq.idx_boost_hz = m_gpu_freq.idx_max_hz = FindIndex(&m_gpu_freq, max_hz);
|
||||
break;
|
||||
case SysClkModule_MEM:
|
||||
m_mem_freq = max_hz;
|
||||
Clocks::SetHz(SysClkModule_MEM, max_hz);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Governor::SetPerfConf(uint32_t id) {
|
||||
m_perf_conf_id = id;
|
||||
m_apm_conf = Clocks::GetEmbeddedApmConfig(id);
|
||||
}
|
||||
|
||||
uint32_t Governor::FindIndex(s_Freq* f, uint32_t hz) {
|
||||
uint32_t idx = 0, hz_in_list;
|
||||
while ((hz_in_list = f->hz_list[idx]) != 0) {
|
||||
if (hz == hz_in_list)
|
||||
return idx;
|
||||
idx++;
|
||||
}
|
||||
ERROR_THROW("[mgr] Cannot find hz: %lu", hz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Governor::TargetRamp(s_Freq* f, FREQ_RAMP_DIRECTION dir) {
|
||||
uint8_t idx_old = f->idx_target_hz;
|
||||
|
||||
switch (dir) {
|
||||
case RAMP_UP:
|
||||
f->idx_target_hz++;
|
||||
if (f->idx_target_hz > f->idx_max_hz)
|
||||
f->idx_target_hz = f->idx_max_hz;
|
||||
break;
|
||||
case RAMP_DOWN:
|
||||
if (f->idx_target_hz > 0)
|
||||
f->idx_target_hz--;
|
||||
if (f->idx_target_hz < f->idx_min_hz)
|
||||
f->idx_target_hz = f->idx_min_hz;
|
||||
break;
|
||||
case RAMP_MAX:
|
||||
f->idx_target_hz = f->idx_max_hz;
|
||||
break;
|
||||
case RAMP_MIN:
|
||||
f->idx_target_hz = f->idx_min_hz;
|
||||
break;
|
||||
case RAMP_BOOST:
|
||||
f->idx_target_hz = f->idx_boost_hz;
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t idx_new = f->idx_target_hz;
|
||||
bool changed = idx_old != idx_new;
|
||||
return changed;
|
||||
}
|
||||
|
||||
void Governor::SetHz(s_Freq* f) {
|
||||
uint32_t hz = f->hz_list[f->idx_target_hz];
|
||||
if (hz)
|
||||
Clocks::SetHz(f->module, hz);
|
||||
}
|
||||
|
||||
void Governor::SetBoostHz(s_Freq* f) {
|
||||
f->idx_target_hz = f->idx_boost_hz;
|
||||
if (f->module == SysClkModule_CPU && f->idx_max_hz > f->idx_boost_hz)
|
||||
f->idx_target_hz = f->idx_max_hz;
|
||||
SetHz(f);
|
||||
}
|
||||
|
||||
Governor::s_CoreContext* Governor::InitCoreContext(
|
||||
s_CoreContext* context, Governor* self, int64_t id
|
||||
) {
|
||||
memset(reinterpret_cast<void*>(context), 0, sizeof(s_CoreContext));
|
||||
context->self = self;
|
||||
context->id = id;
|
||||
return context;
|
||||
}
|
||||
|
||||
void Governor::CheckCpuUtilWorker(void* args) {
|
||||
s_CoreContext* s = static_cast<s_CoreContext*>(args);
|
||||
int64_t coreid = s->id;
|
||||
Governor* self = s->self;
|
||||
|
||||
bool isSystemCore = (coreid == CORE_NUMS - 1);
|
||||
if (isSystemCore)
|
||||
self->CheckCpuUtilWorkerSysCore();
|
||||
else
|
||||
self->CheckCpuUtilWorkerAppCore(coreid);
|
||||
}
|
||||
|
||||
void Governor::CheckCpuUtilWorkerAppCore(int64_t coreid) {
|
||||
constexpr uint64_t STUCK_TICKS = 2;
|
||||
s_Queue<uint64_t> q;
|
||||
while (!m_stop_threads) {
|
||||
bool isBusy = m_core3_stuck_cnt > STUCK_TICKS * (CORE_NUMS - 1);
|
||||
if (isBusy) {
|
||||
m_core3_stuck_cnt = 0;
|
||||
SetBoostHz(&m_cpu_freq);
|
||||
svcSleepThread(STUCK_TICKS * TICK_TIME_CPU_NS);
|
||||
} else {
|
||||
m_core3_stuck_cnt++;
|
||||
}
|
||||
|
||||
uint64_t load = CpuCoreUtil(coreid, TICK_TIME_CPU_MS).Get();
|
||||
q.PopAndPush(load);
|
||||
m_cpu_core_ctx[coreid].util = q.GetAvg();
|
||||
}
|
||||
}
|
||||
|
||||
void Governor::CheckCpuUtilWorkerSysCore() {
|
||||
s_Queue<uint64_t> q;
|
||||
int64_t coreid = CORE_NUMS - 1;
|
||||
while (!m_stop_threads) {
|
||||
uint64_t load = CpuCoreUtil(coreid, TICK_TIME_CPU_MS).Get();
|
||||
q.PopAndPush(load);
|
||||
m_cpu_core_ctx[coreid].util = q.GetAvg() * 7 / 8; // Adjusted, Multipler: 0.875
|
||||
}
|
||||
}
|
||||
|
||||
void Governor::Main(void* args) {
|
||||
Governor* self = static_cast<Governor*>(args);
|
||||
uint32_t nvgpu_field = self->m_nvgpu_field;
|
||||
|
||||
auto GetCpuUtil = [self]() {
|
||||
uint64_t cpu_util = self->m_cpu_core_ctx[0].util;
|
||||
for (size_t i = 1; i < CORE_NUMS; i++) {
|
||||
if (cpu_util < self->m_cpu_core_ctx[i].util)
|
||||
cpu_util = self->m_cpu_core_ctx[i].util;
|
||||
}
|
||||
return cpu_util;
|
||||
};
|
||||
|
||||
struct s_MaxQueue {
|
||||
uint32_t queue[QUEUE_SIZE] = { 0 };
|
||||
size_t pos = 0;
|
||||
} q;
|
||||
|
||||
auto GetGpuUtil = [nvgpu_field, q]() mutable {
|
||||
uint32_t load = GpuCoreUtil(nvgpu_field, TICK_TIME_GPU_MS).Get();
|
||||
if (load > 20) { // Ignore load <= 2.0%
|
||||
q.queue[q.pos % QUEUE_SIZE] = load;
|
||||
q.pos++;
|
||||
} else {
|
||||
load = q.queue[(q.pos - 1) % QUEUE_SIZE];
|
||||
}
|
||||
// Get max of the queue
|
||||
for (size_t i = 1; i < QUEUE_SIZE; i++) {
|
||||
size_t p = (q.pos + i - 1) % QUEUE_SIZE;
|
||||
if (load < q.queue[p])
|
||||
load = q.queue[p];
|
||||
}
|
||||
|
||||
return load;
|
||||
};
|
||||
|
||||
uint64_t update_ticks = SAMPLE_RATE_MAIN;
|
||||
bool CPUBoosted = false;
|
||||
bool GPUBoosted = false; // Limited to 76.8 MHz, literally
|
||||
|
||||
while (!self->m_stop_threads) {
|
||||
self->m_core3_stuck_cnt = 0;
|
||||
|
||||
bool shouldUpdateContext = update_ticks++ >= SAMPLE_RATE_MAIN;
|
||||
if (shouldUpdateContext) {
|
||||
update_ticks = 0;
|
||||
uint32_t hz = Clocks::GetCurrentHz(SysClkModule_GPU);
|
||||
// Sleep mode detected, wait 1 tick
|
||||
while (!hz) {
|
||||
self->m_core3_stuck_cnt = 0;
|
||||
svcSleepThread(TICK_TIME_MAIN_NS);
|
||||
hz = Clocks::GetCurrentHz(SysClkModule_GPU);
|
||||
}
|
||||
|
||||
GPUBoosted = apmExtIsBoostMode(self->m_perf_conf_id, true);
|
||||
CPUBoosted = apmExtIsBoostMode(self->m_perf_conf_id, false);
|
||||
|
||||
self->m_gpu_freq.idx_target_hz = FindIndex(&self->m_gpu_freq, hz);
|
||||
if (GPUBoosted)
|
||||
SetBoostHz(&self->m_gpu_freq);
|
||||
|
||||
hz = Clocks::GetCurrentHz(SysClkModule_CPU);
|
||||
self->m_cpu_freq.idx_target_hz = FindIndex(&self->m_cpu_freq, hz);
|
||||
if (CPUBoosted)
|
||||
SetBoostHz(&self->m_cpu_freq);
|
||||
|
||||
hz = Clocks::GetCurrentHz(SysClkModule_MEM);
|
||||
if (!self->m_mem_freq)
|
||||
self->m_mem_freq = hz;
|
||||
if (hz != self->m_mem_freq)
|
||||
Clocks::SetHz(SysClkModule_MEM, self->m_mem_freq);
|
||||
} else {
|
||||
if (!GPUBoosted) {
|
||||
uint32_t gpu_util = GetGpuUtil();
|
||||
if (gpu_util > GPU_THR_RAMP_MAX) {
|
||||
if (TargetRamp(&self->m_gpu_freq, RAMP_MAX))
|
||||
SetHz(&self->m_gpu_freq);
|
||||
} else if (gpu_util > GPU_THR_RAMP_UP) {
|
||||
if (TargetRamp(&self->m_gpu_freq, RAMP_UP))
|
||||
SetHz(&self->m_gpu_freq);
|
||||
} else if (gpu_util < GPU_THR_RAMP_DOWN) {
|
||||
if (TargetRamp(&self->m_gpu_freq, RAMP_DOWN))
|
||||
SetHz(&self->m_gpu_freq);
|
||||
}
|
||||
}
|
||||
if (!CPUBoosted) {
|
||||
uint64_t cpu_util = GetCpuUtil();
|
||||
if (cpu_util > CPU_THR_RAMP_UP) {
|
||||
if (TargetRamp(&self->m_cpu_freq, RAMP_UP))
|
||||
SetHz(&self->m_cpu_freq);
|
||||
} else if (cpu_util < CPU_THR_RAMP_DOWN) {
|
||||
if (TargetRamp(&self->m_cpu_freq, RAMP_DOWN))
|
||||
SetHz(&self->m_cpu_freq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svcSleepThread(TICK_TIME_MAIN_NS);
|
||||
}
|
||||
}
|
||||
|
||||
215
Source/sys-clk-OC/sysmodule/src/oc_extra.h
Normal file
215
Source/sys-clk-OC/sysmodule/src/oc_extra.h
Normal file
@@ -0,0 +1,215 @@
|
||||
#pragma once
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <stack>
|
||||
#include <nxExt.h>
|
||||
#include <sysclk.h>
|
||||
#include <switch.h>
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
|
||||
class CpuCoreUtil {
|
||||
public:
|
||||
CpuCoreUtil (int coreid = -2, uint64_t ms = 1):
|
||||
m_core_id(coreid), m_wait_time_ms(ms), m_wait_time_ns(ms * 1000'000ULL) {};
|
||||
|
||||
inline uint64_t Get() { Start(); WaitForStop(); Stop(); return Calculate(); };
|
||||
inline void Start() { m_idletick = GetIdleTickCount(); };
|
||||
inline void WaitForStop() { svcSleepThread(m_wait_time_ns); };
|
||||
inline void Stop() { m_idletick = GetIdleTickCount() - m_idletick; };
|
||||
|
||||
static constexpr uint64_t TICKS_PER_MS = 192;
|
||||
inline uint64_t Calculate() { return 100'0 - m_idletick * 10 / (TICKS_PER_MS * m_wait_time_ms); };
|
||||
|
||||
protected:
|
||||
const int m_core_id;
|
||||
const uint64_t m_wait_time_ms, m_wait_time_ns;
|
||||
uint64_t m_idletick;
|
||||
|
||||
inline uint64_t GetIdleTickCount() {
|
||||
uint64_t idletick = 0;
|
||||
svcGetInfo(&idletick, InfoType_IdleTickCount, INVALID_HANDLE, m_core_id);
|
||||
return idletick;
|
||||
};
|
||||
};
|
||||
|
||||
class GpuCoreUtil {
|
||||
public:
|
||||
GpuCoreUtil (uint32_t nvgpu_field, uint64_t ms = 1):
|
||||
m_nvgpu_field(nvgpu_field), m_wait_time_ns(ms * 1000'000ULL) {};
|
||||
|
||||
inline uint64_t Get() { Wait(); return GetLoad(); };
|
||||
inline void Wait() { svcSleepThread(m_wait_time_ns); };
|
||||
inline uint32_t GetLoad() {
|
||||
uint32_t load;
|
||||
nvIoctl(m_nvgpu_field, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &load);
|
||||
// if (R_FAILED(rc)) {
|
||||
// ERROR_THROW("[mgr] nvIoctl() failed: 0x%lX", rc);
|
||||
// }
|
||||
return load;
|
||||
};
|
||||
|
||||
protected:
|
||||
uint32_t m_nvgpu_field;
|
||||
const uint64_t m_wait_time_ns;
|
||||
static constexpr uint64_t NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD = 0x80044715;
|
||||
};
|
||||
|
||||
class ReverseNXSync {
|
||||
public:
|
||||
ReverseNXSync ()
|
||||
: m_rt_mode(ReverseNX_NotFound), m_tool_mode(ReverseNX_NotFound) {
|
||||
CheckToolEnabled();
|
||||
};
|
||||
|
||||
void ToggleSync(bool enable) { m_sync_enabled = enable; };
|
||||
void Reset(uint64_t app_id) { m_app_id = app_id; SetRTMode(ReverseNX_NotFound); GetToolMode(); }
|
||||
ReverseNXMode GetRTMode() { return m_rt_mode; };
|
||||
void SetRTMode(ReverseNXMode mode) { m_rt_mode = mode; };
|
||||
ReverseNXMode GetToolMode() { return m_tool_mode = RecheckToolMode(); };
|
||||
SysClkProfile GetProfile(SysClkProfile real);
|
||||
ReverseNXMode GetMode();
|
||||
|
||||
protected:
|
||||
ReverseNXMode m_rt_mode, m_tool_mode;
|
||||
uint64_t m_app_id = 0;
|
||||
bool m_tool_enabled;
|
||||
bool m_sync_enabled;
|
||||
|
||||
bool CheckToolEnabled();
|
||||
ReverseNXMode GetToolModeFromPatch(const char* patch_path);
|
||||
ReverseNXMode RecheckToolMode();
|
||||
};
|
||||
|
||||
namespace PsmExt {
|
||||
void ChargingHandler(bool fastChargingEnabled, uint32_t chargingLimit);
|
||||
};
|
||||
|
||||
class Governor {
|
||||
public:
|
||||
Governor() {
|
||||
memset(reinterpret_cast<void*>(&m_cpu_freq), 0, sizeof(m_cpu_freq));
|
||||
memset(reinterpret_cast<void*>(&m_gpu_freq), 0, sizeof(m_gpu_freq));
|
||||
|
||||
m_cpu_freq.module = SysClkModule_CPU;
|
||||
m_gpu_freq.module = SysClkModule_GPU;
|
||||
|
||||
m_cpu_freq.hz_list = &sysclk_g_freq_table_cpu_hz[0];
|
||||
m_gpu_freq.hz_list = &sysclk_g_freq_table_gpu_hz[0];
|
||||
|
||||
m_cpu_freq.idx_boost_hz = FindIndex(&m_cpu_freq, 1785'000'000);
|
||||
|
||||
m_gpu_freq.idx_boost_hz = FindIndex(&m_gpu_freq, 76'800'000);
|
||||
m_gpu_freq.idx_min_hz = FindIndex(&m_gpu_freq, 153'600'000);
|
||||
|
||||
nvInitialize();
|
||||
Result rc = nvOpen(&m_nvgpu_field, "/dev/nvhost-ctrl-gpu");
|
||||
if (R_FAILED(rc)) {
|
||||
ERROR_THROW("[mgr] nvOpen() failed: 0x%lX", rc);
|
||||
nvExit();
|
||||
}
|
||||
};
|
||||
|
||||
~Governor() {
|
||||
Stop();
|
||||
nvClose(m_nvgpu_field);
|
||||
nvExit();
|
||||
};
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void SetMaxHz(uint32_t max_hz, SysClkModule module);
|
||||
void SetCPUBoostHz(uint32_t hz) { m_cpu_freq.idx_boost_hz = FindIndex(&m_cpu_freq, hz); };
|
||||
void SetPerfConf(uint32_t id);
|
||||
|
||||
protected:
|
||||
// Parameters for sampling
|
||||
static constexpr uint64_t SAMPLE_RATE_MAIN = 60, SAMPLE_RATE_GPU = 60;
|
||||
static constexpr uint64_t SAMPLE_RATE_CPU = SAMPLE_RATE_GPU / 2;
|
||||
static constexpr uint64_t UPDATE_CONTEXT_RATE = 60;
|
||||
static constexpr uint64_t TICK_TIME_CPU_MS = 1000 / SAMPLE_RATE_CPU;
|
||||
static constexpr uint64_t TICK_TIME_CPU_NS = 1E9 / SAMPLE_RATE_CPU;
|
||||
static constexpr uint64_t TICK_TIME_GPU_MS = 1000 / SAMPLE_RATE_GPU;
|
||||
static constexpr uint64_t TICK_TIME_MAIN_MS = 1000 / SAMPLE_RATE_MAIN;
|
||||
static constexpr uint64_t TICK_TIME_MAIN_NS = 1E9 / SAMPLE_RATE_MAIN;
|
||||
|
||||
// Parameters for frequency ramp threshold
|
||||
static constexpr uint64_t CPU_THR_RAMP_DOWN = 70'0;
|
||||
static constexpr uint64_t CPU_THR_RAMP_UP = 90'0;
|
||||
static constexpr uint64_t GPU_THR_RAMP_DOWN = 60'0;
|
||||
static constexpr uint64_t GPU_THR_RAMP_UP = 80'0;
|
||||
static constexpr uint64_t GPU_THR_RAMP_MAX = 90'0;
|
||||
|
||||
static constexpr int CORE_NUMS = 4;
|
||||
|
||||
bool m_stop_threads = false;
|
||||
Thread m_t_cpuworker[CORE_NUMS], m_t_main;
|
||||
std::atomic<uint64_t> m_core3_stuck_cnt = 0;
|
||||
|
||||
uint32_t m_nvgpu_field;
|
||||
uint32_t m_mem_freq;
|
||||
uint32_t m_perf_conf_id;
|
||||
SysClkApmConfiguration *m_apm_conf;
|
||||
|
||||
typedef enum {
|
||||
RAMP_UP,
|
||||
RAMP_DOWN,
|
||||
RAMP_MAX,
|
||||
RAMP_MIN,
|
||||
RAMP_BOOST,
|
||||
} FREQ_RAMP_DIRECTION;
|
||||
|
||||
typedef struct {
|
||||
SysClkModule module;
|
||||
uint32_t* hz_list;
|
||||
uint8_t idx_target_hz;
|
||||
uint8_t idx_min_hz;
|
||||
uint8_t idx_max_hz;
|
||||
uint8_t idx_boost_hz;
|
||||
} s_Freq;
|
||||
s_Freq m_cpu_freq, m_gpu_freq;
|
||||
|
||||
static uint32_t FindIndex(s_Freq* f, uint32_t hz);
|
||||
static bool TargetRamp(s_Freq* f, FREQ_RAMP_DIRECTION dir);
|
||||
static void SetHz(s_Freq* f);
|
||||
static void SetBoostHz(s_Freq* f);
|
||||
|
||||
typedef struct {
|
||||
Governor* self;
|
||||
int64_t id;
|
||||
uint64_t util;
|
||||
} s_CoreContext;
|
||||
s_CoreContext m_cpu_core_ctx[CORE_NUMS];
|
||||
|
||||
s_CoreContext* InitCoreContext(s_CoreContext* context, Governor* self, int64_t id = 0);
|
||||
|
||||
static void CheckCpuUtilWorker(void* args);
|
||||
static void Main(void* args);
|
||||
|
||||
private:
|
||||
static constexpr size_t QUEUE_SIZE = 8;
|
||||
template <typename T>
|
||||
struct s_Queue {
|
||||
// Much faster than <queue> from stl
|
||||
T queue[QUEUE_SIZE] = { 0 };
|
||||
T sum = 0;
|
||||
T pos = 0;
|
||||
|
||||
T GetAvg() { return sum / QUEUE_SIZE; };
|
||||
T GetFirst() { return queue[pos % QUEUE_SIZE]; };
|
||||
T GetLast() { return queue[(pos - 1) % QUEUE_SIZE]; };
|
||||
T PopAndPush(T val_to_push) {
|
||||
T val_to_pop;
|
||||
sum -= (val_to_pop = GetFirst()); // Pop and subtract from sum
|
||||
sum += (queue[pos % QUEUE_SIZE] = val_to_push); // Push and add to sum
|
||||
pos++;
|
||||
return val_to_pop;
|
||||
}
|
||||
};
|
||||
|
||||
void CheckCpuUtilWorkerSysCore();
|
||||
void CheckCpuUtilWorkerAppCore(int64_t coreid);
|
||||
};
|
||||
Reference in New Issue
Block a user