diff --git a/Source/sys-clk/common/include/sysclk/board.h b/Source/sys-clk/common/include/sysclk/board.h index 6338cf62..274bee8c 100644 --- a/Source/sys-clk/common/include/sysclk/board.h +++ b/Source/sys-clk/common/include/sysclk/board.h @@ -47,9 +47,19 @@ typedef enum HorizonOCConsoleType_Lite, HorizonOCConsoleType_UnreleasedMariko, HorizonOCConsoleType_OLED, - SysClkSocType_EnumMax + HorizonOCConsoleType_EnumMax, } HorizonOCConsoleType; +typedef enum { + HocClkVoltage_SOC = 0, + HocClkVoltage_EMCVDD2, + HocClkVoltage_CPU, + HocClkVoltage_GPU, + HocClkVoltage_EMCVDDQ_MarikoOnly, + HocClkVoltage_Display, + HocClkVoltage_EnumMax, +} HocClkVoltage; + typedef enum { SysClkProfile_Handheld = 0, @@ -85,10 +95,13 @@ typedef enum typedef enum { - SysClkRamLoad_All = 0, - SysClkRamLoad_Cpu, - SysClkRamLoad_EnumMax -} SysClkRamLoad; + SysClkPartLoad_EMC = 0, + SysClkPartLoad_EMCCpu, + HocClkPartLoad_GPU, + HocClkPartLoad_CPUAvg, + SysClkPartLoad_EnumMax +} SysClkPartLoad; + typedef enum { @@ -162,3 +175,25 @@ static inline const char* sysclkFormatProfile(SysClkProfile profile, bool pretty return NULL; } } + + +static inline const char* hocClkFormatVoltage(HocClkVoltage voltage, bool pretty) +{ + switch(voltage) + { + case HocClkVoltage_CPU: + return pretty ? "CPU" : "cpu"; + case HocClkVoltage_GPU: + return pretty ? "GPU" : "gpu"; + case HocClkVoltage_EMCVDD2: + return pretty ? "VDD2" : "emcvdd2"; + case HocClkVoltage_EMCVDDQ_MarikoOnly: + return pretty ? "VDDQ" : "vddq"; + case HocClkVoltage_SOC: + return pretty ? "SOC" : "soc"; + case HocClkVoltage_Display: + return pretty ? "SOC" : "soc"; + default: + return NULL; + } +} \ No newline at end of file diff --git a/Source/sys-clk/common/include/sysclk/clock_manager.h b/Source/sys-clk/common/include/sysclk/clock_manager.h index bbbab8ec..798a5efb 100644 --- a/Source/sys-clk/common/include/sysclk/clock_manager.h +++ b/Source/sys-clk/common/include/sysclk/clock_manager.h @@ -40,7 +40,8 @@ typedef struct uint32_t overrideFreqs[SysClkModule_EnumMax]; uint32_t temps[SysClkThermalSensor_EnumMax]; int32_t power[SysClkPowerSensor_EnumMax]; - uint32_t ramLoad[SysClkRamLoad_EnumMax]; + uint32_t PartLoad[SysClkPartLoad_EnumMax]; + uint32_t voltages[HocClkVoltage_EnumMax]; uint32_t perfConfId; } SysClkContext; diff --git a/Source/sys-clk/common/include/sysclk/config.h b/Source/sys-clk/common/include/sysclk/config.h index 98e6970e..1b4109ce 100644 --- a/Source/sys-clk/common/include/sysclk/config.h +++ b/Source/sys-clk/common/include/sysclk/config.h @@ -120,9 +120,6 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr case HocClkConfigValue_LiteTDPLimit: return pretty ? "Lite TDP Limit" : "tdp_limit_l"; - case HocClkConfigValue_TDPCycleLimit: - return pretty ? "TDP Cycle Limit" : "tdp_limit_c"; - default: return pretty ? "Null" : "null"; } @@ -166,8 +163,6 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val) return 8600ULL; case HocClkConfigValue_LiteTDPLimit: return 6400ULL; - case HocClkConfigValue_TDPCycleLimit: - return 10ULL; default: return 0ULL; } @@ -192,8 +187,6 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in case SysClkConfigValue_FreqLogIntervalMs: case SysClkConfigValue_PowerLogIntervalMs: case SysClkConfigValue_CsvWriteIntervalMs: - case HocClkConfigValue_TDPCycleLimit: - return input >= 0; case HocClkConfigValue_UncappedClocks: case HocClkConfigValue_OverwriteBoostMode: case HocClkConfigValue_ThermalThrottle: diff --git a/Source/sys-clk/overlay/src/ui/gui/base_gui.cpp b/Source/sys-clk/overlay/src/ui/gui/base_gui.cpp index 545f402a..17a3c7ce 100644 --- a/Source/sys-clk/overlay/src/ui/gui/base_gui.cpp +++ b/Source/sys-clk/overlay/src/ui/gui/base_gui.cpp @@ -7,14 +7,14 @@ * * 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. + * 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 . * */ - + /* -------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * , , @@ -24,39 +24,141 @@ * -------------------------------------------------------------------------- */ - #include "base_gui.h" #include "../elements/base_frame.h" #include "logo_rgba_bin.h" - - +#include +#include #define LOGO_X 20 -#define LOGO_Y 45 -#define LOGO_LABEL_FONT_SIZE 35 +#define LOGO_Y 50 +#define LOGO_LABEL_FONT_SIZE 45 #define VERSION_X (LOGO_X + 250) -#define VERSION_Y LOGO_Y-40 +#define VERSION_Y (LOGO_Y - 40) #define VERSION_FONT_SIZE 15 std::string getVersionString() { - char buf[0x100] = ""; // 256 bytes — safe for any expected version string + char buf[0x100] = ""; Result rc = sysclkIpcGetVersionString(buf, sizeof(buf)); if (R_FAILED(rc) || buf[0] == '\0') { - return "unknown"; + return "HorizonOC-Misc"; } return std::string(buf); } -void BaseGui::preDraw(tsl::gfx::Renderer* renderer) +// --------------------------------------------- +// AQUATIC BLUE COLORS (4-bit color space) +// --------------------------------------------- +static constexpr tsl::Color dynamicLogoRGB1 = tsl::Color(0, 4, 8, 15); // Deep ocean blue +static constexpr tsl::Color dynamicLogoRGB2 = tsl::Color(7, 15, 15, 15); // Bright aqua cyan +static constexpr tsl::Color STATIC_AQUA = tsl::Color(2, 10, 12, 15); // Mid aqua + +// --------------------------------------------- +// FULLY ENHANCED ANIMATED LOGO EFFECT +// --------------------------------------------- +static s32 drawDynamicUltraText( + tsl::gfx::Renderer* renderer, + s32 startX, + s32 y, + u32 fontSize, + const tsl::Color& staticColor, + bool useNotificationMethod = false) { -// renderer->drawBitmap(LOGO_X, LOGO_Y, LOGO_WIDTH, LOGO_HEIGHT, logo_rgba_bin); - renderer->drawString("Horizon OC overlay", false, LOGO_X, LOGO_Y, LOGO_LABEL_FONT_SIZE, renderer->a(TEXT_COLOR)); -// renderer->drawString(TARGET_VERSION, false, VERSION_X, VERSION_Y, VERSION_FONT_SIZE, tsl::bannerVersionTextColor); + static constexpr double cycleDuration = 1.6; + + const std::string name = "Horizon OC Zeus"; + s32 currentX = startX; + + const u64 currentTime_ns = armTicksToNs(armGetSystemTick()); + const double timeNow = static_cast(currentTime_ns) / 1e9; + const double timeBase = fmod(timeNow, cycleDuration); + + const double waveScale = 2.0 * M_PI / cycleDuration; + + for (size_t i = 0; i < name.size(); i++) + { + char letter = name[i]; + if (letter == '\0') break; + + double phase = waveScale * (timeBase + i * 0.12); + + double raw = cos(phase); + double n = (raw + 1.0) * 0.5; + double s1 = n * n * (3.0 - 2.0 * n); + double blend = std::clamp(s1, 0.0, 1.0); + + // --------------------------------------------- + // Glow Pulse (brightness modulation) + // --------------------------------------------- + double glow = (cos(phase * 1.5) + 1.0) * 0.5; + double brightness = 0.75 + glow * 0.25; + + // --------------------------------------------- + // Color interpolation (4-bit!) + // --------------------------------------------- + u8 r = static_cast( + (dynamicLogoRGB1.r + (dynamicLogoRGB2.r - dynamicLogoRGB1.r) * blend) * brightness + ); + u8 g = static_cast( + (dynamicLogoRGB1.g + (dynamicLogoRGB2.g - dynamicLogoRGB1.g) * blend) * brightness + ); + u8 b = static_cast( + (dynamicLogoRGB1.b + (dynamicLogoRGB2.b - dynamicLogoRGB1.b) * blend) * brightness + ); + + r = std::clamp(r, 0, 15); + g = std::clamp(g, 0, 15); + b = std::clamp(b, 0, 15); + + // --------------------------------------------- + // ZEUS Lightning Flash + // --------------------------------------------- + bool lightning = (fmod(timeNow, 5.0) < 0.15); + if (lightning) { + r = std::min(r + 4, 15); + g = std::min(g + 4, 15); + b = std::min(b + 15, 15); // strong blue spike + } + + tsl::Color color(r, g, b, 15); + + // --------------------------------------------- + // Vertical Water Wobble + // --------------------------------------------- + s32 wobbleY = y + sin(phase) * 3; + + std::string ls(1, letter); + + if (useNotificationMethod) + currentX += renderer->drawNotificationString(ls, false, currentX, wobbleY, fontSize, color).first; + else + currentX += renderer->drawString(ls, false, currentX, wobbleY, fontSize, color).first; + } + + return currentX; } +// --------------------------------------------- +// PRE-DRAW HOOK +// --------------------------------------------- +void BaseGui::preDraw(tsl::gfx::Renderer* renderer) +{ + drawDynamicUltraText( + renderer, + LOGO_X, + LOGO_Y, + LOGO_LABEL_FONT_SIZE, + STATIC_AQUA, + false + ); +} + +// --------------------------------------------- +// UI SETUP +// --------------------------------------------- tsl::elm::Element* BaseGui::createUI() { BaseFrame* rootFrame = new BaseFrame(this); @@ -64,6 +166,9 @@ tsl::elm::Element* BaseGui::createUI() return rootFrame; } +// --------------------------------------------- +// LIVE UPDATE +// --------------------------------------------- void BaseGui::update() { this->refresh(); diff --git a/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp b/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp index 21f3e0bb..5e5f24d5 100644 --- a/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp +++ b/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.cpp @@ -37,8 +37,6 @@ BaseMenuGui::BaseMenuGui() : tempColors{tsl::Color(0), tsl::Color(0), tsl::Color this->lastContextUpdate = 0; this->listElement = nullptr; - // Initialize all voltages to zero once - memset(&cpuVoltageUv, 0, sizeof(u32) * 5); // Zero all 5 voltage values at once // Pre-cache hardware model during initialization IsMariko(); @@ -104,9 +102,12 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) { renderer->drawString(displayStrings[2], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU renderer->drawString(displayStrings[3], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU renderer->drawString(displayStrings[4], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // MEM - + y = 149; // Direct assignment (129 + 20) - + + renderer->drawString(displayStrings[17], false, positions[3], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU Usage + renderer->drawString(displayStrings[18], false, positions[4], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // RAM Usage + // === REAL FREQUENCIES === renderer->drawString(displayStrings[5], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU real renderer->drawString(displayStrings[6], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU real @@ -117,15 +118,8 @@ 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], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage - - // Memory voltage - check if VDD is present - if (emcVoltageUv && vddVoltageUv) { - renderer->drawStringWithColoredSections(displayStrings[10], false, {""}, dataPositions[5]-16, y, SMALL_TEXT_SIZE, tsl::infoTextColor, tsl::separatorColor); - } else if (vddVoltageUv) { - renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); - } else if (emcVoltageUv) { - renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); - } + + renderer->drawStringWithColoredSections(displayStrings[10], false, {""}, dataPositions[5]-16, y, SMALL_TEXT_SIZE, tsl::infoTextColor, tsl::separatorColor); y = 191; // Direct assignment (169 + 22) @@ -142,11 +136,7 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) { y = 211; // Direct assignment (191 + 20) - // === SOC VOLTAGE & POWER === - // SOC voltage (if available) - if (socVoltageUv) [[likely]] { - renderer->drawString(displayStrings[14], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); - } + renderer->drawString(displayStrings[14], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Power labels and values renderer->drawString(labels[8], false, positions[8]-1, y, SMALL_TEXT_SIZE, tsl::sectionTextColor); @@ -172,59 +162,6 @@ void BaseMenuGui::refresh() this->context = new SysClkContext; } - // === ULTRA-FAST VOLTAGE READING === - // Pre-computed domain configuration based on hardware - static const PowerDomainId domains[] = { - PcvPowerDomainId_Max77621_Cpu, // [0] CPU - PcvPowerDomainId_Max77621_Gpu, // [1] GPU - PcvPowerDomainId_Max77812_Dram, // [2] EMC/DRAM - Mariko only - PcvPowerDomainId_Max77620_Sd0, // [3] SOC - EOS only - PcvPowerDomainId_Max77620_Sd1 // [4] VDD2 - EOS only - }; - - // Voltage array for direct indexing - u32* voltages[] = {&cpuVoltageUv, &gpuVoltageUv, &emcVoltageUv, &socVoltageUv, &vddVoltageUv}; - - // Single regulator init/exit cycle - if (R_SUCCEEDED(rgltrInitialize())) [[likely]] { - if (IsMariko()) { - // Mariko with EOS: all 5 domains - for (int i = 0; i < 5; ++i) { - RgltrSession session; - if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] { - if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) { - *voltages[i] = 0; - } - rgltrCloseSession(&session); - } else { - *voltages[i] = 0; - } - } - } else { - // Erista - // Erista with EOS: CPU, GPU, SOC, VDD (no DRAM) - for (int i = 0; i < 5; ++i) { - if (i == 2) continue; // Skip DRAM domain - - RgltrSession session; - if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] { - if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) { - *voltages[i] = 0; - } - rgltrCloseSession(&session); - } else { - *voltages[i] = 0; - } - emcVoltageUv = 0; // Erista never supports DRAM - } - } - - rgltrExit(); - } else { - // Zero all voltages on regulator failure - memset(&cpuVoltageUv, 0, sizeof(u32) * 5); - } - // === SYSCLK CONTEXT UPDATE === const Result rc = sysclkIpcGetCurrentContext(this->context); if (R_FAILED(rc)) [[unlikely]] { @@ -240,39 +177,37 @@ void BaseMenuGui::refresh() strcpy(displayStrings[1], sysclkFormatProfile(context->profile, true)); // Current frequencies - u32 hz = context->freqs[0]; // CPU + u32 hz = context->freqs[SysClkModule_CPU]; // CPU sprintf(displayStrings[2], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U); - hz = context->freqs[1]; // GPU + hz = context->freqs[SysClkModule_GPU]; // GPU sprintf(displayStrings[3], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U); - - hz = context->freqs[2]; // MEM + + hz = context->freqs[SysClkModule_MEM]; // MEM sprintf(displayStrings[4], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U); // Real frequencies - hz = context->realFreqs[0]; // CPU + hz = context->realFreqs[SysClkModule_CPU]; // CPU sprintf(displayStrings[5], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U); - hz = context->realFreqs[1]; // GPU + hz = context->realFreqs[SysClkModule_GPU]; // GPU sprintf(displayStrings[6], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U); - hz = context->realFreqs[2]; // MEM + hz = context->realFreqs[SysClkModule_MEM]; // MEM sprintf(displayStrings[7], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U); // Voltages - sprintf(displayStrings[8], "%.1f mV", cpuVoltageUv / 1000.0); - sprintf(displayStrings[9], "%.1f mV", gpuVoltageUv / 1000.0); + sprintf(displayStrings[8], "%.1f mV", context->voltages[HocClkVoltage_CPU] / 1000.0); + sprintf(displayStrings[9], "%.1f mV", context->voltages[HocClkVoltage_GPU] / 1000.0); // Memory voltage (handle VDD case) - if (emcVoltageUv && vddVoltageUv) { + if (IsMariko()) { //sprintf(displayStrings[10], "%u%u mV", vddVoltageUv / 1000U, emcVoltageUv / 1000U); //sprintf(displayStrings[10], "%u%.1f mV", vddVoltageUv / 1000U, emcVoltageUv / 1000.0f); - sprintf(displayStrings[10], "%u.%u%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U, emcVoltageUv / 1000U); - } else if (vddVoltageUv) { + sprintf(displayStrings[10], "%u.%u%u mV", context->voltages[HocClkVoltage_EMCVDD2] / 1000U, (context->voltages[HocClkVoltage_EMCVDD2] % 1000U) / 100U, context->voltages[HocClkVoltage_EMCVDDQ_MarikoOnly] / 1000U); + } else { //sprintf(displayStrings[10], "%u mV", vddVoltageUv / 1000U); - sprintf(displayStrings[10], "%u.%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U); - } else if (emcVoltageUv) { - sprintf(displayStrings[10], "%u mV", emcVoltageUv / 1000U); + sprintf(displayStrings[10], "%u.%u%u mV", context->voltages[HocClkVoltage_EMCVDD2] / 1000U, (context->voltages[HocClkVoltage_EMCVDD2] % 1000U) / 100U, context->voltages[HocClkVoltage_EMCVDD2] / 1000U); } // Temperatures and pre-compute colors @@ -289,13 +224,16 @@ void BaseMenuGui::refresh() tempColors[2] = tsl::GradientColor(millis * 0.001f); // SOC voltage (if available) - if (socVoltageUv) { - sprintf(displayStrings[14], "%u mV", socVoltageUv / 1000U); - } + sprintf(displayStrings[14], "%u mV", context->voltages[HocClkVoltage_SOC] / 1000U); // Power sprintf(displayStrings[15], "%d mW", context->power[0]); // Now sprintf(displayStrings[16], "%d mW", context->power[1]); // Avg + + + sprintf(displayStrings[17], "%u%%", context->PartLoad[HocClkPartLoad_GPU] / 10); + sprintf(displayStrings[18], "%u%%", context->PartLoad[SysClkPartLoad_EMC] / 10); + } tsl::elm::Element* BaseMenuGui::baseUI() diff --git a/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.h b/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.h index 22a76c2a..225445d2 100644 --- a/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.h +++ b/Source/sys-clk/overlay/src/ui/gui/base_menu_gui.h @@ -36,11 +36,6 @@ class BaseMenuGui : public BaseGui protected: SysClkContext* context; std::uint64_t lastContextUpdate; - std::uint32_t cpuVoltageUv; - std::uint32_t gpuVoltageUv; - std::uint32_t emcVoltageUv; - std::uint32_t socVoltageUv; //add soc voltage - std::uint32_t vddVoltageUv;//add vdd2 voltage public: bool g_hardwareModelCached = false; @@ -71,6 +66,6 @@ class BaseMenuGui : public BaseGui virtual void listUI() = 0; private: - char displayStrings[17][32]; // Pre-formatted display strings + char displayStrings[32][32]; // Pre-formatted display strings tsl::Color tempColors[3]; // Pre-computed temperature colors }; diff --git a/Source/sys-clk/sysmodule/src/board.cpp b/Source/sys-clk/sysmodule/src/board.cpp index 11500be5..0a77af5e 100644 --- a/Source/sys-clk/sysmodule/src/board.cpp +++ b/Source/sys-clk/sysmodule/src/board.cpp @@ -28,13 +28,39 @@ #include #include "board.h" #include "errors.h" +#include "rgltr.h" +#include "file_utils.h" +#include // for std::clamp +#include +#include #define HOSSVC_HAS_CLKRST (hosversionAtLeast(8,0,0)) #define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0)) +#define NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD 0x80044715 + +#define systemtickfrequency 19200000 +#define systemtickfrequencyF 19200000.0f +#define CPU_TICK_WAIT (1000'000ULL) + +Result nvCheck = 1; + +Thread gpuLThread; +Thread cpuCore0Thread; +Thread cpuCore1Thread; +Thread cpuCore2Thread; +Thread cpuCore3Thread; + +uint32_t GPU_Load_u = 0, fd = 0; static SysClkSocType g_socType = SysClkSocType_Erista; static HorizonOCConsoleType g_consoleType = HorizonOCConsoleType_Unknown; +uint64_t idletick0 = systemtickfrequency; +uint64_t idletick1 = systemtickfrequency; +uint64_t idletick2 = systemtickfrequency; +uint64_t idletick3 = systemtickfrequency; +u32 cpu0, cpu1, cpu2, cpu3, cpuAvg; + const char* Board::GetModuleName(SysClkModule module, bool pretty) { ASSERT_ENUM_VALID(SysClkModule, module); @@ -85,6 +111,65 @@ PcvModuleId Board::GetPcvModuleId(SysClkModule sysclkModule) return pcvModuleId; } +void CheckCore0(void*) { + while(true) { + uint64_t idletick_a0 = 0; + uint64_t idletick_b0 = 0; + svcGetInfo(&idletick_b0, InfoType_IdleTickCount, INVALID_HANDLE, 0); + svcSleepThread(CPU_TICK_WAIT); + svcGetInfo(&idletick_a0, InfoType_IdleTickCount, INVALID_HANDLE, 0); + idletick0 = idletick_a0 - idletick_b0; + } +} + +void CheckCore1(void*) { + while(true) { + uint64_t idletick_a1 = 0; + uint64_t idletick_b1 = 0; + svcGetInfo(&idletick_b1, InfoType_IdleTickCount, INVALID_HANDLE, 1); + svcSleepThread(CPU_TICK_WAIT); + svcGetInfo(&idletick_a1, InfoType_IdleTickCount, INVALID_HANDLE, 1); + idletick1 = idletick_a1 - idletick_b1; + } +} + +void CheckCore2(void*) { + while(true) { + uint64_t idletick_a2 = 0; + uint64_t idletick_b2 = 0; + svcGetInfo(&idletick_b2, InfoType_IdleTickCount, INVALID_HANDLE, 2); + svcSleepThread(CPU_TICK_WAIT); + svcGetInfo(&idletick_a2, InfoType_IdleTickCount, INVALID_HANDLE, 2); + idletick2 = idletick_a2 - idletick_b2; + } +} + +void CheckCore3(void*) { + while(true) { + uint64_t idletick_a3 = 0; + uint64_t idletick_b3 = 0; + svcGetInfo(&idletick_b3, InfoType_IdleTickCount, INVALID_HANDLE, 3); + svcSleepThread(CPU_TICK_WAIT); + svcGetInfo(&idletick_a3, InfoType_IdleTickCount, INVALID_HANDLE, 3); + idletick3 = idletick_a3 - idletick_b3; + } +} + +void gpuLoadThread(void*) { + #define gpu_samples_average 8 + uint32_t gpu_load_array[gpu_samples_average] = {0}; + size_t i = 0; + if (R_SUCCEEDED(nvCheck)) do { + u32 temp; + if (R_SUCCEEDED(nvIoctl(fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &temp))) { + gpu_load_array[i++ % gpu_samples_average] = temp; + GPU_Load_u = std::accumulate(&gpu_load_array[0], &gpu_load_array[gpu_samples_average], 0) / gpu_samples_average; + } + svcSleepThread(16'666'000); // wait a bit (this is the perfect amount of time to keep the reading accurate) + } while(true); +} + + void Board::Initialize() { Result rc = 0; @@ -118,6 +203,24 @@ void Board::Initialize() rc = tmp451Initialize(); ASSERT_RESULT_OK(rc, "tmp451Initialize"); + if (R_SUCCEEDED(nvInitialize())) nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu"); + + threadCreate(&gpuLThread, gpuLoadThread, NULL, NULL, 0x1000, 0x3F, -2); + threadStart(&gpuLThread); + + threadCreate(&cpuCore0Thread, CheckCore0, NULL, NULL, 0x1000, 0x10, 0); + threadStart(&cpuCore0Thread); + + threadCreate(&cpuCore1Thread, CheckCore1, NULL, NULL, 0x1000, 0x10, 1); + threadStart(&cpuCore1Thread); + + threadCreate(&cpuCore2Thread, CheckCore2, NULL, NULL, 0x1000, 0x10, 2); + threadStart(&cpuCore2Thread); + + threadCreate(&cpuCore3Thread, CheckCore3, NULL, NULL, 0x1000, 0x10, 3); + threadStart(&cpuCore3Thread); + + FetchHardwareInfos(); } @@ -142,6 +245,13 @@ void Board::Exit() max17050Exit(); tmp451Exit(); + + threadClose(&gpuLThread); + threadClose(&cpuCore0Thread); + threadClose(&cpuCore1Thread); + threadClose(&cpuCore2Thread); + threadClose(&cpuCore3Thread); + } SysClkProfile Board::GetProfile() @@ -464,21 +574,26 @@ std::int32_t Board::GetPowerMw(SysClkPowerSensor sensor) return 0; } -std::uint32_t Board::GetRamLoad(SysClkRamLoad loadSource) -{ +std::uint32_t Board::GetPartLoad(SysClkPartLoad loadSource) +{ switch(loadSource) { - case SysClkRamLoad_All: + case SysClkPartLoad_EMC: return t210EmcLoadAll(); - case SysClkRamLoad_Cpu: + case SysClkPartLoad_EMCCpu: return t210EmcLoadCpu(); + case HocClkPartLoad_GPU: + return GPU_Load_u; + case HocClkPartLoad_CPUAvg: + return (idletick0 + idletick1 + idletick2 + idletick3) / 4; default: - ASSERT_ENUM_VALID(SysClkRamLoad, loadSource); + ASSERT_ENUM_VALID(SysClkPartLoad, loadSource); } return 0; } + SysClkSocType Board::GetSocType() { return g_socType; } @@ -500,12 +615,108 @@ void Board::FetchHardwareInfos() switch(sku) { - case 2 .. 5: + case 2: + case 3: + case 4: + case 5: g_socType = SysClkSocType_Mariko; break; default: g_socType = SysClkSocType_Erista; } - g_consoleType = sku; -} \ No newline at end of file + g_consoleType = (HorizonOCConsoleType)sku; +} + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | 0.85V (AO, pcv) +* ldo5 | GC Card | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) +* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V (pcv) +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo8 | XUSB, DP, MCU | 50000 | 800000 | 1050000 | 2800000 | 1.05V/2.8V (pcv) + +typedef enum { + PcvPowerDomainId_Max77620_Sd0 = 0x3A000080, + PcvPowerDomainId_Max77620_Sd1 = 0x3A000081, // vdd2 + PcvPowerDomainId_Max77620_Sd2 = 0x3A000082, + PcvPowerDomainId_Max77620_Sd3 = 0x3A000083, + PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0, + PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1, + PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2, + PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3, + PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4, + PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5, + PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6, + PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7, + PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8, + PcvPowerDomainId_Max77621_Cpu = 0x3A000003, + PcvPowerDomainId_Max77621_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Cpu = 0x3A000003, + PcvPowerDomainId_Max77812_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Dram = 0x3A000005, // vddq +} PowerDomainId; + +*/ + +std::uint32_t Board::GetVoltage(HocClkVoltage voltage) +{ + RgltrSession session; + Result rc = 0; + u32 out; + switch(voltage) + { + case HocClkVoltage_SOC: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd0); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_EMCVDD2: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd1); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_CPU: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Cpu); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_GPU: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Gpu); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_EMCVDDQ_MarikoOnly: + if(Board::GetSocType() == SysClkSocType_Mariko) { + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Dram); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + } + break; + case HocClkVoltage_Display: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Ldo0); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + default: + ASSERT_ENUM_VALID(HocClkVoltage, voltage); + } + + return out > 0 ? out : 0; +} diff --git a/Source/sys-clk/sysmodule/src/board.h b/Source/sys-clk/sysmodule/src/board.h index 625786ef..575b9d37 100644 --- a/Source/sys-clk/sysmodule/src/board.h +++ b/Source/sys-clk/sysmodule/src/board.h @@ -51,9 +51,10 @@ class Board static void GetFreqList(SysClkModule module, std::uint32_t* outList, std::uint32_t maxCount, std::uint32_t* outCount); static std::uint32_t GetTemperatureMilli(SysClkThermalSensor sensor); static std::int32_t GetPowerMw(SysClkPowerSensor sensor); - static std::uint32_t GetRamLoad(SysClkRamLoad load); + static std::uint32_t GetPartLoad(SysClkPartLoad load); static SysClkSocType GetSocType(); static HorizonOCConsoleType GetConsoleType(); + static std::uint32_t GetVoltage(HocClkVoltage voltage); protected: static void FetchHardwareInfos(); diff --git a/Source/sys-clk/sysmodule/src/clock_manager.cpp b/Source/sys-clk/sysmodule/src/clock_manager.cpp index b769d75f..86dd418c 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.cpp +++ b/Source/sys-clk/sysmodule/src/clock_manager.cpp @@ -227,6 +227,8 @@ void ClockManager::RefreshFreqTableRow(SysClkModule module) void ClockManager::Tick() { std::scoped_lock lock{this->contextMutex}; + AppletOperationMode opMode = appletGetOperationMode(); + if(this->config->GetConfigValue(HocClkConfigValue_HandheldTDP) && opMode == AppletOperationMode_Handheld) { if(Board::GetConsoleType() == HorizonOCConsoleType_Lite) { if(Board::GetPowerMw(SysClkPowerSensor_Now) < -(int)this->config->GetConfigValue(HocClkConfigValue_LiteTDPLimit)) { @@ -254,7 +256,6 @@ void ClockManager::Tick() std::uint32_t nearestHz = 0; std::uint32_t mode = 0; - AppletOperationMode opMode = appletGetOperationMode(); Result rc = apmExtGetCurrentPerformanceConfiguration(&mode); ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); @@ -427,9 +428,14 @@ bool ClockManager::RefreshContext() } // ram load do not and should not force a refresh, hasChanged untouched - for (unsigned int loadSource = 0; loadSource < SysClkRamLoad_EnumMax; loadSource++) + for (unsigned int loadSource = 0; loadSource < SysClkPartLoad_EnumMax; loadSource++) { - this->context->ramLoad[loadSource] = Board::GetRamLoad((SysClkRamLoad)loadSource); + this->context->PartLoad[loadSource] = Board::GetPartLoad((SysClkPartLoad)loadSource); + } + + for (unsigned int voltageSource = 0; voltageSource < HocClkVoltage_EnumMax; voltageSource++) + { + this->context->voltages[voltageSource] = Board::GetVoltage((HocClkVoltage)voltageSource); } if (this->ConfigIntervalTimeout(SysClkConfigValue_CsvWriteIntervalMs, ns, &this->lastCsvWriteNs)) diff --git a/Source/sys-clk/sysmodule/src/main.cpp b/Source/sys-clk/sysmodule/src/main.cpp index 5c10a49f..bfb96661 100644 --- a/Source/sys-clk/sysmodule/src/main.cpp +++ b/Source/sys-clk/sysmodule/src/main.cpp @@ -47,9 +47,11 @@ extern "C" std::uint32_t __nx_applet_type = AppletType_None; TimeServiceType __nx_time_service_type = TimeServiceType_System; std::uint32_t __nx_fs_num_sessions = 1; - + u32 __nx_nv_transfermem_size = 0x8000; size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; + NvServiceType __nx_nv_service_type = NvServiceType_Factory; + void __libnx_initheap(void) { diff --git a/Source/sys-clk/sysmodule/src/notification.h b/Source/sys-clk/sysmodule/src/notification.h new file mode 100644 index 00000000..479fde6c --- /dev/null +++ b/Source/sys-clk/sysmodule/src/notification.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +static void writeNotification(const std::string& message) { + const char* flagPath = "sdmc:/config/ultrahand/flags/NOTIFICATIONS.flag"; + + FILE* flagFile = fopen(flagPath, "r"); + if (!flagFile) { + return; + } + fclose(flagFile); + + std::string filename = "Horizon OC -" + std::to_string(std::time(nullptr)) + ".notify"; + std::string fullPath = "sdmc:/config/ultrahand/notifications/" + filename; + + FILE* file = fopen(fullPath.c_str(), "w"); + if (file) { + fprintf(file, "{\n"); + fprintf(file, " \"text\": \"%s\",\n", message.c_str()); + fprintf(file, " \"fontSize\": 28\n"); + fprintf(file, "}\n"); + fclose(file); + } + } diff --git a/Source/sys-clk/sysmodule/src/pcv_types.h b/Source/sys-clk/sysmodule/src/pcv_types.h new file mode 100644 index 00000000..4258cb9d --- /dev/null +++ b/Source/sys-clk/sysmodule/src/pcv_types.h @@ -0,0 +1,95 @@ +#pragma once +/* + * SDx actual min is 625 mV. Multipliers 0/1 reserved. + * SD0 max is 1400 mV + * SD1 max is 1550 mV + * SD2 max is 3787.5 mV + * SD3 max is 3787.5 mV + */ + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | 0.85V (AO, pcv) +* ldo5 | GC Card | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) +* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V (pcv) +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo8 | XUSB, DP, MCU | 50000 | 800000 | 1050000 | 2800000 | 1.05V/2.8V (pcv) +*/ + + +// GPIOs T210: 3: 3.3V, 5: CPU PMIC, 6: GPU PMIC, 7: DSI/VI 1.2V powered by ldo0. + +/* + * OTP: T210 - T210B01: + * SD0: 1.0V 1.05V - SoC. EN Based on FPSSRC. + * SD1: 1.15V 1.1V - DRAM for T210. EN Based on FPSSRC. + * SD2: 1.35V 1.35V + * SD3: 1.8V 1.8V + * All powered off? + * LDO0: -- -- - Display + * LDO1: 1.05V 1.05V + * LDO2: -- -- - SD + * LDO3: 3.1V 3.1V - GC ASIC + * LDO4: 1.0V 0.8V - Needed for RTC domain on T210. + * LDO5: 3.1V 3.1V + * LDO6: 2.8V 2.9V - Touch. + * LDO7: 1.05V 1.0V + * LDO8: 1.05V 1.0V + */ + +/* +* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode +* MAX77620_REG_GPIOx: 0x9 sets output and enable +*/ + +typedef enum { + PcvPowerDomain_Max77620_Sd0 = 0, + PcvPowerDomain_Max77620_Sd1 = 1, + PcvPowerDomain_Max77620_Sd2 = 2, + PcvPowerDomain_Max77620_Sd3 = 3, + PcvPowerDomain_Max77620_Ldo0 = 4, + PcvPowerDomain_Max77620_Ldo1 = 5, + PcvPowerDomain_Max77620_Ldo2 = 6, + PcvPowerDomain_Max77620_Ldo3 = 7, + PcvPowerDomain_Max77620_Ldo4 = 8, + PcvPowerDomain_Max77620_Ldo5 = 9, + PcvPowerDomain_Max77620_Ldo6 = 10, + PcvPowerDomain_Max77620_Ldo7 = 11, + PcvPowerDomain_Max77620_Ldo8 = 12, + PcvPowerDomain_Max77621_Cpu = 13, + PcvPowerDomain_Max77621_Gpu = 14, + PcvPowerDomain_Max77812_Cpu = 15, + PcvPowerDomain_Max77812_Gpu = 16, + PcvPowerDomain_Max77812_Dram = 17, +} PowerDomain; + +typedef enum { + PcvPowerDomainId_Max77620_Sd0 = 0x3A000080, + PcvPowerDomainId_Max77620_Sd1 = 0x3A000081, // vdd2 + PcvPowerDomainId_Max77620_Sd2 = 0x3A000082, + PcvPowerDomainId_Max77620_Sd3 = 0x3A000083, + PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0, + PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1, + PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2, + PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3, + PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4, + PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5, + PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6, + PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7, + PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8, + PcvPowerDomainId_Max77621_Cpu = 0x3A000003, + PcvPowerDomainId_Max77621_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Cpu = 0x3A000003, + PcvPowerDomainId_Max77812_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Dram = 0x3A000005, // vddq +} PowerDomainId; \ No newline at end of file diff --git a/Source/sys-clk/sysmodule/src/rgltr.h b/Source/sys-clk/sysmodule/src/rgltr.h new file mode 100644 index 00000000..dd57f4c5 --- /dev/null +++ b/Source/sys-clk/sysmodule/src/rgltr.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include "pcv_types.h" + +typedef struct { + Service s; +} RgltrSession; + +Result rgltrInitialize(void); + +void rgltrExit(void); + +Service* rgltrGetServiceSession(void); + +Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id); +void rgltrCloseSession(RgltrSession* session); +Result rgltrGetVoltage(RgltrSession* session, u32 *out_volt); +Result rgltrGetPowerModuleNumLimit(u32 *out); +Result rgltrGetVoltageEnabled(RgltrSession* session, u32 *out); diff --git a/Source/sys-clk/sysmodule/src/rgltr_services.cpp b/Source/sys-clk/sysmodule/src/rgltr_services.cpp new file mode 100644 index 00000000..b104db18 --- /dev/null +++ b/Source/sys-clk/sysmodule/src/rgltr_services.cpp @@ -0,0 +1,41 @@ +#include +#include "rgltr.h" +#include "rgltr_services.h" // for extern Service g_rgltrSrv, etc. + +// Global service handle +Service g_rgltrSrv; + +Result rgltrInitialize(void) { + if (hosversionBefore(8, 0, 0)) { + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + } + return smGetService(&g_rgltrSrv, "rgltr"); +} + +void rgltrExit(void) { + serviceClose(&g_rgltrSrv); +} + +Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id) { + const u32 in = (u32)module_id; + return serviceDispatchIn( + &g_rgltrSrv, + 0, + in, + .out_num_objects = 1, + .out_objects = &session_out->s + ); +} + +Result rgltrGetVoltage(RgltrSession* session, u32* out_volt) { + u32 temp = 0; + Result rc = serviceDispatchOut(&session->s, 4, temp); + if (R_SUCCEEDED(rc)) { + *out_volt = temp; + } + return rc; +} + +void rgltrCloseSession(RgltrSession* session) { + serviceClose(&session->s); +} diff --git a/Source/sys-clk/sysmodule/src/rgltr_services.h b/Source/sys-clk/sysmodule/src/rgltr_services.h new file mode 100644 index 00000000..963b781e --- /dev/null +++ b/Source/sys-clk/sysmodule/src/rgltr_services.h @@ -0,0 +1,15 @@ +#pragma once + +#include // for Service, Result, hosversionBefore(), smGetService(), serviceClose(), etc. +#include "rgltr.h" // for RgltrSession, PowerDomainId, etc. + +extern Service g_rgltrSrv; + +Result rgltrInitialize(void); +void rgltrExit(void); + +Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id); + +Result rgltrGetVoltage(RgltrSession* session, u32* out_volt); + +void rgltrCloseSession(RgltrSession* session);