hocclk: refactoring and ram pll measurement

This commit is contained in:
souldbminersmwc
2026-04-07 19:48:17 -04:00
parent c6cd863526
commit 90e53b52b2
17 changed files with 427 additions and 201 deletions

View File

@@ -158,6 +158,12 @@ typedef enum {
RamDisplayMode_EnumMax,
} RamDisplayMode;
typedef enum {
MemoryFrequencyMeasurementMode_Actmon = 0,
MemoryFrequencyMeasurementMode_PLL,
MemoryFrequencyMeasurementMode_EnumMax,
} MemoryFrequencyMeasurementMode;
#define HOCCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax)
// Packed u32

View File

@@ -68,6 +68,8 @@ typedef enum {
HocClkConfigValue_CpuGovernorMinimumFreq,
HocClkConfigValue_DisplayVoltage,
HocClkConfigValue_MemoryFrequencyMeasurementMode,
KipConfigValue_custRev,
// KipConfigValue_mtcConf,
KipConfigValue_hpMode,
@@ -257,6 +259,9 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case HocClkConfigValue_DisplayVoltage:
return pretty ? "Display Voltage" : "display_voltage";
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
return pretty ? "Memory Frequency Measurement Mode" : "mem_freq_measurement_mode";
// KIP config values
case KipConfigValue_custRev:
return pretty ? "Custom Revision" : "kip_cust_rev";
@@ -436,6 +441,7 @@ static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
case HocClkConfigValue_GPUScheduling:
case HocClkConfigValue_LiveCpuUv:
case HocClkConfigValue_GPUSchedulingMethod:
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
return 0ULL;
case HocClkConfigValue_EristaMaxCpuClock:
return 1785ULL;
@@ -592,6 +598,7 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case HocClkConfigValue_GPUScheduling:
case HocClkConfigValue_RAMVoltDisplayMode:
case HocClkConfigValue_CpuGovernorMinimumFreq:
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
return true;
case HocClkConfigValue_BatteryChargeCurrent:
return ((input >= 1024) && (input <= 3072)) || !input;

View File

@@ -525,3 +525,24 @@
#define CL_DVFS_I2C_STS_0 0x48
#define CL_DVFS_INTR_STS_0 0x5C
#define CL_DVFS_I2C_CLK_DIVISOR_REGISTER_0 0x16C
#define CLK_SOURCE_EMC 0x19c
#define PLLC_BASE 0x080
#define PLLM_BASE 0x090
#define PLLP_BASE 0x0a0
#define PLLA_BASE 0x0b0
#define PLLU_BASE 0x0c0
#define _PLLD_BASE 0x0d0
#define PLLX_BASE 0x0e0
#define PLLE_BASE 0x0e8
#define PLLC2_BASE 0x4e8
#define PLLC3_BASE 0x4fc
#define PLLD2_BASE 0x4b8
#define PLLRE_BASE 0x4c4
#define PLLC4_BASE 0x5a4
#define PLLMB_BASE 0x5e8
#define PLLA1_BASE 0x6a4
#define PLLDP_BASE 0x590
#define OSC_HZ 38400000ULL

View File

@@ -121,7 +121,7 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
// === VOLTAGES ===
renderer->drawString(displayStrings[8], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU voltage
renderer->drawString(displayStrings[9], false, dataPositions[1] + 9, y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage
renderer->drawString(displayStrings[9], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage
renderer->drawStringWithColoredSections(displayStrings[10], false, {""}, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor, tsl::separatorColor);

View File

@@ -42,6 +42,7 @@ class CpuSubmenuGui;
class GpuSubmenuGui;
class GpuCustomTableSubmenuGui;
class RamTableEditor;
class ExperimentalSettingsSubMenuGui;
MiscGui::MiscGui()
{
@@ -458,100 +459,19 @@ void MiscGui::listUI()
displaySubMenu->setValue(R_ARROW);
this->listElement->addItem(displaySubMenu);
#if IS_MINIMAL == 0
// std::vector<NamedValue> chargerCurrents = {
// NamedValue("Disabled", 0),
// NamedValue("1024mA", 1024),
// NamedValue("1280mA", 1280),
// NamedValue("1536mA", 1536),
// NamedValue("1792mA", 1792),
// NamedValue("2048mA", 2048),
// NamedValue("2304mA", 2304),
// NamedValue("2560mA", 2560),
// NamedValue("2816mA", 2816),
// NamedValue("3072mA", 3072),
// };
if(this->configList->values[HocClkConfigValue_EnableExperimentalSettings]) {
this->listElement->addItem(new tsl::elm::CategoryHeader("Experimental"));
addConfigToggle(HocClkConfigValue_LiveCpuUv, nullptr);
std::vector<NamedValue> gpuSchedMethodValues = {
NamedValue("INI", GpuSchedulingOverrideMethod_Ini),
NamedValue("NV Service", GpuSchedulingOverrideMethod_NvService),
};
addConfigButton(
HocClkConfigValue_GPUSchedulingMethod,
"GPU Scheduling Override Method",
ValueRange(0, 0, 1, "", 0),
"GPU Scheduling Override Method",
&thresholdsDisabled,
{},
gpuSchedMethodValues,
false
);
tsl::elm::CustomDrawer* chargeWarningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE150 Overriding the charge current", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
renderer->drawString("can be dangerous and may cause", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
renderer->drawString("damage to your battery or charger!", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
});
chargeWarningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 90);
this->listElement->addItem(chargeWarningText);
if(!IsHoag()) {
std::vector<NamedValue> chargerCurrents = {
NamedValue("Disabled", 0),
NamedValue("1024mA", 1024),
NamedValue("1280mA", 1280),
NamedValue("1536mA", 1536),
NamedValue("1792mA", 1792),
NamedValue("2048mA", 2048),
NamedValue("2304mA", 2304),
NamedValue("2560mA", 2560),
NamedValue("2816mA", 2816),
NamedValue("3072mA", 3072),
};
ValueThresholds chargerThresholds(2048, 2049);
addConfigButton(
HocClkConfigValue_BatteryChargeCurrent,
"Charge Current Override",
ValueRange(0, 0, 1, "", 0),
"Charge Current Override",
&chargerThresholds,
{},
chargerCurrents,
false
);
if(this->configList->values[HocClkConfigValue_EnableExperimentalSettings]) {
tsl::elm::ListItem* experimentalSubMenu = new tsl::elm::ListItem("Experimental Settings");
experimentalSubMenu->setClickListener([](u64 keys) {
if (keys & HidNpadButton_A) {
tsl::changeTo<ExperimentalSettingsSubMenuGui>();
return true;
}
else {
std::vector<NamedValue> chargerCurrents = {
NamedValue("Disabled", 0),
NamedValue("1024mA", 1024),
NamedValue("1280mA", 1280),
NamedValue("1536mA", 1536),
NamedValue("1792mA", 1792),
NamedValue("2048mA", 2048),
NamedValue("2304mA", 2304),
NamedValue("2560mA", 2560),
};
return false;
});
experimentalSubMenu->setValue(R_ARROW);
this->listElement->addItem(experimentalSubMenu);
}
ValueThresholds chargerThresholds(1792, 1793);
addConfigButton(
HocClkConfigValue_BatteryChargeCurrent,
"Charge Current Override",
ValueRange(0, 0, 1, "", 0),
"Charge Current Override",
&chargerThresholds,
{},
chargerCurrents,
false
);
}
}
#endif
}
class GeneralSettingsSubMenuGui : public MiscGui {
@@ -579,9 +499,125 @@ protected:
{},
false
);
tsl::elm::CustomDrawer* exSetWarning = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE150 Experimental Settings", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
renderer->drawString("are a work in progress! Use with caution!", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
renderer->drawString("Here be dragons!", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
});
exSetWarning->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 90);
this->listElement->addItem(exSetWarning);
addConfigToggle(HocClkConfigValue_EnableExperimentalSettings, nullptr);
}
};
class ExperimentalSettingsSubMenuGui : public MiscGui {
public:
ExperimentalSettingsSubMenuGui() { }
protected:
void listUI() override {
this->listElement->addItem(new tsl::elm::CategoryHeader("Experimental Settings"));
ValueThresholds thresholdsDisabled(0, 0);
addConfigToggle(HocClkConfigValue_LiveCpuUv, nullptr);
std::vector<NamedValue> gpuSchedMethodValues = {
NamedValue("INI", GpuSchedulingOverrideMethod_Ini),
NamedValue("NV Service", GpuSchedulingOverrideMethod_NvService),
};
addConfigButton(
HocClkConfigValue_GPUSchedulingMethod,
"GPU Scheduling Override Method",
ValueRange(0, 0, 1, "", 0),
"GPU Scheduling Override Method",
&thresholdsDisabled,
{},
gpuSchedMethodValues,
false
);
std::vector<NamedValue> ramRFMeasurementMethods = {
NamedValue("Actmon", MemoryFrequencyMeasurementMode_Actmon),
NamedValue("PLL", MemoryFrequencyMeasurementMode_PLL),
};
addConfigButton(
HocClkConfigValue_MemoryFrequencyMeasurementMode,
"Memory Frequency Measurement Mode",
ValueRange(0, 0, 1, "", 0),
"Memory Frequency Measurement Mode",
&thresholdsDisabled,
{},
ramRFMeasurementMethods,
false
);
tsl::elm::CustomDrawer* chargeWarningText = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE150 Overriding the charge current", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
renderer->drawString("can be dangerous and may cause", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
renderer->drawString("damage to your battery or charger!", false, x + 20, y + 70, 18, tsl::style::color::ColorText);
});
chargeWarningText->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, 90);
this->listElement->addItem(chargeWarningText);
if(!IsHoag()) {
std::vector<NamedValue> chargerCurrents = {
NamedValue("Disabled", 0),
NamedValue("1024mA", 1024),
NamedValue("1280mA", 1280),
NamedValue("1536mA", 1536),
NamedValue("1792mA", 1792),
NamedValue("2048mA", 2048),
NamedValue("2304mA", 2304),
NamedValue("2560mA", 2560),
NamedValue("2816mA", 2816),
NamedValue("3072mA", 3072),
};
ValueThresholds chargerThresholds(2048, 2049);
addConfigButton(
HocClkConfigValue_BatteryChargeCurrent,
"Charge Current Override",
ValueRange(0, 0, 1, "", 0),
"Charge Current Override",
&chargerThresholds,
{},
chargerCurrents,
false
);
} else {
std::vector<NamedValue> chargerCurrents = {
NamedValue("Disabled", 0),
NamedValue("1024mA", 1024),
NamedValue("1280mA", 1280),
NamedValue("1536mA", 1536),
NamedValue("1792mA", 1792),
NamedValue("2048mA", 2048),
NamedValue("2304mA", 2304),
NamedValue("2560mA", 2560),
};
ValueThresholds chargerThresholds(1792, 1793);
addConfigButton(
HocClkConfigValue_BatteryChargeCurrent,
"Charge Current Override",
ValueRange(0, 0, 1, "", 0),
"Charge Current Override",
&chargerThresholds,
{},
chargerCurrents,
false
);
}
}
};
class GovernorSettingsSubMenuGui : public MiscGui {
public:
GovernorSettingsSubMenuGui() { }
@@ -1314,7 +1350,7 @@ protected:
addConfigButton(
KipConfigValue_eristaGpuVmin,
"GPU Minimum Voltage",
ValueRange(700, 875, 5, "mV", 1),
ValueRange(675, 875, 5, "mV", 1),
"GPU Minimum Voltage",
&thresholdsDisabled,
{},

View File

@@ -86,7 +86,8 @@
"is_ro": false,
"is_io": true
}
}, {
},
{
"type": "syscalls",
"value": {
"svcUnknown": "0x00",

View File

@@ -44,6 +44,8 @@
#include "../file_utils.hpp"
namespace board {
u64 clkVirtAddr, dsiVirtAddr;
HocClkSocType gSocType;
u8 gDramID;
HocClkConsoleType gConsoleType = HocClkConsoleType_Iowa;
@@ -145,14 +147,12 @@ namespace board {
StartMiscThread(pwmCheck, &iCon);
u64 clkVirtAddr, dsiVirtAddr;
rc = QueryMemoryMapping(&clkVirtAddr, 0x60006000, 0x1000);
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (clk)");
rc = QueryMemoryMapping(&dsiVirtAddr, 0x54300000, 0x40000);
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (dsi)");
display::DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr, .isLite = (GetConsoleType() == HocClkConsoleType_Hoag), .isRetroSUPER = integrations::GetRETROSuperStatus()};
display::Initialize(&cfg);

View File

@@ -40,7 +40,7 @@
#define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0))
namespace board {
extern u64 clkVirtAddr, dsiVirtAddr;
void Initialize();
void Exit();
HocClkSocType GetSocType();

View File

@@ -31,7 +31,8 @@
#include "board.hpp"
#include "board_name.hpp"
#include "../errors.hpp"
#include "pllmb.hpp"
#include "../config.hpp"
namespace board {
PcvModule GetPcvModule(HocClkModule hocclkModule) {
@@ -141,10 +142,9 @@ namespace board {
case HocClkModule_GPU:
return t210ClkGpuFreq();
case HocClkModule_MEM:
return t210ClkMemFreq();
return config::GetConfigValue(HocClkConfigValue_MemoryFrequencyMeasurementMode) == MemoryFrequencyMeasurementMode_PLL ? pllmb::getRamClockRatePLLMB() : t210ClkMemFreq();
case HocClkModule_Display:
return GetDisplayRate(hz);
return hz;
default:
ASSERT_ENUM_VALID(HocClkModule, module);
}

View File

@@ -0,0 +1,132 @@
#include "pllmb.hpp"
namespace pllmb {
static const u8 qlin_hw_to_pdiv[17] = {
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 30, 32
};
enum pdiv_type {
PDIV_QLIN,
PDIV_POW2
};
struct pll_desc_t {
u32 base_offset;
u8 divm_shift;
u8 divm_width;
u8 divn_shift;
u8 divn_width;
u8 divp_shift;
u8 divp_width;
pdiv_type ptype;
};
static const pll_desc_t pll_table[] = {
{ PLLM_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLMB_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLP_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ PLLA_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ PLLU_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ _PLLD_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ PLLX_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLA1_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLDP_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLD2_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLC4_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLRE_BASE, 0, 8, 8, 8, 16, 4, PDIV_QLIN },
{ PLLC_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
{ PLLC2_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
{ PLLC3_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
};
static inline u32 clk_read32(u32 offset)
{
return *(volatile u32 *)(uintptr_t)(board::clkVirtAddr + offset);
}
static inline u32 extract(u32 val, u8 shift, u8 width)
{
return (val >> shift) & ((1u << width) - 1u);
}
static u64 pll_rate_from_desc(const pll_desc_t &pll, u64 osc_hz,
bool undivided)
{
u32 base = clk_read32(pll.base_offset);
u32 divm = extract(base, pll.divm_shift, pll.divm_width);
u32 divn = extract(base, pll.divn_shift, pll.divn_width);
if (divm == 0 || divn == 0)
return 0;
u64 vco = osc_hz * divn / divm;
if (undivided)
return vco;
u32 hw_p = extract(base, pll.divp_shift, pll.divp_width);
u32 pdiv;
if (pll.ptype == PDIV_QLIN)
pdiv = (hw_p < 17) ? qlin_hw_to_pdiv[hw_p] : 1;
else
pdiv = 1u << hw_p;
return vco / pdiv;
}
static u64 pll_rate_by_offset(u32 base_offset, u64 osc_hz,
bool undivided)
{
for (const auto &pll : pll_table) {
if (pll.base_offset == base_offset)
return pll_rate_from_desc(pll, osc_hz, undivided);
}
return 0;
}
u64 getRamClockRatePLLMB()
{
u32 clk_src = clk_read32(CLK_SOURCE_EMC);
u32 src = (clk_src >> 29) & 0x7;
u32 div = (clk_src >> 0) & 0xff;
u32 pll_off;
bool undivided = false;
switch (src) {
case EMC_SRC_PLLM:
pll_off = PLLM_BASE;
break;
case EMC_SRC_PLLM_UD:
pll_off = PLLM_BASE;
undivided = true;
break;
case EMC_SRC_PLLMB:
pll_off = PLLMB_BASE;
break;
case EMC_SRC_PLLMB_UD:
pll_off = PLLMB_BASE;
undivided = true;
break;
case EMC_SRC_PLLP:
pll_off = PLLP_BASE;
break;
case EMC_SRC_PLLP_UD:
pll_off = PLLP_BASE;
undivided = true;
break;
case EMC_SRC_PLLC:
pll_off = PLLC_BASE;
break;
case EMC_SRC_CLK_M:
return OSC_HZ;
default:
return 0;
}
u64 pll_hz = pll_rate_by_offset(pll_off, OSC_HZ, undivided);
return pll_hz / (div + 2) * 2;
}
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <cstdint>
#include <switch.h>
#include <hocclk.h>
#include "board.hpp"
#include <registers.h>
namespace pllmb {
typedef enum PLLSource {
EMC_SRC_PLLM = 0,
EMC_SRC_PLLC = 1,
EMC_SRC_PLLP = 2,
EMC_SRC_CLK_M = 3,
EMC_SRC_PLLM_UD = 4,
EMC_SRC_PLLMB_UD = 5,
EMC_SRC_PLLMB = 6,
EMC_SRC_PLLP_UD = 7
} PLLSource;
u64 getRamClockRatePLLMB();
}

View File

@@ -545,8 +545,6 @@ namespace clockManager {
void Initialize()
{
config::Initialize();
gContext = {};
gContext.applicationId = 0;
gContext.profile = HocClkProfile_Handheld;
@@ -592,7 +590,6 @@ namespace clockManager {
void Exit()
{
governor::exitThreads();
config::Exit();
}
HocClkContext GetCurrentContext()

View File

@@ -134,6 +134,7 @@ int main(int argc, char** argv)
fatalThrow(rc);
return 1;
}
config::Initialize();
board::Initialize();
processManagement::Initialize();
@@ -164,7 +165,7 @@ int main(int argc, char** argv)
clockManager::Exit();
processManagement::Exit();
board::Exit();
config::Exit();
fileUtils::LogLine("Exit");
svcSleepThread(1000000ULL);
fileUtils::Exit();