From a637c0460a99eb0db74723469df6c4211c46010b Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Fri, 12 Dec 2025 18:16:15 +0100 Subject: [PATCH] UV2 with dynamic voltages --- .../loader/source/oc/customize.cpp | 127 +++++++++--------- .../loader/source/oc/customize.hpp | 5 +- .../stratosphere/loader/source/oc/pcv/pcv.hpp | 72 +++++++++- .../loader/source/oc/pcv/pcv_erista.cpp | 8 +- .../loader/source/oc/pcv/pcv_mariko.cpp | 76 +++++++++-- 5 files changed, 202 insertions(+), 86 deletions(-) diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 9c6ed7ce..6c8243f0 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -20,6 +20,7 @@ #include "customize.hpp" +#define AUTO 0 #define ENABLED 1 #define DISABLED 0 #define CPU_MAX_MAX_VOLT 1235000 @@ -63,22 +64,26 @@ volatile CustomizeTable C = { .marikoCpuUVLow = 0, // No undervolt .marikoCpuUVHigh = 0, // No undervolt .tableConf = DEFAULT_TABLE, /* TODO: Add AUTO */ -.marikoCpuLowVmin = 610, +.marikoCpuLowVmin = 620, .marikoCpuHighVmin = 750, .marikoCpuMaxVolt = 1120, -.marikoCpuBoostClock = 1963000, // Default boost clock .eristaCpuBoostClock = 1785000, // Default boost clock +.marikoCpuBoostClock = 1963000, // Default boost clock .eristaGpuUV = 0, .eristaGpuVmin = 810, .marikoGpuUV = 0, +/* For automatic vmin detection, set this to AUTO. */ .marikoGpuVmin = 610, + .marikoGpuVmax = 800, .commonGpuVoltOffset = 0, +.gpuSpeedo = 1450, + /* >1305 GPU unlock. */ /* WARNING! This removes ALL gpu frequency limits and risks permanent hardware damage. */ /* This setting is very dangerous and can damage your pmic, degrade your soc, damage the voltage rails and can cause various other damage. */ @@ -406,72 +411,70 @@ volatile CustomizeTable C = { * 1305600 might not work for some SoCs. */ .marikoGpuDvfsTable = { - { 76800, {}, { 610000, } }, - { 153600, {}, { 610000, } }, - { 230400, {}, { 610000, } }, - { 307200, {}, { 610000, } }, - { 384000, {}, { 610000, } }, - { 460800, {}, { 610000, } }, - { 537600, {}, { 801688, -10900, -163, 298, -10599, 162 } }, - { 614400, {}, { 824214, -5743, -452, 238, -6325, 81 } }, - { 691200, {}, { 848830, -3903, -552, 119, -4030, -2 } }, - { 768000, {}, { 891575, -4409, -584, 0, -2849, 39 } }, - { 844800, {}, { 940071, -5367, -602, -60, -63, -93 } }, - { 921600, {}, { 986765, -6637, -614, -179, 1905, -13 } }, - { 998400, {}, { 1098475, -13529, -497, -179, 3626, 9 } }, - { 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40 } }, - { 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110 } }, - { 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313 } }, - // { 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559 } }, - // Appending table - //{ 1305600, {}, { 1374130, -13725, -859, 0, 4442, 576 } }, + { 76800, { }, { 480000, } }, + { 153600, { }, { 480000, } }, + { 230400, { }, { 480000, } }, + { 307200, { }, { 738712, -7304, -552, 119, -3750, -2 } }, + { 384000, { }, { 758712, -7304, -552, 119, -3750, -2 } }, + { 460800, { }, { 778712, -7304, -552, 119, -3750, -2 } }, + { 537600, { }, { 798712, -7304, -552, 119, -3750, -2 } }, + { 614400, { }, { 818712, -7304, -552, 119, -3750, -2 } }, + { 691200, { }, { 838712, -7304, -552, 119, -3750, -2 } }, + { 768000, { }, { 880210, -7955, -584, 0, -2849, 39 } }, + { 844800, { }, { 926398, -8892, -602, -60, -384, -93 } }, + { 921600, { }, { 970060,-10108, -614,-179, 1508, -13 } }, + { 998400, { }, { 1065665,-16075, -497,-179, 3213, 9 } }, + { 1075200, { }, { 1132576,-16093, -648, 0, 1077, 40 } }, + { 1152000, { }, { 1180029,-14534, -830, 0, 1469, 110 } }, + { 1228800, { }, { 1248293,-16383, -859, 0, 3722, 313 } }, + { 1267200, { }, { 1286399,-17475, -867, 0, 3681, 559 } }, }, .marikoGpuDvfsTableSLT = { - { 76800, {}, { 600000, } }, - { 153600, {}, { 600000, } }, - { 230400, {}, { 600000, } }, - { 307200, {}, { 600000, } }, - { 384000, {}, { 600000, } }, - { 460800, {}, { 795089, -11096, -163, 298, -10421, 162 } }, - { 537600, {}, { 795089, -11096, -163, 298, -10421, 162 } }, - { 614400, {}, { 820606, -6285, -452, 238, -6182, 81 } }, - { 691200, {}, { 846289, -4565, -552, 119, -3958, -2 } }, - { 768000, {}, { 888720, -5110, -584, 0, -2849, 39 } }, - { 844800, {}, { 936634, -6089, -602, -60, -99, -93 } }, - { 921600, {}, { 982562, -7373, -614, -179, 1797, -13 } }, - { 998400, {}, { 1090179, -14125, -497, -179, 3518, 9 } }, - { 1075200, {}, { 1155798, -13465, -648, 0, 1077, 40 } }, - { 1152000, {}, { 1198568, -10904, -830, 0, 1469, 110 } }, - { 1228800, {}, { 1269988, -12707, -859, 0, 3722, 313 } }, - { 1267200, {}, { 1308155, -13694, -867, 0, 3681, 559 } }, + { 76800, { }, { 480000, } }, + { 153600, { }, { 480000, } }, + { 230400, { }, { 480000, } }, + { 307200, { }, { 738712, -7304, -552, 119, -3750, -2 } }, + { 384000, { }, { 758712, -7304, -552, 119, -3750, -2 } }, + { 460800, { }, { 778712, -7304, -552, 119, -3750, -2 } }, + { 537600, { }, { 798712, -7304, -552, 119, -3750, -2 } }, + { 614400, { }, { 818712, -7304, -552, 119, -3750, -2 } }, + { 691200, { }, { 838712, -7304, -552, 119, -3750, -2 } }, + { 768000, { }, { 880210, -7955, -584, 0, -2849, 39 } }, + { 844800, { }, { 926398, -8892, -602, -60, -384, -93 } }, + { 921600, { }, { 970060, -10108, -614, -179, 1508, -13 } }, + { 998400, { }, { 1065665, -16075, -497, -179, 3213, 9 } }, + { 1075200, { }, { 1132576, -16093, -648, 0, 1077, 40 } }, + { 1152000, { }, { 1180029, -14534, -830, 0, 1469, 110 } }, + { 1228800, { }, { 1238293, -16383, -859, 0, 3722, 313 } }, + { 1267200, { }, { 1276399, -17475, -867, 0, 3681, 559 } }, }, .marikoGpuDvfsTableHiOPT = { - { 76800, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 153600, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 230400, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 307200, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 384000, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 460800, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 537600, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 614400, { }, { 590000, 0, 0, 0, 0, 0 }, }, - { 691200, { }, { 838712, -7304, -552, 119, -3750, -2 }, }, - { 768000, { }, { 880210, -7955, -584, 0, -2849, 39 }, }, - { 844800, { }, { 926398, -8892, -602, -60, -384, -93 }, }, - { 921600, { }, { 970060, -10108, -614, -179, 1508, -13 }, }, - { 998400, { }, { 1060665, -16075, -497, -179, 3213, 9 }, }, - { 1075200, { }, { 1117576, -16093, -648, 0, 1077, 40 }, }, - { 1152000, { }, { 1094475, -12688, -648, 0, 1077, 40 }, }, - { 1228800, { }, { 1124475, -12688, -648, 0, 1077, 40 }, }, - { 1267200, { }, { 1145060, -12688, -648, 0, 1077, 40 }, }, - { 1305600, { }, { 1163644, -12688, -648, 0, 1077, 40 }, }, - { 1344000, { }, { 1182228, -12688, -648, 0, 1077, 40 }, }, - { 1382400, { }, { 1200812, -12688, -648, 0, 1077, 40 }, }, - { 1420800, { }, { 1219396, -12688, -648, 0, 1077, 40 }, }, - { 1459200, { }, { 1237980, -12688, -648, 0, 1077, 40 }, }, - { 1497600, { }, { 1256564, -12688, -648, 0, 1077, 40 }, }, - { 1536000, { }, { 1275148, -12688, -648, 0, 1077, 40 }, }, + { 76800, { }, { 480000, } }, + { 153600, { }, { 480000, } }, + { 230400, { }, { 480000, } }, + { 307200, { }, { 738712, -7304, -552, 119, -3750, -2 } }, + { 384000, { }, { 758712, -7304, -552, 119, -3750, -2 } }, + { 460800, { }, { 778712, -7304, -552, 119, -3750, -2 } }, + { 537600, { }, { 798712, -7304, -552, 119, -3750, -2 } }, + { 614400, { }, { 818712, -7304, -552, 119, -3750, -2 } }, + { 691200, { }, { 838712, -7304, -552, 119, -3750, -2 } }, + { 768000, { }, { 880210, -7955, -584, 0, -2849, 39 } }, + { 844800, { }, { 926398, -8892, -602, -60, -384, -93 } }, + { 921600, { }, { 970060, -10108, -614, -179, 1508, -13 } }, + { 998400, { }, { 1060665, -16075, -497, -179, 3213, 9 } }, + { 1075200, { }, { 1061475, -12688, -648, 0, 1077, 40 } }, + { 1152000, { }, { 1094475, -12688, -648, 0, 1077, 40 } }, + { 1228800, { }, { 1124475, -12688, -648, 0, 1077, 40 } }, + { 1267200, { }, { 1142060, -12688, -648, 0, 1077, 40 } }, + { 1305600, { }, { 1163644, -12688, -648, 0, 1077, 40 } }, + { 1344000, { }, { 0, 0, 0, 0, 0, 0 } }, + { 1382400, { }, { 0, 0, 0, 0, 0, 0 } }, + { 1420800, { }, { 0, 0, 0, 0, 0, 0 } }, + { 1459200, { }, { 0, 0, 0, 0, 0, 0 } }, + { 1497600, { }, { 0, 0, 0, 0, 0, 0 } }, + { 1536000, { }, { 0, 0, 0, 0, 0, 0 } }, }, }; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index 6252ce1e..7e47b98d 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -87,8 +87,8 @@ typedef struct CustomizeTable { u32 marikoCpuHighVmin; u32 marikoCpuMaxVolt; - u32 marikoCpuBoostClock; u32 eristaCpuBoostClock; + u32 marikoCpuBoostClock; u32 eristaGpuUV; u32 eristaGpuVmin; @@ -99,6 +99,9 @@ typedef struct CustomizeTable { u32 commonGpuVoltOffset; + /* TODO: Automatically detect speedo. */ + u32 gpuSpeedo; + u32 marikoGpuFullUnlock; u32 eristaGpuVoltArray[27]; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index 28e159bf..bf525ed1 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -48,8 +48,9 @@ namespace ams::ldr::oc::pcv { { }, }; - constexpr int gpuVmax = 750; - constexpr int gpuVmin = 610; + constexpr u32 CpuClkOfficial = 1963'500; + constexpr u32 CpuVoltOfficial = 1120; + constexpr u32 CpuVminOfficial = 620; constexpr u32 CpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 }; constexpr s32 CpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 }; @@ -59,9 +60,6 @@ namespace ams::ldr::oc::pcv { constexpr s32 CpuVoltageSecondaryPatchOffsets[] = { -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; static_assert(sizeof(CpuVoltageSecondaryPatchValues) == sizeof(CpuVoltageSecondaryPatchOffsets), "Invalid secondary CpuVoltagePatch size"); - constexpr u32 CpuClkOfficial = 1963'500; - constexpr u32 CpuVoltOfficial = 1120; - constexpr cvb_entry_t GpuCvbTableDefault[] = { // GPUB01_NA_CVB_TABLE { 76800, {}, { 610000, } }, @@ -86,8 +84,68 @@ namespace ams::ldr::oc::pcv { constexpr u32 GpuClkPllMax = 1300'000'000; constexpr u32 GpuClkPllLimit = 2'600'000; + constexpr int GpuVminOfficial = 610; - constexpr u32 CpuVminOfficial = 620; + constexpr u32 GpuDVFSPattern[] = { 1050, 1000, 100, 1000, 10, }; + constexpr u32 GpuVoltThermalPattern[] = { 800, 1120, 0, 610, 1120, 20000, 610, 1120, 30000, 610, 1120, 50000, 610, 1120, 70000, 610, 1120, 90000, }; + static_assert(sizeof(GpuVoltThermalPattern) == 72, "Invalid GpuVoltThermalPattern"); + + struct SpeedoVminTable { + u32 speedo; + u32 voltage; + }; + + struct RamVminOffsetTable { + u32 maxClock; + u32 offset; + }; + + constexpr u32 GetGpuVminVoltage() { + constexpr SpeedoVminTable Table[] { + {1560, 590}, + {1583, 570}, + {1620, 565}, + {1670, 560}, + {1694, 555}, + {1731, 550}, + {1750, 540}, + {0xFFFFFFFF, 530}, + }; + + for (auto e : Table) { + if (C.gpuSpeedo <= e.speedo) { + return e.voltage; + } + } + + return 530; + } + + constexpr u32 GetRamVminAdjustment(u32 vmin) { + if (C.marikoEmcMaxClock < 2133000) { + return vmin; + } + + const u32 ramScale = (((C.marikoEmcMaxClock / 1000) - 2133) / 33) * 5 + vmin; + + constexpr RamVminOffsetTable RamOffset[] { + {2400000, 5}, + {2533000, 10}, + {2666000, 15}, + {2800000, 20}, + {2933000, 25}, + {3200000, 30}, + {0xFFFFFFFF, 35}, + }; + + for (auto r : RamOffset) { + if (C.marikoEmcMaxClock < r.maxClock) { + return ramScale + r.offset; + } + } + + return ramScale; + } /* GPU Max Clock asm Pattern: * @@ -157,7 +215,7 @@ namespace ams::ldr::oc::pcv { { }, }; - constexpr int gpuVmin = 810; + constexpr int GpuVminOfficial = 810; constexpr u32 CpuVoltOfficial = 1235; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp index 68532707..7d15f7a7 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -473,9 +473,9 @@ namespace ams::ldr::oc::pcv::erista { // using namespace pcv::erista; // // #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) // note: add backslashes to make the macro definition work - // TABLE->burst_regs.PARAM = VALUE; - // TABLE->shadow_regs_ca_train.PARAM = VALUE; - // TABLE->shadow_regs_quse_train.PARAM = VALUE; + // TABLE->burst_regs.PARAM = VALUE; + // TABLE->shadow_regs_ca_train.PARAM = VALUE; + // TABLE->shadow_regs_quse_train.PARAM = VALUE; // TABLE->shadow_regs_rdwr_train.PARAM = VALUE; // #define GET_CYCLE(PARAM) ((u32)((double)(PARAM) / (1000000.0 / 1600000.0))) @@ -726,7 +726,7 @@ namespace ams::ldr::oc::pcv::erista { {"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit}, {"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit}, {"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS}, - {"GPU Vmin", &GpuVmin, 0, nullptr, gpuVmin}, + {"GPU Vmin", &GpuVmin, 0, nullptr, GpuVminOfficial}, }; for (uintptr_t ptr = mapped_nso; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp index f7365b39..2c1bb93c 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -24,17 +24,70 @@ namespace ams::ldr::oc::pcv::mariko { - Result GpuVmin(u32 *ptr) { - if (!C.marikoGpuVmin) - R_SKIP(); - PATCH_OFFSET(ptr, (int)C.marikoGpuVmin); + /* Note: EOS (probably?) has a bug in this function that always results in high vmin, this is fixed here. */ + u32 GetAutoVoltage() { + u32 voltage = GetGpuVminVoltage(); + voltage = GetRamVminAdjustment(voltage); + + u32 voltageOffset = 590 - C.commonGpuVoltOffset; + + if (voltageOffset < voltage) { + voltage = voltageOffset; + } + + return voltage; + } + + Result GpuVoltDVFS(u32 *ptr) { + /* Check for valid pattern. */ + for (size_t i = 0; i < std::size(GpuDVFSPattern); ++i) { + if (*(ptr + i + 1) != GpuDVFSPattern[i]) { + R_THROW(ldr::ResultInvalidGpuDvfs()); + } + } + + /* Default value is 800mV. */ + if (C.marikoGpuVmax) { + PATCH_OFFSET(ptr + 1, C.marikoGpuVmax); + } + + /* C.marikoGpuVmin is non zero, user sets manual voltage. */ + if (C.marikoGpuVmin) { + PATCH_OFFSET(ptr, C.marikoGpuVmin); + R_SUCCEED(); + } + + /* C.marikoGpuVmin is zero, auto voltage is applied. */ + /* Get vmin depending on speedo and ram clock. */ + u32 autoVmin = GetAutoVoltage(); + PATCH_OFFSET(ptr, autoVmin); R_SUCCEED(); } - Result GpuVmax(u32 *ptr) { - if (!C.marikoGpuVmax) - R_SKIP(); - PATCH_OFFSET(ptr, (int)C.marikoGpuVmax); + Result GpuVoltThermals(u32 *ptr) { + u32 vmin = std::memcmp(ptr - 3, GpuVoltThermalPattern, sizeof(GpuVoltThermalPattern)); + if (vmin) { + R_THROW(ldr::ResultInvalidGpuDvfs()); + } + + /* Automatic voltage. */ + if (!C.marikoGpuVmin) { + vmin = GetAutoVoltage(); + PATCH_OFFSET(ptr, vmin); + PATCH_OFFSET(ptr + 3, vmin); + PATCH_OFFSET(ptr + 6, vmin); + PATCH_OFFSET(ptr + 9, vmin); + } else { + /* Manual voltage. */ + PATCH_OFFSET(ptr, C.marikoGpuVmin); + PATCH_OFFSET(ptr + 3, C.marikoGpuVmin); + PATCH_OFFSET(ptr + 6, C.marikoGpuVmin); + PATCH_OFFSET(ptr + 9, C.marikoGpuVmin); + vmin = C.marikoGpuVmin; + } + + PATCH_OFFSET(ptr + 12, vmin); + R_SUCCEED(); } @@ -944,9 +997,10 @@ namespace ams::ldr::oc::pcv::mariko { PatcherEntry patches[] = { {"CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit}, {"CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq}, - {"CPU Volt DVFS", &CpuVoltDVFS, 2, nullptr, /*CpuVminOfficial*/ 620}, - // {"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, /*CpuVminOfficial*/ 620}, + {"CPU Volt DVFS", &CpuVoltDVFS, 2, nullptr, CpuVminOfficial}, {"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0x0000FFCF}, + {"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial}, + {"Gpu Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial}, {"GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq}, {"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn}, {"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax}, @@ -957,8 +1011,6 @@ namespace ams::ldr::oc::pcv::mariko { {"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit}, {"MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault}, {"MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default}, - {"GPU Vmin", &GpuVmin, 0, nullptr, gpuVmin}, - {"GPU Vmax", &GpuVmax, 0, nullptr, gpuVmax}, }; for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {