- Fixed gpu_hz_list typo in governor (#46)
- Parse loader.kip config from { "/", "/atmosphere/", "/atmosphere/kips/", "/bootloader/" } (#44)
This commit is contained in:
@@ -60,8 +60,6 @@ ClockManager::ClockManager()
|
||||
this->oc->systemCoreBoostCPU = false;
|
||||
this->oc->governor = false;
|
||||
this->oc->realProfile = SysClkProfile_Handheld;
|
||||
this->oc->maxMEMFreq = 0;
|
||||
this->oc->boostCPUFreq = 0;
|
||||
|
||||
this->rnxSync = new ReverseNXSync;
|
||||
this->governor = new Governor;
|
||||
@@ -76,17 +74,6 @@ ClockManager::~ClockManager()
|
||||
delete this->config;
|
||||
}
|
||||
|
||||
bool ClockManager::IsBoostMode()
|
||||
{
|
||||
std::uint32_t confId = this->context->perfConfId;
|
||||
bool isBoostMode = apmExtIsBoostMode(confId);
|
||||
if (apmExtIsCPUBoosted(confId) && !this->oc->boostCPUFreq) {
|
||||
this->oc->boostCPUFreq = std::max(this->context->freqs[SysClkModule_CPU], 1785'000'000U);
|
||||
this->governor->SetCPUBoostHz(this->oc->boostCPUFreq);
|
||||
}
|
||||
return isBoostMode;
|
||||
}
|
||||
|
||||
void ClockManager::SetRunning(bool running)
|
||||
{
|
||||
this->running = running;
|
||||
@@ -127,7 +114,7 @@ uint32_t ClockManager::GetHz(SysClkModule module)
|
||||
case SysClkModule_MEM:
|
||||
hz = (mode == ReverseNX_Docked ||
|
||||
this->oc->realProfile == SysClkProfile_Docked) ?
|
||||
MAX_MEM_CLOCK : 1600'000'000;
|
||||
Clocks::maxMemFreq : 1600'000'000;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -138,27 +125,13 @@ uint32_t ClockManager::GetHz(SysClkModule module)
|
||||
{
|
||||
/* Considering realProfile frequency limit */
|
||||
hz = Clocks::GetNearestHz(module, this->oc->realProfile, hz);
|
||||
|
||||
if (module == SysClkModule_MEM && hz == MAX_MEM_CLOCK)
|
||||
{
|
||||
/* Trigger Max Mem Clock and record it */
|
||||
if (!this->oc->maxMEMFreq)
|
||||
{
|
||||
uint32_t currentHz = Clocks::GetCurrentHz(SysClkModule_MEM);
|
||||
Clocks::SetHz(SysClkModule_MEM, MAX_MEM_CLOCK);
|
||||
this->oc->maxMEMFreq = Clocks::GetCurrentHz(SysClkModule_MEM);
|
||||
Clocks::SetHz(SysClkModule_MEM, currentHz);
|
||||
}
|
||||
|
||||
return this->oc->maxMEMFreq;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle CPU Auto Boost, no user-defined hz required */
|
||||
if (module == SysClkModule_CPU)
|
||||
{
|
||||
if (this->oc->systemCoreBoostCPU && hz < this->oc->boostCPUFreq)
|
||||
return this->oc->boostCPUFreq;
|
||||
if (this->oc->systemCoreBoostCPU && hz < Clocks::boostCpuFreq)
|
||||
return Clocks::boostCpuFreq;
|
||||
if (!hz)
|
||||
/* Trigger RefreshContext() and Tick(), resetting default CPU frequency */
|
||||
return 1020'000'000;
|
||||
@@ -182,7 +155,8 @@ void ClockManager::Tick()
|
||||
if (hz && hz != this->context->freqs[module] && !this->oc->governor)
|
||||
{
|
||||
// Skip setting CPU or GPU clocks in CpuBoostMode if CPU <= boostCPUFreq or GPU >= 76.8MHz
|
||||
bool skipBoost = IsBoostMode() && ((module == SysClkModule_CPU && hz <= this->oc->boostCPUFreq) || module == SysClkModule_GPU);
|
||||
bool skipBoost = apmExtIsBoostMode(this->context->perfConfId);
|
||||
skipBoost &= (module == SysClkModule_CPU && hz <= Clocks::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);
|
||||
@@ -210,7 +184,7 @@ void ClockManager::WaitForNextTick()
|
||||
this->GetConfig()->GetConfigValue(SysClkConfigValue_AutoCPUBoost) &&
|
||||
this->context->enabled &&
|
||||
this->oc->realProfile != SysClkProfile_Handheld &&
|
||||
this->context->freqs[SysClkModule_CPU] <= this->oc->boostCPUFreq;
|
||||
this->context->freqs[SysClkModule_CPU] <= Clocks::boostCpuFreq;
|
||||
|
||||
if (boostOK) {
|
||||
uint32_t core3Util = CpuCoreUtil(3, tickWaitTimeMs * 1000'000ULL).Get();
|
||||
@@ -222,7 +196,7 @@ void ClockManager::WaitForNextTick()
|
||||
Clocks::SetHz(SysClkModule_CPU, GetHz(SysClkModule_CPU));
|
||||
|
||||
if (!lastBoost && this->oc->systemCoreBoostCPU)
|
||||
Clocks::SetHz(SysClkModule_CPU, this->oc->boostCPUFreq);
|
||||
Clocks::SetHz(SysClkModule_CPU, Clocks::boostCpuFreq);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -313,7 +287,7 @@ bool ClockManager::RefreshContext()
|
||||
}
|
||||
|
||||
// let ptm module handle boost clocks rather than resetting
|
||||
if (hasChanged && !IsBoostMode()) {
|
||||
if (hasChanged && !apmExtIsBoostMode(this->context->perfConfId)) {
|
||||
Clocks::ResetToStock();
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,5 @@ class ClockManager
|
||||
ReverseNXSync *rnxSync;
|
||||
Governor *governor;
|
||||
|
||||
bool IsBoostMode();
|
||||
|
||||
uint32_t GetHz(SysClkModule);
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <nxExt.h>
|
||||
#include "clocks.h"
|
||||
#include "errors.h"
|
||||
#include "file_utils.h"
|
||||
|
||||
void Clocks::GetRange(SysClkModule module, SysClkProfile profile, uint32_t** min, uint32_t** max)
|
||||
{
|
||||
@@ -58,18 +59,18 @@ void Clocks::GetRange(SysClkModule module, SysClkProfile profile, uint32_t** min
|
||||
ERROR_THROW("No such PcvModule: %u", module);
|
||||
}
|
||||
|
||||
Result Clocks::GetTable(SysClkModule module, SysClkProfile profile, size_t max_entry_num, uint32_t* out_table) {
|
||||
Result Clocks::GetTable(SysClkModule module, SysClkProfile profile, SysClkFrequencyTable* out_table) {
|
||||
uint32_t* min = NULL;
|
||||
uint32_t* max = NULL;
|
||||
memset(out_table, 0, max_entry_num * sizeof(uint32_t));
|
||||
GetRange(module, profile, &min, &max);
|
||||
if (!min || !max || (max - min) / sizeof(uint32_t) >= max_entry_num)
|
||||
if (!min || !max || (max - min) / sizeof(uint32_t) >= sizeof(SysClkFrequencyTable) / sizeof(uint32_t))
|
||||
return 1;
|
||||
|
||||
memset(out_table, 0, sizeof(SysClkFrequencyTable));
|
||||
uint32_t* p = min;
|
||||
size_t idx = 0;
|
||||
while(p <= max)
|
||||
out_table[idx++] = *p++;
|
||||
out_table->values[idx++] = *p++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -78,7 +79,6 @@ void Clocks::Initialize()
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
// Check if it's Mariko
|
||||
u64 hardware_type = 0;
|
||||
rc = splInitialize();
|
||||
ASSERT_RESULT_OK(rc, "splInitialize");
|
||||
@@ -89,11 +89,11 @@ void Clocks::Initialize()
|
||||
switch (hardware_type) {
|
||||
case 0: // Icosa
|
||||
case 1: // Copper
|
||||
case 4: // Calcio
|
||||
isMariko = false;
|
||||
break;
|
||||
case 2: // Hoag
|
||||
case 3: // Iowa
|
||||
case 4: // Calcio
|
||||
case 5: // Aula
|
||||
isMariko = true;
|
||||
break;
|
||||
@@ -127,6 +127,19 @@ void Clocks::Initialize()
|
||||
rc = tcInitialize();
|
||||
ASSERT_RESULT_OK(rc, "tcInitialize");
|
||||
}
|
||||
|
||||
FileUtils::ParseLoaderKip();
|
||||
|
||||
if (!maxMemFreq) {
|
||||
uint32_t curr_mem_hz = GetCurrentHz(SysClkModule_MEM);
|
||||
SetHz(SysClkModule_MEM, MAX_MEM_CLOCK);
|
||||
svcSleepThread(1'000'000);
|
||||
if (uint32_t hz = GetCurrentHz(SysClkModule_MEM))
|
||||
maxMemFreq = hz;
|
||||
else
|
||||
maxMemFreq = MAX_MEM_CLOCK;
|
||||
SetHz(SysClkModule_MEM, curr_mem_hz);
|
||||
}
|
||||
}
|
||||
|
||||
void Clocks::Exit()
|
||||
@@ -359,6 +372,9 @@ std::uint32_t Clocks::GetCurrentHz(SysClkModule module)
|
||||
|
||||
std::uint32_t Clocks::GetNearestHz(SysClkModule module, SysClkProfile profile, std::uint32_t inHz)
|
||||
{
|
||||
if (module == SysClkModule_MEM && inHz == MAX_MEM_CLOCK)
|
||||
return Clocks::maxMemFreq;
|
||||
|
||||
uint32_t* min = NULL;
|
||||
uint32_t* max = NULL;
|
||||
GetRange(module, profile, &min, &max);
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
class Clocks
|
||||
{
|
||||
public:
|
||||
static inline uint32_t boostCpuFreq = 1785000000;
|
||||
static inline uint32_t maxMemFreq = MAX_MEM_CLOCK;
|
||||
|
||||
static void GetRange(SysClkModule module, SysClkProfile profile, uint32_t** min, uint32_t** max);
|
||||
static Result GetTable(SysClkModule module, SysClkProfile profile, size_t max_entry_num, uint32_t* out_table);
|
||||
static Result GetTable(SysClkModule module, SysClkProfile profile, SysClkFrequencyTable* out_table);
|
||||
static void SetAllowUnsafe(bool allow) { allowUnsafe = allow; };
|
||||
static bool GetIsMariko() { return isMariko; };
|
||||
static void Exit();
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include <dirent.h>
|
||||
#include <filesystem>
|
||||
#include <nxExt.h>
|
||||
|
||||
static LockableMutex g_log_mutex;
|
||||
@@ -182,3 +181,126 @@ void FileUtils::Exit()
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
}
|
||||
|
||||
void FileUtils::ParseLoaderKip() {
|
||||
const char* dirs[] = { "/", "/atmosphere/", "/atmosphere/kips/", "/bootloader/" };
|
||||
char* full_path = new char[0x200];
|
||||
for (auto const& dir : dirs) {
|
||||
struct dirent *entry = NULL;
|
||||
DIR *dp = opendir(dir);
|
||||
if (!dp)
|
||||
continue;
|
||||
|
||||
while ((entry = readdir(dp))) {
|
||||
if (entry->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
snprintf(full_path, 0x200, "%s%s", dir, entry->d_name);
|
||||
|
||||
FILE* fp = fopen(full_path, "r");
|
||||
if (!fp)
|
||||
continue;
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long res = ftell(fp);
|
||||
fclose(fp);
|
||||
if (res == -1)
|
||||
continue;
|
||||
|
||||
size_t filesize = (size_t)res;
|
||||
if (filesize < 0x1000 || filesize > 512 * 1024)
|
||||
continue;
|
||||
|
||||
const char kip_ext[] = {'.', 'k', 'i', 'p'};
|
||||
size_t file_name_len = strnlen(reinterpret_cast<const char*>(&entry->d_name), 256);
|
||||
const char* file_ext = &entry->d_name[file_name_len - sizeof(kip_ext)];
|
||||
|
||||
if (strncasecmp((const char*)kip_ext, file_ext, sizeof(kip_ext)))
|
||||
continue;
|
||||
|
||||
if (R_SUCCEEDED(CustParser(full_path, filesize))) {
|
||||
LogLine("Parsed cust config from %s, maxMemFreq: %u MHz, boostCpuFreq: %u MHz",
|
||||
full_path,
|
||||
Clocks::maxMemFreq / 1'000'000,
|
||||
Clocks::boostCpuFreq / 1'000'000
|
||||
);
|
||||
delete[] full_path;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] full_path;
|
||||
}
|
||||
|
||||
Result FileUtils::CustParser(const char* filepath, size_t filesize) {
|
||||
enum ParseError {
|
||||
ParseError_Success = 0,
|
||||
ParseError_OpenReadFailed,
|
||||
ParseError_WrongKipMagic,
|
||||
ParseError_CustNotFound,
|
||||
ParseError_WrongCustRev,
|
||||
};
|
||||
|
||||
FILE* fp = fopen(filepath, "r");
|
||||
if (!fp)
|
||||
return ParseError_OpenReadFailed;
|
||||
|
||||
constexpr uint8_t KIP_MAGIC[] = {'K', 'I', 'P', '1', 'L', 'o', 'a', 'd', 'e', 'r'};
|
||||
constexpr size_t BLOCK_SIZE = 0x1000;
|
||||
|
||||
char* tmp_block = new char[BLOCK_SIZE];
|
||||
fread(tmp_block, sizeof(char), BLOCK_SIZE, fp);
|
||||
|
||||
if (memcmp(KIP_MAGIC, tmp_block, sizeof(KIP_MAGIC))) {
|
||||
delete[] tmp_block;
|
||||
return ParseError_WrongKipMagic;
|
||||
}
|
||||
|
||||
CustTable table {};
|
||||
|
||||
fpos_t cust_pos = 0;
|
||||
long block_pos = 0;
|
||||
while ((block_pos = ftell(fp)) >= 0 && (size_t)block_pos < filesize) {
|
||||
for (size_t i = 0; i < BLOCK_SIZE; i += sizeof(table.cust)) {
|
||||
if (memcmp(table.cust, &tmp_block[i], sizeof(table.cust)))
|
||||
continue;
|
||||
|
||||
fgetpos(fp, &cust_pos);
|
||||
cust_pos = cust_pos + i - BLOCK_SIZE;
|
||||
goto found;
|
||||
}
|
||||
fread(tmp_block, sizeof(char), BLOCK_SIZE, fp);
|
||||
}
|
||||
|
||||
found:
|
||||
delete[] tmp_block;
|
||||
|
||||
if (!cust_pos) {
|
||||
fclose(fp);
|
||||
return ParseError_CustNotFound;
|
||||
}
|
||||
|
||||
memset(reinterpret_cast<void*>(&table), 0, sizeof(CustTable));
|
||||
fsetpos(fp, &cust_pos);
|
||||
if (!fread(reinterpret_cast<char*>(&table), 1, sizeof(CustTable), fp)) {
|
||||
fclose(fp);
|
||||
return ParseError_OpenReadFailed;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (table.custRev != 2)
|
||||
return ParseError_WrongCustRev;
|
||||
|
||||
if (Clocks::GetIsMariko()) {
|
||||
if (table.marikoCpuBoostClock)
|
||||
Clocks::boostCpuFreq = table.marikoCpuBoostClock * 1000;
|
||||
if (table.marikoEmcMaxClock)
|
||||
Clocks::maxMemFreq = table.marikoEmcMaxClock * 1000;
|
||||
} else {
|
||||
if (table.eristaEmcMaxClock)
|
||||
Clocks::maxMemFreq = table.eristaEmcMaxClock * 1000;
|
||||
}
|
||||
|
||||
return ParseError_Success;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <sysclk.h>
|
||||
@@ -34,6 +35,23 @@ class FileUtils
|
||||
static void InitializeAsync();
|
||||
static void LogLine(const char *format, ...);
|
||||
static void WriteContextToCsv(const SysClkContext* context);
|
||||
static void ParseLoaderKip();
|
||||
protected:
|
||||
typedef struct CustTable {
|
||||
uint8_t cust[4] = {'C', 'U', 'S', 'T'};
|
||||
uint16_t custRev;
|
||||
uint16_t mtcConf;
|
||||
uint32_t marikoCpuMaxClock;
|
||||
uint32_t marikoCpuBoostClock;
|
||||
uint32_t marikoCpuMaxVolt;
|
||||
uint32_t marikoGpuMaxClock;
|
||||
uint32_t marikoEmcMaxClock;
|
||||
uint32_t eristaCpuOCEnable;
|
||||
uint32_t eristaCpuMaxVolt;
|
||||
uint32_t eristaEmcMaxClock;
|
||||
uint32_t eristaEmcVolt;
|
||||
} CustTable;
|
||||
|
||||
static void RefreshFlags(bool force);
|
||||
static Result CustParser(const char* path, size_t filesize);
|
||||
};
|
||||
|
||||
@@ -164,8 +164,8 @@ Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8*
|
||||
if(r->data.size >= sizeof(SysClkIpc_GetFrequencyTable_Args))
|
||||
{
|
||||
SysClkIpc_GetFrequencyTable_Args* in_args = (SysClkIpc_GetFrequencyTable_Args*)r->data.ptr;
|
||||
*out_dataSize = sizeof(uint32_t) * in_args->max_entry_num;
|
||||
return ipcSrv->GetFrequencyTable(in_args, (uint32_t*)out_data);
|
||||
*out_dataSize = sizeof(SysClkFrequencyTable);
|
||||
return ipcSrv->GetFrequencyTable(in_args, (SysClkFrequencyTable*)out_data);
|
||||
}
|
||||
break;
|
||||
case SysClkIpcCmd_GetIsMariko:
|
||||
@@ -313,8 +313,8 @@ Result IpcService::SetReverseNXRTMode(ReverseNXMode mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result IpcService::GetFrequencyTable(SysClkIpc_GetFrequencyTable_Args* args, uint32_t* out_table) {
|
||||
return Clocks::GetTable(args->module, args->profile, args->max_entry_num, out_table);
|
||||
Result IpcService::GetFrequencyTable(SysClkIpc_GetFrequencyTable_Args* args, SysClkFrequencyTable* out_table) {
|
||||
return Clocks::GetTable(args->module, args->profile, out_table);
|
||||
}
|
||||
|
||||
Result IpcService::GetIsMariko(bool* out_is_mariko) {
|
||||
|
||||
@@ -37,7 +37,7 @@ class IpcService
|
||||
Result GetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
Result SetConfigValues(SysClkConfigValueList* configValues);
|
||||
Result SetReverseNXRTMode(ReverseNXMode mode);
|
||||
Result GetFrequencyTable(SysClkIpc_GetFrequencyTable_Args* args, uint32_t* out_table);
|
||||
Result GetFrequencyTable(SysClkIpc_GetFrequencyTable_Args* args, SysClkFrequencyTable* out_table);
|
||||
Result GetIsMariko(bool* out_is_mariko);
|
||||
|
||||
bool running;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "ipc_service.h"
|
||||
#include "oc_extra.h"
|
||||
|
||||
#define INNER_HEAP_SIZE 0x38000
|
||||
#define INNER_HEAP_SIZE 0x40000
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
@@ -135,10 +135,10 @@ Governor::Governor() {
|
||||
m_cpu_freq.module = SysClkModule_CPU;
|
||||
m_gpu_freq.module = SysClkModule_GPU;
|
||||
|
||||
m_cpu_freq.hz_list = &g_freq_table_cpu_hz[0];
|
||||
m_gpu_freq.hz_list = &g_freq_table_cpu_hz[0];
|
||||
m_cpu_freq.hz_list = GetTable(SysClkModule_CPU);
|
||||
m_gpu_freq.hz_list = GetTable(SysClkModule_GPU);
|
||||
|
||||
m_cpu_freq.boost_hz = 1785'000'000;
|
||||
m_cpu_freq.boost_hz = Clocks::boostCpuFreq;
|
||||
m_cpu_freq.utilref_hz = 2397'000'000;
|
||||
|
||||
m_gpu_freq.boost_hz = 76'800'000;
|
||||
@@ -244,7 +244,7 @@ void Governor::s_FreqContext::SetNextFreq(uint32_t norm_util) {
|
||||
} else {
|
||||
uint32_t* p = hz_list;
|
||||
do {
|
||||
if (*p > next_freq) {
|
||||
if (*p >= next_freq) {
|
||||
adj_next_freq = *p;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user