From 54e8465e4758730a1c0c4f8ab3a88d329f9c6903 Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:22:47 +0100 Subject: [PATCH] Fix erista mrf --- .../stratosphere/loader/source/oc/pcv/pcv.hpp | 4 +- .../loader/source/oc/pcv/pcv_erista.cpp | 68 +++++++++---------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index c68e00fb..772e1a7a 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -209,7 +209,8 @@ namespace ams::ldr::hoc::pcv { constexpr u32 CpuVoltL4T = 1257'000; static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 }; - static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "invalid cpuVoltDvfsPattern size"); + static const u32 cpuVoltDvfsOffsets[] = { 5, 6, 7, 8, 9 }; + static_assert(sizeof(cpuVoltDvfsPattern) == sizeof(cpuVoltDvfsOffsets), "Invalid cpuVoltDvfsPattern"); static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 }; static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "invalid cpuVoltageThermalPattern size"); @@ -284,6 +285,7 @@ namespace ams::ldr::hoc::pcv { }; constexpr u32 MemVoltHOS = 1125'000; + constexpr u32 EmcClkMinFreq = 40800; /* 40.8 MHz table only exists on erista. */ constexpr u32 EmcClkPllmLimit = 1866'000'000; constexpr u32 MTC_TABLE_REV = 7; 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 be9f8c93..0e4f6457 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -24,21 +24,6 @@ namespace ams::ldr::hoc::pcv::erista { - Result CpuVoltRange(u32* ptr) { - u32 min_volt_got = *(ptr - 1); - for (const auto& mv : CpuMinVolts) { - if (min_volt_got != mv) - continue; - - if (!C.eristaCpuMaxVolt) - R_SKIP(); - - PATCH_OFFSET(ptr, C.eristaCpuMaxVolt); - R_SUCCEED(); - } - R_THROW(ldr::ResultInvalidCpuMinVolt()); - } - Result CpuVoltDvfs(u32 *ptr) { if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) { R_THROW(ldr::ResultInvalidCpuMinVolt()); @@ -77,7 +62,6 @@ namespace ams::ldr::hoc::pcv::erista { R_SUCCEED(); } - /* In theory this should work, but it doesn't, I have no idea why ¯\_(ツ)_/¯ */ Result CpuVoltDfll(u32* ptr) { cvb_cpu_dfll_data *entry = reinterpret_cast(ptr); R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry()); @@ -85,7 +69,7 @@ namespace ams::ldr::hoc::pcv::erista { R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); - if( !C.eristaCpuUV) { + if (!C.eristaCpuUV) { R_SKIP(); } @@ -367,44 +351,54 @@ namespace ams::ldr::hoc::pcv::erista { table->min_volt = std::min(static_cast(1050), 900 + C.emcDvbShift * 25); } + /* Probably more intuitive to point to 40800 rather than 1600000, but oh well. */ Result MemFreqMtcTable(u32 *ptr) { if (GET_MAX_OF_ARR(maxEmcClocks) <= EmcClkOSLimit) { R_SKIP(); } - u32 khz_list[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800}; - std::sort(maxEmcClocks, maxEmcClocks + std::size(maxEmcClocks), std::greater<>()); - u32 khz_list_size = sizeof(khz_list) / sizeof(u32); + u32 khz_list[] = { 40800, 68000, 102000, 204000, 408000, 665600, 800000, 1065600, 1331200, 1600000 }; + std::sort(maxEmcClocks, maxEmcClocks + std::size(maxEmcClocks)); + u32 khz_list_size = std::size(khz_list); // Generate list for mtc table pointers EristaMtcTable *table_list[khz_list_size]; for (u32 i = 0; i < khz_list_size; i++) { + u32 mtcIndex = khz_list_size - 1 - i; u8 *table = reinterpret_cast(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable); - table_list[i] = reinterpret_cast(table); - R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable()); - R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); + table_list[mtcIndex] = reinterpret_cast(table); + R_UNLESS(table_list[mtcIndex]->rate_khz == khz_list[mtcIndex], ldr::ResultInvalidMtcTable()); + R_UNLESS(table_list[mtcIndex]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); } - u32 additionalFreqs = 0; - for (u32 i = 0; i < std::size(maxEmcClocks); ++i) { - if (maxEmcClocks[i] > EmcClkOSLimit) { - ++additionalFreqs; + /* If we oc ram at all, tables are always shifted by at least 1. */ + u32 tableShifts = 1; + for (u32 i = 0; i < std::size(maxEmcClocks) - 1; ++i) { + /* Duplicated mtc tables may cause pcv to not select frequencies properly, causing issues. */ + if (maxEmcClocks[i] != maxEmcClocks[i + 1] && maxEmcClocks[i] > EmcClkOSLimit) { + ++tableShifts; } else { - break; + maxEmcClocks[i] = 0; } } - // Make room for new mtc table, discarding useless 40.8, 68000 and 102000 MHz table - // 40800 overwritten by 204000, ..., 1331200 overwritten by 1600000, leaving table_list[0], table_list[1] and table_list[2] not overwritten - for (u32 i = khz_list_size - 1; i > additionalFreqs - 1; --i) { - std::memcpy(static_cast(table_list[i]), static_cast(table_list[i - additionalFreqs]), sizeof(EristaMtcTable)); + /* Erista has extra, useless mtc tables, such as 40.8 Mhz, overwrite them to make room for oc freqs. */ + /* More than 3 tables can be overwritten, but 3 is plenty. */ + std::memmove(table_list[0], table_list[tableShifts], sizeof(EristaMtcTable) * (khz_list_size - tableShifts)); + + /* Since we're not scaling r/w latency properly on Erista, we first overwrite the tables with the 1600 MHz table before scaling it. */ + for (u32 i = 0; i < tableShifts; ++i) { + std::memcpy(table_list[khz_list_size - i - 1], table_list[khz_list_size - tableShifts - 1], sizeof(EristaMtcTable)); } - for (u32 i = 0; i < additionalFreqs; ++i) { - /* Since we're not scaling latency timings properly, copy over the 1600Mhz table to get the closest timings. */ - std::memcpy(table_list[i], table_list[additionalFreqs], sizeof(EristaMtcTable)); - table_list[i]->rate_khz = maxEmcClocks[i]; - MemMtcTableAutoAdjust(table_list[i]); + for (u32 i = tableShifts, j = 0; i > 0 && j < std::size(maxEmcClocks); ++j) { + if (!maxEmcClocks[j]) { + continue; + } + + table_list[khz_list_size - i]->rate_khz = maxEmcClocks[j]; + MemMtcTableAutoAdjust(table_list[khz_list_size - i]); + --i; } R_SUCCEED();