This commit is contained in:
souldbminersmwc
2026-02-24 16:22:03 -05:00
7 changed files with 382 additions and 366 deletions

View File

@@ -80,7 +80,7 @@ volatile CustomizeTable C = {
.marikoCpuUVLow = 0, // No undervolt .marikoCpuUVLow = 0, // No undervolt
.marikoCpuUVHigh = 0, // No undervolt .marikoCpuUVHigh = 0, // No undervolt
.tableConf = DEFAULT_TABLE, .tableConf = TBREAK_1683,
.marikoCpuLowVmin = 620, .marikoCpuLowVmin = 620,
.marikoCpuHighVmin = 750, .marikoCpuHighVmin = 750,
/* 1120mV is NVIDIA rating */ /* 1120mV is NVIDIA rating */
@@ -98,7 +98,7 @@ volatile CustomizeTable C = {
.eristaCpuBoostClock = 1785000, // Default boost clock .eristaCpuBoostClock = 1785000, // Default boost clock
.marikoCpuBoostClock = 1963000, // Default boost clock .marikoCpuBoostClock = 1963000, // Default boost clock
.eristaGpuUV = 0, .eristaGpuUV = 2,
.eristaGpuVmin = 810, .eristaGpuVmin = 810,
.marikoGpuUV = 0, .marikoGpuUV = 0,

View File

@@ -22,7 +22,7 @@
namespace ams::ldr::hoc::pcv { namespace ams::ldr::hoc::pcv {
Result MemFreqPllmLimit(u32* ptr) { Result MemFreqPllmLimit(u32* ptr) {
clk_pll_param* entry = reinterpret_cast<clk_pll_param *>(ptr); clk_pll_param* entry = reinterpret_cast<clk_pll_param *>(ptr);
R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry()); R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry());
@@ -31,9 +31,9 @@ Result MemFreqPllmLimit(u32* ptr) {
entry->freq = max_clk; entry->freq = max_clk;
entry->vco_max = max_clk; entry->vco_max = max_clk;
R_SUCCEED(); R_SUCCEED();
} }
Result MemVoltHandler(u32* ptr) { Result MemVoltHandler(u32* ptr) {
// ptr value might be default_uv or max_uv // ptr value might be default_uv or max_uv
regulator* entries[2] = { regulator* entries[2] = {
reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.default_uv)), reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.default_uv)),
@@ -54,25 +54,28 @@ Result MemVoltHandler(u32* ptr) {
regulator* entry = nullptr; regulator* entry = nullptr;
for (auto& i : entries) { for (auto& i : entries) {
if (R_SUCCEEDED(validator(i))) if (R_SUCCEEDED(validator(i))) {
entry = i; entry = i;
} }
}
R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry()); R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry());
u32 emc_uv = C.commonEmcMemVolt; u32 emc_uv = C.commonEmcMemVolt;
if (!emc_uv) if (!emc_uv) {
R_SKIP(); R_SKIP();
}
if (emc_uv % uv_step) if (emc_uv % uv_step) {
emc_uv = emc_uv / uv_step * uv_step; // rounding emc_uv = emc_uv / uv_step * uv_step; // rounding
}
PATCH_OFFSET(ptr, emc_uv); PATCH_OFFSET(ptr, emc_uv);
R_SUCCEED(); R_SUCCEED();
} }
void SafetyCheck() { void SafetyCheck() {
// if (C.custRev != CUST_REV) // if (C.custRev != CUST_REV)
// CRASH("Triggered"); // CRASH("Triggered");
@@ -94,6 +97,7 @@ void SafetyCheck() {
R_SUCCEED(); R_SUCCEED();
} }
}; };
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq); u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
u32 marikoCpuDvfsMaxFreq; u32 marikoCpuDvfsMaxFreq;
if (C.marikoCpuUVHigh) { if (C.marikoCpuUVHigh) {
@@ -106,8 +110,7 @@ void SafetyCheck() {
); );
} }
u32 eristaGpuDvfsMaxFreq; u32 eristaGpuDvfsMaxFreq;
switch (C.eristaGpuUV) switch (C.eristaGpuUV) {
{
case 0: case 0:
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq); eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
break; break;
@@ -138,6 +141,21 @@ void SafetyCheck() {
break; break;
} }
using namespace ams::ldr::hoc::pcv;
sValidator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true },
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true },
{ C.commonEmcMemVolt, 912'500, 1350'000 }, // Official burst vmax for the RAMs is 1500mV
{ C.eristaCpuMaxVolt, 1000, 1257 },
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000 },
{ C.marikoCpuMaxVolt, 1000, 1235 },
{ C.marikoEmcMaxClock, 1600'000, 3500'000 },
{ C.marikoEmcVddqVolt, 250'000, 700'000 },
{ eristaCpuDvfsMaxFreq, 1785'000, 2295'000 },
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000 },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000 },
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
};
using namespace ams::ldr::hoc::pcv; using namespace ams::ldr::hoc::pcv;
sValidator validators[] = { sValidator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true }, { C.eristaCpuBoostClock, 1020'000, 2295'000, true },
@@ -155,12 +173,13 @@ void SafetyCheck() {
}; };
for (auto& i : validators) { for (auto& i : validators) {
if (R_FAILED(i.check())) if (R_FAILED(i.check())) {
CRASH("Validation FAIL"); CRASH("Validation FAIL");
} }
} }
}
void Patch(uintptr_t mapped_nso, size_t nso_size) { void Patch(uintptr_t mapped_nso, size_t nso_size) {
#ifdef ATMOSPHERE_IS_STRATOSPHERE #ifdef ATMOSPHERE_IS_STRATOSPHERE
SafetyCheck(); SafetyCheck();
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko); bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
@@ -169,6 +188,6 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) {
else else
erista::Patch(mapped_nso, nso_size); erista::Patch(mapped_nso, nso_size);
#endif #endif
} }
} }

View File

@@ -16,9 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
namespace ams::ldr::hoc::pcv { namespace ams::ldr::hoc::pcv {
typedef struct cvb_coefficients { typedef struct cvb_coefficients {
s32 c0 = 0; s32 c0 = 0;
@@ -135,9 +135,10 @@
auto is_empty = [](NT* entry) { auto is_empty = [](NT* entry) {
uint8_t* m = reinterpret_cast<uint8_t *>(entry); uint8_t* m = reinterpret_cast<uint8_t *>(entry);
for (size_t i = 0; i < sizeof(NT); i++) { for (size_t i = 0; i < sizeof(NT); i++) {
if (*(m + i)) if (*(m + i)) {
return false; return false;
} }
}
return true; return true;
}; };
@@ -165,4 +166,4 @@
return table + index; return table + index;
} }
} }

View File

@@ -80,6 +80,7 @@ namespace ams::ldr::hoc::pcv::erista {
R_SUCCEED(); R_SUCCEED();
} }
/* In theory this should work, but it doesn't, I have no idea why ¯\_(ツ)_/¯ */
Result CpuVoltDfll(u32* ptr) { Result CpuVoltDfll(u32* ptr) {
cvb_cpu_dfll_data *entry = reinterpret_cast<cvb_cpu_dfll_data *>(ptr); cvb_cpu_dfll_data *entry = reinterpret_cast<cvb_cpu_dfll_data *>(ptr);
R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry()); R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry());
@@ -453,12 +454,11 @@ namespace ams::ldr::hoc::pcv::erista {
void Patch(uintptr_t mapped_nso, size_t nso_size) { void Patch(uintptr_t mapped_nso, size_t nso_size) {
PatcherEntry<u32> patches[] = { PatcherEntry<u32> patches[] = {
{"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq)}, {"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq)},
{"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, 825}, {"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, CpuVminOfficial},
{"CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn}, {"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial},
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, 825},
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0xFFEAD0FF}, {"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0xFFEAD0FF},
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, 810}, {"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial},
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, 810}, {"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial},
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq)}, {"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq)},
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn}, {"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn},
{"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax}, {"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax},

View File

@@ -20,16 +20,16 @@
namespace ams::ldr::hoc::ptm { namespace ams::ldr::hoc::ptm {
Result CpuPtmBoost(perf_conf_entry* entry) { Result CpuPtmBoost(perf_conf_entry* entry) {
#ifdef ATMOSPHERE_IS_STRATOSPHERE #ifdef ATMOSPHERE_IS_STRATOSPHERE
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko); bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
#else #else
bool isMariko = true; bool isMariko = true;
#endif #endif
if (!C.eristaCpuBoostClock || !C.marikoCpuBoostClock) if (!C.eristaCpuBoostClock || !C.marikoCpuBoostClock) {
R_SUCCEED(); R_SUCCEED();
}
u32 cpuPtmBoostNew = isMariko ? C.marikoCpuBoostClock * 1000 : C.eristaCpuBoostClock * 1000; u32 cpuPtmBoostNew = isMariko ? C.marikoCpuBoostClock * 1000 : C.eristaCpuBoostClock * 1000;
@@ -37,35 +37,31 @@ Result CpuPtmBoost(perf_conf_entry* entry) {
PATCH_OFFSET(&(entry->cpu_freq_2), cpuPtmBoostNew); PATCH_OFFSET(&(entry->cpu_freq_2), cpuPtmBoostNew);
R_SUCCEED(); R_SUCCEED();
} }
Result MemPtm(perf_conf_entry* entry) { Result MemPtm(perf_conf_entry* entry) {
PATCH_OFFSET(&(entry->emc_freq_1), memPtmLimit); PATCH_OFFSET(&(entry->emc_freq_1), memPtmLimit);
PATCH_OFFSET(&(entry->emc_freq_2), memPtmLimit); PATCH_OFFSET(&(entry->emc_freq_2), memPtmLimit);
R_SUCCEED(); R_SUCCEED();
} }
bool PtmEntryIsValid(perf_conf_entry* entry) { bool PtmEntryIsValid(perf_conf_entry* entry) {
return (entry->cpu_freq_1 == entry->cpu_freq_2 && return (entry->cpu_freq_1 == entry->cpu_freq_2 && entry->gpu_freq_1 == entry->gpu_freq_2 && entry->emc_freq_1 == entry->emc_freq_2);
entry->gpu_freq_1 == entry->gpu_freq_2 && }
entry->emc_freq_1 == entry->emc_freq_2);
}
bool PtmTablePatternFn(u32* ptr) { bool PtmTablePatternFn(u32* ptr) {
perf_conf_entry* entry = reinterpret_cast<perf_conf_entry *>(ptr); perf_conf_entry* entry = reinterpret_cast<perf_conf_entry *>(ptr);
if (!PtmEntryIsValid(entry)) if (!PtmEntryIsValid(entry)) {
return false; return false;
}
return entry->cpu_freq_1 == cpuPtmDefault; return entry->cpu_freq_1 == cpuPtmDefault;
} }
void Patch(uintptr_t mapped_nso, size_t nso_size) { void Patch(uintptr_t mapped_nso, size_t nso_size) {
perf_conf_entry* confTable = nullptr; perf_conf_entry* confTable = nullptr;
for (uintptr_t ptr = mapped_nso; for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(perf_conf_entry) * entryCnt; ptr += sizeof(u32)) {
ptr <= mapped_nso + nso_size - sizeof(perf_conf_entry) * entryCnt;
ptr += sizeof(u32))
{
u32* ptr32 = reinterpret_cast<u32 *>(ptr); u32* ptr32 = reinterpret_cast<u32 *>(ptr);
if (PtmTablePatternFn(ptr32)) { if (PtmTablePatternFn(ptr32)) {
confTable = reinterpret_cast<perf_conf_entry *>(ptr); confTable = reinterpret_cast<perf_conf_entry *>(ptr);
@@ -86,9 +82,8 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) {
bool isMariko = true; bool isMariko = true;
#endif #endif
for (u32 i = 0; i < entryCnt; i++) { for (u32 i = 0; i < entryCnt; i++) {
perf_conf_entry* entry = confTable + i; perf_conf_entry *entry = confTable + i;
if (!PtmEntryIsValid(entry)) { if (!PtmEntryIsValid(entry)) {
LOGGING("@%p", &entry); LOGGING("@%p", &entry);
@@ -130,6 +125,6 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) {
if (R_FAILED(memPtmPatch.CheckResult())) if (R_FAILED(memPtmPatch.CheckResult()))
CRASH(memPtmPatch.description); CRASH(memPtmPatch.description);
} }
} }
} }

View File

@@ -22,7 +22,7 @@
namespace ams::ldr::hoc::ptm { namespace ams::ldr::hoc::ptm {
typedef struct { typedef struct {
u32 conf_id; u32 conf_id;
u32 cpu_freq_1; // min-max pair? u32 cpu_freq_1; // min-max pair?
u32 cpu_freq_2; u32 cpu_freq_2;
@@ -31,17 +31,17 @@ typedef struct {
u32 emc_freq_1; u32 emc_freq_1;
u32 emc_freq_2; u32 emc_freq_2;
u32 padding; u32 padding;
} perf_conf_entry; } perf_conf_entry;
constexpr u32 entryCnt = 16; constexpr u32 entryCnt = 16;
constexpr u32 cpuPtmDefault = 1020'000'000; constexpr u32 cpuPtmDefault = 1020'000'000;
constexpr u32 cpuPtmDevOC = 1224'000'000; constexpr u32 cpuPtmDevOC = 1224'000'000;
constexpr u32 cpuPtmBoost = 1785'000'000; constexpr u32 cpuPtmBoost = 1785'000'000;
constexpr u32 memPtmLimit = 1600'000'000; constexpr u32 memPtmLimit = 1600'000'000;
constexpr u32 memPtmAlt = 1331'200'000; constexpr u32 memPtmAlt = 1331'200'000;
constexpr u32 memPtmClamp = 1065'600'000; constexpr u32 memPtmClamp = 1065'600'000;
void Patch(uintptr_t mapped_nso, size_t nso_size); void Patch(uintptr_t mapped_nso, size_t nso_size);
} }

View File

@@ -1249,6 +1249,7 @@ MarikoCpuUvEntry marikoCpuUvHigh[12] = {
{0x0, 0xdfff, 0, 0x27f07ff}, {0x0, 0xdfff, 0, 0x27f07ff},
}; };
void Board::SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint) { void Board::SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint) {
return;
u32* tune0_ptr = (u32*)(cldvfs + CL_DVFS_TUNE0_0); u32* tune0_ptr = (u32*)(cldvfs + CL_DVFS_TUNE0_0);
u32* tune1_ptr = (u32*)(cldvfs + CL_DVFS_TUNE1_0); u32* tune1_ptr = (u32*)(cldvfs + CL_DVFS_TUNE1_0);